mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 21:38:05 -05:00
Compare commits
294 Commits
deneb-inte
...
interopFix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
765122dc05 | ||
|
|
a283fa58fb | ||
|
|
12fe2d91ed | ||
|
|
d44905329c | ||
|
|
e2238bd6d1 | ||
|
|
c0310ad534 | ||
|
|
e10ac0af02 | ||
|
|
6e358da5ed | ||
|
|
df291e2ffb | ||
|
|
5ba5b303d3 | ||
|
|
f2ce4dcab3 | ||
|
|
8765c3ac42 | ||
|
|
57fff2d88e | ||
|
|
c010a972e7 | ||
|
|
c02ed805b0 | ||
|
|
93adf4980a | ||
|
|
3fe969992a | ||
|
|
2135108830 | ||
|
|
4c146dc896 | ||
|
|
042a3cda02 | ||
|
|
b8676480f0 | ||
|
|
711022d34e | ||
|
|
eec93be4ed | ||
|
|
21d096622f | ||
|
|
62846d61b8 | ||
|
|
a228a407be | ||
|
|
f527b676da | ||
|
|
5bd4e10dd6 | ||
|
|
d19e13352b | ||
|
|
6bda9a0bf2 | ||
|
|
2da6b7bb97 | ||
|
|
7faed861c4 | ||
|
|
8b9129d84e | ||
|
|
8b219b14da | ||
|
|
5bf9bd3d73 | ||
|
|
59f12c8ac1 | ||
|
|
1094ca0838 | ||
|
|
ebe4b309c0 | ||
|
|
ea94f0e70d | ||
|
|
47443e130d | ||
|
|
4dfa5c2757 | ||
|
|
1851d40f74 | ||
|
|
eee1d47655 | ||
|
|
7ce76652fb | ||
|
|
19e6f0c19a | ||
|
|
6470e2718a | ||
|
|
30cd5c076e | ||
|
|
03d8af5cda | ||
|
|
194f0cb76d | ||
|
|
2a0e8510d4 | ||
|
|
5e35f778b9 | ||
|
|
972ae7f169 | ||
|
|
80fafaddff | ||
|
|
e6ecdfde0d | ||
|
|
1daf51788d | ||
|
|
35055539a7 | ||
|
|
81ab3ca46c | ||
|
|
5895b10678 | ||
|
|
7ea645ed37 | ||
|
|
a900792160 | ||
|
|
cd87bfd8ab | ||
|
|
98477a0286 | ||
|
|
2d1a63d9f4 | ||
|
|
52be270f0a | ||
|
|
895a86fd53 | ||
|
|
af6246a5f9 | ||
|
|
5e80ceeff9 | ||
|
|
ee661971f0 | ||
|
|
cc7e36776d | ||
|
|
14a9d9a1ad | ||
|
|
2b9fb29ed2 | ||
|
|
9300d1026f | ||
|
|
48345eb68e | ||
|
|
60d14f1806 | ||
|
|
d80d4d01a6 | ||
|
|
275192680f | ||
|
|
9ca958064e | ||
|
|
604958da6c | ||
|
|
ade94444f2 | ||
|
|
4df2f4c790 | ||
|
|
3f8f5edb3f | ||
|
|
0ad4e433a5 | ||
|
|
1be2503e82 | ||
|
|
0dd228bb94 | ||
|
|
78450ea557 | ||
|
|
f0e6d4a0bd | ||
|
|
97901c90a5 | ||
|
|
1379dbfc23 | ||
|
|
19dbc7e249 | ||
|
|
76a70065f2 | ||
|
|
51f513b246 | ||
|
|
2b349a1b06 | ||
|
|
a819caca16 | ||
|
|
e7116d4ea8 | ||
|
|
f8cd989161 | ||
|
|
4c19265ac5 | ||
|
|
f361bf781f | ||
|
|
a458e556e0 | ||
|
|
773b259cd5 | ||
|
|
2bb3da1ba3 | ||
|
|
47367d98b4 | ||
|
|
1ff18c07a4 | ||
|
|
279a95deba | ||
|
|
c0bfa6ef79 | ||
|
|
7e961c2be9 | ||
|
|
c7c7f9bf1b | ||
|
|
7ce85cac31 | ||
|
|
2d836f485d | ||
|
|
780253b786 | ||
|
|
710bb98575 | ||
|
|
d5387851d0 | ||
|
|
ab8dd3788f | ||
|
|
bf1b550b7d | ||
|
|
705564108c | ||
|
|
3df82e7540 | ||
|
|
d0a749ce4b | ||
|
|
081c80998c | ||
|
|
8c62f10b74 | ||
|
|
e232b3ce30 | ||
|
|
17153bb4e9 | ||
|
|
329a45c06a | ||
|
|
1c82394a69 | ||
|
|
856081c80c | ||
|
|
6fff327864 | ||
|
|
e2879f8352 | ||
|
|
523fe58f61 | ||
|
|
04a303c8d2 | ||
|
|
0844bd62ea | ||
|
|
816dc47b17 | ||
|
|
caeec851d4 | ||
|
|
062933af35 | ||
|
|
169573c32e | ||
|
|
e7a7b2861e | ||
|
|
fe6c80fe95 | ||
|
|
2f52dfe96e | ||
|
|
93a7b96f16 | ||
|
|
f078b62c3e | ||
|
|
f476c39708 | ||
|
|
c02e507422 | ||
|
|
ece07e5fbb | ||
|
|
6bbe3dbd10 | ||
|
|
18bfc2a34e | ||
|
|
b774af9535 | ||
|
|
719a5fca02 | ||
|
|
b4a0e4375a | ||
|
|
4d276d2fdf | ||
|
|
8797179cfb | ||
|
|
7cc38108aa | ||
|
|
365ced285e | ||
|
|
97e5730fd9 | ||
|
|
3919b49000 | ||
|
|
7f13396e44 | ||
|
|
ae3e5718e6 | ||
|
|
97a49240ba | ||
|
|
232d519445 | ||
|
|
afd815bb5d | ||
|
|
56d383a354 | ||
|
|
733023df03 | ||
|
|
9d9ce13753 | ||
|
|
975e7a76bf | ||
|
|
20ae23bd42 | ||
|
|
63cf429fa0 | ||
|
|
2aab4e2efe | ||
|
|
e2156f25e0 | ||
|
|
a537833f75 | ||
|
|
61bf95e4e2 | ||
|
|
357d3f3b6a | ||
|
|
b0bbfcab7f | ||
|
|
06801a5230 | ||
|
|
278857d576 | ||
|
|
9c5c70fb32 | ||
|
|
54326af141 | ||
|
|
6020682ad1 | ||
|
|
3f45d54986 | ||
|
|
ecb51dc55d | ||
|
|
cbf4aeb859 | ||
|
|
b107bd2a5a | ||
|
|
a5e2c3f551 | ||
|
|
efffaeb359 | ||
|
|
144576cf36 | ||
|
|
fcf2be08d8 | ||
|
|
2b5cd139f0 | ||
|
|
77a4fdb509 | ||
|
|
a14d37b0ad | ||
|
|
38e28af51e | ||
|
|
6dbe6cfd8c | ||
|
|
c156c1fb91 | ||
|
|
393a744091 | ||
|
|
f83993b211 | ||
|
|
41433f8b2e | ||
|
|
95f62de465 | ||
|
|
fbb140eff7 | ||
|
|
22483a285a | ||
|
|
1d835d9859 | ||
|
|
98f8ab331a | ||
|
|
0e88418b12 | ||
|
|
ef3ff6f1d5 | ||
|
|
7c22496c65 | ||
|
|
c5256d09e0 | ||
|
|
d3d1eb833e | ||
|
|
2d9fd4ea29 | ||
|
|
be168e4034 | ||
|
|
f7c2b9c197 | ||
|
|
89b7cf9be3 | ||
|
|
3591f85a66 | ||
|
|
6ba5ad0325 | ||
|
|
76b16a8989 | ||
|
|
74a19741b4 | ||
|
|
fdb68c482e | ||
|
|
b51729bd2f | ||
|
|
aef1269223 | ||
|
|
9fc1683ec7 | ||
|
|
3790c5edb2 | ||
|
|
c6c7f8234d | ||
|
|
c66ea88da8 | ||
|
|
9a8facd76b | ||
|
|
ca7e0e4807 | ||
|
|
6acedb7dfd | ||
|
|
a3183bc33e | ||
|
|
f6caf627e1 | ||
|
|
fa696a883d | ||
|
|
cbbf188637 | ||
|
|
4d3e65bdcd | ||
|
|
0be2bde4cc | ||
|
|
d5662556bc | ||
|
|
26a10ca56e | ||
|
|
bda70352ca | ||
|
|
fbd45dbf50 | ||
|
|
119ef0f8fa | ||
|
|
04f38324ba | ||
|
|
fa27b6e24c | ||
|
|
fe647e99fc | ||
|
|
bbcaa7eaf2 | ||
|
|
1c1b2eb811 | ||
|
|
427e792073 | ||
|
|
463481febe | ||
|
|
6e41923388 | ||
|
|
17798f878a | ||
|
|
d502f0825a | ||
|
|
96fe2b76bf | ||
|
|
a51a4ca9eb | ||
|
|
9dd8a1737c | ||
|
|
c97f74ccef | ||
|
|
806a923974 | ||
|
|
4b4c2b97b7 | ||
|
|
9d22ea840e | ||
|
|
8a507d749a | ||
|
|
2850581611 | ||
|
|
59bc0c679c | ||
|
|
969dec8ad2 | ||
|
|
91fb8eea8c | ||
|
|
e7ebdb11be | ||
|
|
ff3bb0aa8a | ||
|
|
5945849cb4 | ||
|
|
3435a61413 | ||
|
|
a3b69600ef | ||
|
|
01841434ec | ||
|
|
f60edb055c | ||
|
|
ee3d106a36 | ||
|
|
9b41a069eb | ||
|
|
dc1d5b778b | ||
|
|
224b92781f | ||
|
|
6f54a9d057 | ||
|
|
7906e571a8 | ||
|
|
458817d5ad | ||
|
|
06290c6805 | ||
|
|
1adf1f1bef | ||
|
|
af57cf5e96 | ||
|
|
d59ba818f0 | ||
|
|
9aa2dd1ae6 | ||
|
|
f3abe70838 | ||
|
|
fe4a852e78 | ||
|
|
6af0f619c9 | ||
|
|
3d405910e7 | ||
|
|
2779daee32 | ||
|
|
a0ba4a8563 | ||
|
|
926b3725a1 | ||
|
|
5cc9f4df0b | ||
|
|
fd297999b8 | ||
|
|
0d45eeac56 | ||
|
|
e2fcd25039 | ||
|
|
2436d84370 | ||
|
|
5418d8c367 | ||
|
|
55e5dee7ab | ||
|
|
6a06a4bf98 | ||
|
|
a9d981dce1 | ||
|
|
a69947ba51 | ||
|
|
6a32b18ca9 | ||
|
|
9ebf8651b4 | ||
|
|
8467485aec | ||
|
|
fdb6cf9b57 | ||
|
|
3da55ad7a4 | ||
|
|
773d561361 | ||
|
|
7f6d3ccb36 |
0
.bazelignore
Normal file
0
.bazelignore
Normal file
30
WORKSPACE
30
WORKSPACE
@@ -197,6 +197,8 @@ filegroup(
|
||||
url = "https://github.com/eth2-clients/slashing-protection-interchange-tests/archive/b8413ca42dc92308019d0d4db52c87e9e125c4e9.tar.gz",
|
||||
)
|
||||
|
||||
eth2_spec_version = "v1.1.0-beta.1"
|
||||
|
||||
http_archive(
|
||||
name = "eth2_spec_tests_general",
|
||||
build_file_content = """
|
||||
@@ -209,8 +211,8 @@ filegroup(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
sha256 = "deacc076365c727d653ac064894ecf0d1b0a675d86704dc8de271259f6a7314b",
|
||||
url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v1.1.0-alpha.3/general.tar.gz",
|
||||
sha256 = "e9b4cc60a3e676c6b4a9348424e44cff1ebada603ffb31b0df600dbd70e7fbf6",
|
||||
url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/%s/general.tar.gz" % eth2_spec_version,
|
||||
)
|
||||
|
||||
http_archive(
|
||||
@@ -225,8 +227,8 @@ filegroup(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
sha256 = "6e9886af3d2f024e563249d70388129e28e3e92f742f289238ed9b7ec7a7f930",
|
||||
url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v1.1.0-alpha.3/minimal.tar.gz",
|
||||
sha256 = "cf82dc729ffe7b924f852e57d1973e1a6377c5b52acc903c953277fa9b4e6de8",
|
||||
url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/%s/minimal.tar.gz" % eth2_spec_version,
|
||||
)
|
||||
|
||||
http_archive(
|
||||
@@ -241,8 +243,24 @@ filegroup(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
sha256 = "a7b3d0ffc02a567250f424d69b2474fdc9477cd56eada60af7474560b46a8527",
|
||||
url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v1.1.0-alpha.3/mainnet.tar.gz",
|
||||
sha256 = "6c6792375b81858037014e282d28a64b0cf12e12daf16054265c85403b8b329f",
|
||||
url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/%s/mainnet.tar.gz" % eth2_spec_version,
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "eth2_spec",
|
||||
build_file_content = """
|
||||
filegroup(
|
||||
name = "spec_data",
|
||||
srcs = glob([
|
||||
"**/*.yaml",
|
||||
]),
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
sha256 = "16094dad1bab4e8ab3adb60c10e311cd1e294cd7bbf5a89505f24bebd3d0e513",
|
||||
strip_prefix = "eth2.0-specs-" + eth2_spec_version[1:],
|
||||
url = "https://github.com/ethereum/eth2.0-specs/archive/refs/tags/%s.tar.gz" % eth2_spec_version,
|
||||
)
|
||||
|
||||
http_archive(
|
||||
|
||||
@@ -5,6 +5,7 @@ go_library(
|
||||
srcs = [
|
||||
"chain_info.go",
|
||||
"head.go",
|
||||
"head_sync_committee_info.go",
|
||||
"info.go",
|
||||
"init_sync_process_block.go",
|
||||
"log.go",
|
||||
@@ -26,6 +27,7 @@ go_library(
|
||||
deps = [
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/cache/depositcache:go_default_library",
|
||||
"//beacon-chain/core/altair:go_default_library",
|
||||
"//beacon-chain/core/epoch/precompute:go_default_library",
|
||||
"//beacon-chain/core/feed:go_default_library",
|
||||
"//beacon-chain/core/feed/state:go_default_library",
|
||||
@@ -42,6 +44,7 @@ go_library(
|
||||
"//beacon-chain/powchain:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/stategen:go_default_library",
|
||||
"//beacon-chain/state/v2:go_default_library",
|
||||
"//cmd/beacon-chain/flags:go_default_library",
|
||||
"//proto/eth/v1:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
@@ -56,7 +59,9 @@ go_library(
|
||||
"//shared/slotutil:go_default_library",
|
||||
"//shared/timeutils:go_default_library",
|
||||
"//shared/traceutil:go_default_library",
|
||||
"//shared/version:go_default_library",
|
||||
"@com_github_emicklei_dot//:go_default_library",
|
||||
"@com_github_hashicorp_golang_lru//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
|
||||
|
||||
@@ -24,6 +24,8 @@ type ChainInfoFetcher interface {
|
||||
GenesisFetcher
|
||||
CanonicalFetcher
|
||||
ForkFetcher
|
||||
TimeFetcher
|
||||
HeadDomainFetcher
|
||||
}
|
||||
|
||||
// TimeFetcher retrieves the Ethereum consensus data that's related to time.
|
||||
@@ -48,8 +50,12 @@ type HeadFetcher interface {
|
||||
HeadSeed(ctx context.Context, epoch types.Epoch) ([32]byte, error)
|
||||
HeadGenesisValidatorRoot() [32]byte
|
||||
HeadETH1Data() *ethpb.Eth1Data
|
||||
HeadPublicKeyToValidatorIndex(ctx context.Context, pubKey [48]byte) (types.ValidatorIndex, bool)
|
||||
HeadValidatorIndexToPublicKey(ctx context.Context, index types.ValidatorIndex) ([48]byte, error)
|
||||
ProtoArrayStore() *protoarray.Store
|
||||
ChainHeads() ([][32]byte, []types.Slot)
|
||||
HeadSyncCommitteeFetcher
|
||||
HeadDomainFetcher
|
||||
}
|
||||
|
||||
// ForkFetcher retrieves the current fork information of the Ethereum beacon chain.
|
||||
|
||||
188
beacon-chain/blockchain/head_sync_committee_info.go
Normal file
188
beacon-chain/blockchain/head_sync_committee_info.go
Normal file
@@ -0,0 +1,188 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/altair"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
core "github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
stateAltair "github.com/prysmaticlabs/prysm/beacon-chain/state/v2"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
// HeadSyncCommitteeFetcher is the interface that wraps the head sync committee related functions.
|
||||
// The head sync committee functions return callers sync committee indices and public keys with respect to current head state.
|
||||
type HeadSyncCommitteeFetcher interface {
|
||||
HeadCurrentSyncCommitteeIndices(ctx context.Context, index types.ValidatorIndex, slot types.Slot) ([]types.CommitteeIndex, error)
|
||||
HeadNextSyncCommitteeIndices(ctx context.Context, index types.ValidatorIndex, slot types.Slot) ([]types.CommitteeIndex, error)
|
||||
HeadSyncCommitteePubKeys(ctx context.Context, slot types.Slot, committeeIndex types.CommitteeIndex) ([][]byte, error)
|
||||
}
|
||||
|
||||
// HeadDomainFetcher is the interface that wraps the head sync domain related functions.
|
||||
// The head sync committee domain functions return callers domain data with respect to slot and head state.
|
||||
type HeadDomainFetcher interface {
|
||||
HeadSyncCommitteeDomain(ctx context.Context, slot types.Slot) ([]byte, error)
|
||||
HeadSyncSelectionProofDomain(ctx context.Context, slot types.Slot) ([]byte, error)
|
||||
HeadSyncContributionProofDomain(ctx context.Context, slot types.Slot) ([]byte, error)
|
||||
}
|
||||
|
||||
// HeadSyncCommitteeDomain returns the head sync committee domain using current head state advanced up to `slot`.
|
||||
func (s *Service) HeadSyncCommitteeDomain(ctx context.Context, slot types.Slot) ([]byte, error) {
|
||||
s.headLock.RLock()
|
||||
defer s.headLock.RUnlock()
|
||||
|
||||
return s.domainWithHeadState(ctx, slot, params.BeaconConfig().DomainSyncCommittee)
|
||||
}
|
||||
|
||||
// HeadSyncSelectionProofDomain returns the head sync committee domain using current head state advanced up to `slot`.
|
||||
func (s *Service) HeadSyncSelectionProofDomain(ctx context.Context, slot types.Slot) ([]byte, error) {
|
||||
s.headLock.RLock()
|
||||
defer s.headLock.RUnlock()
|
||||
|
||||
return s.domainWithHeadState(ctx, slot, params.BeaconConfig().DomainSyncCommitteeSelectionProof)
|
||||
}
|
||||
|
||||
// HeadSyncContributionProofDomain returns the head sync committee domain using current head state advanced up to `slot`.
|
||||
func (s *Service) HeadSyncContributionProofDomain(ctx context.Context, slot types.Slot) ([]byte, error) {
|
||||
s.headLock.RLock()
|
||||
defer s.headLock.RUnlock()
|
||||
|
||||
return s.domainWithHeadState(ctx, slot, params.BeaconConfig().DomainContributionAndProof)
|
||||
}
|
||||
|
||||
// HeadCurrentSyncCommitteeIndices returns the input validator `index`'s position indices in the current sync committee with respect to `slot`.
|
||||
// Head state advanced up to `slot` is used for calculation.
|
||||
func (s *Service) HeadCurrentSyncCommitteeIndices(ctx context.Context, index types.ValidatorIndex, slot types.Slot) ([]types.CommitteeIndex, error) {
|
||||
s.headLock.RLock()
|
||||
defer s.headLock.RUnlock()
|
||||
|
||||
headState, err := s.getSyncCommitteeHeadState(ctx, slot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return helpers.CurrentPeriodSyncSubcommitteeIndices(headState, index)
|
||||
}
|
||||
|
||||
// HeadNextSyncCommitteeIndices returns the input validator `index`'s position indices in the next sync committee with respect to `slot`.
|
||||
// Head state advanced up to `slot` is used for calculation.
|
||||
func (s *Service) HeadNextSyncCommitteeIndices(ctx context.Context, index types.ValidatorIndex, slot types.Slot) ([]types.CommitteeIndex, error) {
|
||||
s.headLock.RLock()
|
||||
defer s.headLock.RUnlock()
|
||||
|
||||
headState, err := s.getSyncCommitteeHeadState(ctx, slot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return helpers.NextPeriodSyncSubcommitteeIndices(headState, index)
|
||||
}
|
||||
|
||||
// HeadSyncCommitteePubKeys returns the head sync committee public keys with respect to `slot` and subcommittee index `committeeIndex`.
|
||||
// Head state advanced up to `slot` is used for calculation.
|
||||
func (s *Service) HeadSyncCommitteePubKeys(ctx context.Context, slot types.Slot, committeeIndex types.CommitteeIndex) ([][]byte, error) {
|
||||
s.headLock.RLock()
|
||||
defer s.headLock.RUnlock()
|
||||
|
||||
headState, err := s.getSyncCommitteeHeadState(ctx, slot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nextSlotEpoch := helpers.SlotToEpoch(headState.Slot() + 1)
|
||||
currEpoch := helpers.SlotToEpoch(headState.Slot())
|
||||
|
||||
var syncCommittee *ethpb.SyncCommittee
|
||||
if helpers.SyncCommitteePeriod(currEpoch) == helpers.SyncCommitteePeriod(nextSlotEpoch) {
|
||||
syncCommittee, err = headState.CurrentSyncCommittee()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
syncCommittee, err = headState.NextSyncCommittee()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return altair.SyncSubCommitteePubkeys(syncCommittee, committeeIndex)
|
||||
}
|
||||
|
||||
// returns calculated domain using input `domain` and `slot`.
|
||||
func (s *Service) domainWithHeadState(ctx context.Context, slot types.Slot, domain [4]byte) ([]byte, error) {
|
||||
headState, err := s.getSyncCommitteeHeadState(ctx, slot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return helpers.Domain(headState.Fork(), helpers.SlotToEpoch(headState.Slot()), domain, headState.GenesisValidatorRoot())
|
||||
}
|
||||
|
||||
// returns the head state that is advanced up to `slot`. It utilizes the cache `syncCommitteeHeadState` by retrieving using `slot` as key.
|
||||
// For the cache miss, it processes head state up to slot and fill the cache with `slot` as key.
|
||||
func (s *Service) getSyncCommitteeHeadState(ctx context.Context, slot types.Slot) (state.BeaconState, error) {
|
||||
var headState state.BeaconState
|
||||
var err error
|
||||
|
||||
// If there's already a head state exists with the request slot, we don't need to process slots.
|
||||
cachedState := syncCommitteeHeadStateCache.get(slot)
|
||||
if cachedState != nil && !cachedState.IsNil() {
|
||||
syncHeadStateHit.Inc()
|
||||
headState = cachedState
|
||||
} else {
|
||||
headState, err = s.HeadState(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if slot > headState.Slot() {
|
||||
headState, err = core.ProcessSlots(ctx, headState, slot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
syncHeadStateMiss.Inc()
|
||||
syncCommitteeHeadStateCache.add(slot, headState)
|
||||
}
|
||||
|
||||
return headState, nil
|
||||
}
|
||||
|
||||
var syncCommitteeHeadStateCache = newSyncCommitteeHeadState()
|
||||
|
||||
// syncCommitteeHeadState to caches latest head state requested by the sync committee participant.
|
||||
type syncCommitteeHeadState struct {
|
||||
cache *lru.Cache
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// newSyncCommitteeHeadState initializes the lru cache for `syncCommitteeHeadState` with size of 1.
|
||||
func newSyncCommitteeHeadState() *syncCommitteeHeadState {
|
||||
c, err := lru.New(1) // only need size of 1 to avoid redundant state copy, HTR, and process slots.
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &syncCommitteeHeadState{cache: c}
|
||||
}
|
||||
|
||||
// add `slot` as key and `state` as value onto the lru cache.
|
||||
func (c *syncCommitteeHeadState) add(slot types.Slot, state state.BeaconState) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
c.cache.Add(slot, state)
|
||||
}
|
||||
|
||||
// get `state` using `slot` as key. Return nil if nothing is found.
|
||||
func (c *syncCommitteeHeadState) get(slot types.Slot) state.BeaconState {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
val, exists := c.cache.Get(slot)
|
||||
if !exists {
|
||||
return nil
|
||||
}
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
return val.(*stateAltair.BeaconState)
|
||||
}
|
||||
108
beacon-chain/blockchain/head_sync_committee_info_test.go
Normal file
108
beacon-chain/blockchain/head_sync_committee_info_test.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
|
||||
func TestService_HeadCurrentSyncCommitteeIndices(t *testing.T) {
|
||||
s, _ := testutil.DeterministicGenesisStateAltair(t, params.BeaconConfig().TargetCommitteeSize)
|
||||
c := &Service{}
|
||||
c.head = &head{state: s}
|
||||
|
||||
// Process slot up to `EpochsPerSyncCommitteePeriod` so it can `ProcessSyncCommitteeUpdates`.
|
||||
slot := uint64(params.BeaconConfig().EpochsPerSyncCommitteePeriod)*uint64(params.BeaconConfig().SlotsPerEpoch) + 1
|
||||
indices, err := c.HeadCurrentSyncCommitteeIndices(context.Background(), 0, types.Slot(slot))
|
||||
require.NoError(t, err)
|
||||
|
||||
// NextSyncCommittee becomes CurrentSyncCommittee so it should be empty by default.
|
||||
require.Equal(t, 0, len(indices))
|
||||
}
|
||||
|
||||
func TestService_HeadNextSyncCommitteeIndices(t *testing.T) {
|
||||
s, _ := testutil.DeterministicGenesisStateAltair(t, params.BeaconConfig().TargetCommitteeSize)
|
||||
c := &Service{}
|
||||
c.head = &head{state: s}
|
||||
|
||||
// Process slot up to `EpochsPerSyncCommitteePeriod` so it can `ProcessSyncCommitteeUpdates`.
|
||||
slot := uint64(params.BeaconConfig().EpochsPerSyncCommitteePeriod)*uint64(params.BeaconConfig().SlotsPerEpoch) + 1
|
||||
indices, err := c.HeadNextSyncCommitteeIndices(context.Background(), 0, types.Slot(slot))
|
||||
require.NoError(t, err)
|
||||
|
||||
// NextSyncCommittee should be be empty after `ProcessSyncCommitteeUpdates`. Validator should get indices.
|
||||
require.NotEqual(t, 0, len(indices))
|
||||
}
|
||||
|
||||
func TestService_HeadSyncCommitteePubKeys(t *testing.T) {
|
||||
s, _ := testutil.DeterministicGenesisStateAltair(t, params.BeaconConfig().TargetCommitteeSize)
|
||||
c := &Service{}
|
||||
c.head = &head{state: s}
|
||||
|
||||
// Process slot up to 2 * `EpochsPerSyncCommitteePeriod` so it can run `ProcessSyncCommitteeUpdates` twice.
|
||||
slot := uint64(2*params.BeaconConfig().EpochsPerSyncCommitteePeriod)*uint64(params.BeaconConfig().SlotsPerEpoch) + 1
|
||||
pubkeys, err := c.HeadSyncCommitteePubKeys(context.Background(), types.Slot(slot), 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Any subcommittee should match the subcommittee size.
|
||||
subCommitteeSize := params.BeaconConfig().SyncCommitteeSize / params.BeaconConfig().SyncCommitteeSubnetCount
|
||||
require.Equal(t, int(subCommitteeSize), len(pubkeys))
|
||||
}
|
||||
|
||||
func TestService_HeadSyncCommitteeDomain(t *testing.T) {
|
||||
s, _ := testutil.DeterministicGenesisStateAltair(t, params.BeaconConfig().TargetCommitteeSize)
|
||||
c := &Service{}
|
||||
c.head = &head{state: s}
|
||||
|
||||
wanted, err := helpers.Domain(s.Fork(), helpers.SlotToEpoch(s.Slot()), params.BeaconConfig().DomainSyncCommittee, s.GenesisValidatorRoot())
|
||||
require.NoError(t, err)
|
||||
|
||||
d, err := c.HeadSyncCommitteeDomain(context.Background(), 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.DeepEqual(t, wanted, d)
|
||||
}
|
||||
|
||||
func TestService_HeadSyncContributionProofDomain(t *testing.T) {
|
||||
s, _ := testutil.DeterministicGenesisStateAltair(t, params.BeaconConfig().TargetCommitteeSize)
|
||||
c := &Service{}
|
||||
c.head = &head{state: s}
|
||||
|
||||
wanted, err := helpers.Domain(s.Fork(), helpers.SlotToEpoch(s.Slot()), params.BeaconConfig().DomainContributionAndProof, s.GenesisValidatorRoot())
|
||||
require.NoError(t, err)
|
||||
|
||||
d, err := c.HeadSyncContributionProofDomain(context.Background(), 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.DeepEqual(t, wanted, d)
|
||||
}
|
||||
|
||||
func TestService_HeadSyncSelectionProofDomain(t *testing.T) {
|
||||
s, _ := testutil.DeterministicGenesisStateAltair(t, params.BeaconConfig().TargetCommitteeSize)
|
||||
c := &Service{}
|
||||
c.head = &head{state: s}
|
||||
|
||||
wanted, err := helpers.Domain(s.Fork(), helpers.SlotToEpoch(s.Slot()), params.BeaconConfig().DomainSyncCommitteeSelectionProof, s.GenesisValidatorRoot())
|
||||
require.NoError(t, err)
|
||||
|
||||
d, err := c.HeadSyncSelectionProofDomain(context.Background(), 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.DeepEqual(t, wanted, d)
|
||||
}
|
||||
|
||||
func TestSyncCommitteeHeadStateCache_RoundTrip(t *testing.T) {
|
||||
c := newSyncCommitteeHeadState()
|
||||
beaconState, _ := testutil.DeterministicGenesisStateAltair(t, 100)
|
||||
require.NoError(t, beaconState.SetSlot(100))
|
||||
cachedState := c.get(101)
|
||||
require.Equal(t, nil, cachedState)
|
||||
c.add(101, beaconState)
|
||||
cachedState = c.get(101)
|
||||
require.DeepEqual(t, beaconState, cachedState)
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/timeutils"
|
||||
"github.com/prysmaticlabs/prysm/shared/version"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -33,6 +34,12 @@ func logStateTransitionData(b block.BeaconBlock) {
|
||||
if len(b.Body().VoluntaryExits()) > 0 {
|
||||
log = log.WithField("voluntaryExits", len(b.Body().VoluntaryExits()))
|
||||
}
|
||||
if b.Version() == version.Altair {
|
||||
agg, err := b.Body().SyncAggregate()
|
||||
if err == nil {
|
||||
log = log.WithField("syncBitsCount", agg.SyncCommitteeBits.Count())
|
||||
}
|
||||
}
|
||||
log.Info("Finished applying state transition")
|
||||
}
|
||||
|
||||
|
||||
@@ -3,15 +3,18 @@ package blockchain
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/altair"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch/precompute"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/version"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -107,6 +110,14 @@ var (
|
||||
Buckets: []float64{1, 2, 3, 4, 6, 32, 64},
|
||||
},
|
||||
)
|
||||
syncHeadStateMiss = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "sync_head_state_miss",
|
||||
Help: "The number of sync head state requests that are present in the cache.",
|
||||
})
|
||||
syncHeadStateHit = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "sync_head_state_hit",
|
||||
Help: "The number of sync head state requests that are not present in the cache.",
|
||||
})
|
||||
)
|
||||
|
||||
// reportSlotMetrics reports slot related metrics.
|
||||
@@ -206,14 +217,31 @@ func reportEpochMetrics(ctx context.Context, postState, headState state.BeaconSt
|
||||
beaconFinalizedRoot.Set(float64(bytesutil.ToLowInt64(postState.FinalizedCheckpoint().Root)))
|
||||
currentEth1DataDepositCount.Set(float64(postState.Eth1Data().DepositCount))
|
||||
|
||||
// Validator participation should be viewed on the canonical chain.
|
||||
v, b, err := precompute.New(ctx, headState)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, b, err = precompute.ProcessAttestations(ctx, headState, v, b)
|
||||
if err != nil {
|
||||
return err
|
||||
var b *precompute.Balance
|
||||
var v []*precompute.Validator
|
||||
var err error
|
||||
switch headState.Version() {
|
||||
case version.Phase0:
|
||||
// Validator participation should be viewed on the canonical chain.
|
||||
v, b, err = precompute.New(ctx, headState)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, b, err = precompute.ProcessAttestations(ctx, headState, v, b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case version.Altair:
|
||||
v, b, err = altair.InitializeEpochValidators(ctx, headState)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, b, err = altair.ProcessEpochParticipation(ctx, headState, b, v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errors.Errorf("invalid state type provided: %T", headState.InnerStateUnsafe())
|
||||
}
|
||||
prevEpochActiveBalances.Set(float64(b.ActivePrevEpoch))
|
||||
prevEpochSourceBalances.Set(float64(b.PrevEpochAttested))
|
||||
|
||||
@@ -62,6 +62,11 @@ func (mb *mockBroadcaster) BroadcastAttestation(_ context.Context, _ uint64, _ *
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mb *mockBroadcaster) BroadcastSyncCommitteeMessage(_ context.Context, _ uint64, _ *ethpb.SyncCommitteeMessage) error {
|
||||
mb.broadcastCalled = true
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ p2p.Broadcaster = (*mockBroadcaster)(nil)
|
||||
|
||||
func setupBeaconChain(t *testing.T, beaconDB db.Database) *Service {
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
statepb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
@@ -42,7 +41,7 @@ type ChainService struct {
|
||||
Genesis time.Time
|
||||
ValidatorsRoot [32]byte
|
||||
CanonicalRoots map[[32]byte]bool
|
||||
Fork *statepb.Fork
|
||||
Fork *ethpb.Fork
|
||||
ETH1Data *ethpb.Eth1Data
|
||||
DB db.Database
|
||||
stateNotifier statefeed.Notifier
|
||||
@@ -52,6 +51,13 @@ type ChainService struct {
|
||||
ForkChoiceStore *protoarray.Store
|
||||
VerifyBlkDescendantErr error
|
||||
Slot *types.Slot // Pointer because 0 is a useful value, so checking against it can be incorrect.
|
||||
CurrentSyncCommitteeIndices []types.CommitteeIndex
|
||||
NextSyncCommitteeIndices []types.CommitteeIndex
|
||||
SyncCommitteeDomain []byte
|
||||
SyncSelectionProofDomain []byte
|
||||
SyncContributionProofDomain []byte
|
||||
PublicKey [48]byte
|
||||
SyncCommitteePubkeys [][]byte
|
||||
}
|
||||
|
||||
// StateNotifier mocks the same method in the chain service.
|
||||
@@ -259,7 +265,7 @@ func (s *ChainService) HeadState(context.Context) (state.BeaconState, error) {
|
||||
}
|
||||
|
||||
// CurrentFork mocks HeadState method in chain service.
|
||||
func (s *ChainService) CurrentFork() *statepb.Fork {
|
||||
func (s *ChainService) CurrentFork() *ethpb.Fork {
|
||||
return s.Fork
|
||||
}
|
||||
|
||||
@@ -393,3 +399,43 @@ func (s *ChainService) ChainHeads() ([][32]byte, []types.Slot) {
|
||||
},
|
||||
[]types.Slot{0, 1}
|
||||
}
|
||||
|
||||
// HeadPublicKeyToValidatorIndex mocks HeadPublicKeyToValidatorIndex and always return 0 and true.
|
||||
func (s *ChainService) HeadPublicKeyToValidatorIndex(ctx context.Context, pubKey [48]byte) (types.ValidatorIndex, bool) {
|
||||
return 0, true
|
||||
}
|
||||
|
||||
// HeadValidatorIndexToPublicKey mocks HeadValidatorIndexToPublicKey and always return empty and nil.
|
||||
func (s *ChainService) HeadValidatorIndexToPublicKey(ctx context.Context, index types.ValidatorIndex) ([48]byte, error) {
|
||||
return s.PublicKey, nil
|
||||
}
|
||||
|
||||
// HeadCurrentSyncCommitteeIndices mocks HeadCurrentSyncCommitteeIndices and always return `CurrentSyncCommitteeIndices`.
|
||||
func (s *ChainService) HeadCurrentSyncCommitteeIndices(ctx context.Context, index types.ValidatorIndex, slot types.Slot) ([]types.CommitteeIndex, error) {
|
||||
return s.CurrentSyncCommitteeIndices, nil
|
||||
}
|
||||
|
||||
// HeadNextSyncCommitteeIndices mocks HeadNextSyncCommitteeIndices and always return `HeadNextSyncCommitteeIndices`.
|
||||
func (s *ChainService) HeadNextSyncCommitteeIndices(ctx context.Context, index types.ValidatorIndex, slot types.Slot) ([]types.CommitteeIndex, error) {
|
||||
return s.NextSyncCommitteeIndices, nil
|
||||
}
|
||||
|
||||
// HeadSyncCommitteePubKeys mocks HeadSyncCommitteePubKeys and always return empty nil.
|
||||
func (s *ChainService) HeadSyncCommitteePubKeys(ctx context.Context, slot types.Slot, committeeIndex types.CommitteeIndex) ([][]byte, error) {
|
||||
return s.SyncCommitteePubkeys, nil
|
||||
}
|
||||
|
||||
// HeadSyncCommitteeDomain mocks HeadSyncCommitteeDomain and always return empty nil.
|
||||
func (s *ChainService) HeadSyncCommitteeDomain(ctx context.Context, slot types.Slot) ([]byte, error) {
|
||||
return s.SyncCommitteeDomain, nil
|
||||
}
|
||||
|
||||
// HeadSyncSelectionProofDomain mocks HeadSyncSelectionProofDomain and always return empty nil.
|
||||
func (s *ChainService) HeadSyncSelectionProofDomain(ctx context.Context, slot types.Slot) ([]byte, error) {
|
||||
return s.SyncSelectionProofDomain, nil
|
||||
}
|
||||
|
||||
// HeadSyncContributionProofDomain mocks HeadSyncContributionProofDomain and always return empty nil.
|
||||
func (s *ChainService) HeadSyncContributionProofDomain(ctx context.Context, slot types.Slot) ([]byte, error) {
|
||||
return s.SyncContributionProofDomain, nil
|
||||
}
|
||||
|
||||
9
beacon-chain/cache/BUILD.bazel
vendored
9
beacon-chain/cache/BUILD.bazel
vendored
@@ -10,17 +10,21 @@ go_library(
|
||||
"committees.go",
|
||||
"common.go",
|
||||
"doc.go",
|
||||
"error.go",
|
||||
"proposer_indices_type.go",
|
||||
"skip_slot_cache.go",
|
||||
"subnet_ids.go",
|
||||
"sync_subnet_ids.go",
|
||||
] + select({
|
||||
"//fuzz:fuzzing_enabled": [
|
||||
"committee_disabled.go",
|
||||
"proposer_indices_disabled.go",
|
||||
"sync_committee_disabled.go",
|
||||
],
|
||||
"//conditions:default": [
|
||||
"committee.go",
|
||||
"proposer_indices.go",
|
||||
"sync_committee.go",
|
||||
],
|
||||
}),
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/cache",
|
||||
@@ -32,9 +36,11 @@ go_library(
|
||||
deps = [
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/copyutil:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/rand:go_default_library",
|
||||
"//shared/sliceutil:go_default_library",
|
||||
"@com_github_hashicorp_golang_lru//:go_default_library",
|
||||
"@com_github_patrickmn_go_cache//:go_default_library",
|
||||
@@ -58,6 +64,8 @@ go_test(
|
||||
"proposer_indices_test.go",
|
||||
"skip_slot_cache_test.go",
|
||||
"subnet_ids_test.go",
|
||||
"sync_committee_test.go",
|
||||
"sync_subnet_ids_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
@@ -66,6 +74,7 @@ go_test(
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"//shared/testutil/assert:go_default_library",
|
||||
"//shared/testutil/require:go_default_library",
|
||||
"@com_github_google_gofuzz//:go_default_library",
|
||||
|
||||
29
beacon-chain/cache/committee.go
vendored
29
beacon-chain/cache/committee.go
vendored
@@ -127,6 +127,35 @@ func (c *CommitteeCache) ActiveIndices(seed [32]byte) ([]types.ValidatorIndex, e
|
||||
return item.SortedIndices, nil
|
||||
}
|
||||
|
||||
// ActiveBalance returns the total active balance of a given seed stored in cache.
|
||||
func (c *CommitteeCache) ActiveBalance(seed [32]byte) (uint64, error) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
obj, exists := c.CommitteeCache.Get(key(seed))
|
||||
|
||||
if exists {
|
||||
CommitteeCacheHit.Inc()
|
||||
} else {
|
||||
CommitteeCacheMiss.Inc()
|
||||
return 0, ErrNonCommitteeKey
|
||||
}
|
||||
|
||||
item, ok := obj.(*Committees)
|
||||
if !ok {
|
||||
return 0, ErrNotCommittee
|
||||
}
|
||||
if item == nil {
|
||||
return 0, errors.New("item is nil")
|
||||
}
|
||||
|
||||
// Return `ErrNonCommitteeKey` if active balance field doesnt exist in item.
|
||||
if !item.ActiveBalance.Exist {
|
||||
return 0, ErrNonCommitteeKey
|
||||
}
|
||||
|
||||
return item.ActiveBalance.Total, nil
|
||||
}
|
||||
|
||||
// ActiveIndicesCount returns the active indices count of a given seed stored in cache.
|
||||
func (c *CommitteeCache) ActiveIndicesCount(seed [32]byte) (int, error) {
|
||||
c.lock.RLock()
|
||||
|
||||
5
beacon-chain/cache/committee_disabled.go
vendored
5
beacon-chain/cache/committee_disabled.go
vendored
@@ -41,6 +41,11 @@ func (c *FakeCommitteeCache) ActiveIndicesCount(seed [32]byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// ActiveBalance returns the active balance of a given seed stored in cache.
|
||||
func (c *FakeCommitteeCache) ActiveBalance(seed [32]byte) (uint64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// ProposerIndices returns the proposer indices of a given seed.
|
||||
func (c *FakeCommitteeCache) ProposerIndices(seed [32]byte) ([]types.ValidatorIndex, error) {
|
||||
return nil, nil
|
||||
|
||||
18
beacon-chain/cache/committee_test.go
vendored
18
beacon-chain/cache/committee_test.go
vendored
@@ -88,6 +88,24 @@ func TestCommitteeCache_ActiveCount(t *testing.T) {
|
||||
assert.Equal(t, len(item.SortedIndices), count)
|
||||
}
|
||||
|
||||
func TestCommitteeCache_ActiveBalance(t *testing.T) {
|
||||
cache := NewCommitteesCache()
|
||||
|
||||
balances := &Balance{
|
||||
Exist: true,
|
||||
Total: uint64(10000),
|
||||
}
|
||||
item := &Committees{Seed: [32]byte{'A'}, ActiveBalance: balances}
|
||||
_, err := cache.ActiveBalance(item.Seed)
|
||||
require.Equal(t, ErrNonCommitteeKey, err)
|
||||
|
||||
require.NoError(t, cache.AddCommitteeShuffledList(item))
|
||||
|
||||
got, err := cache.ActiveBalance(item.Seed)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, balances.Total, got)
|
||||
}
|
||||
|
||||
func TestCommitteeCache_CanRotate(t *testing.T) {
|
||||
cache := NewCommitteesCache()
|
||||
|
||||
|
||||
12
beacon-chain/cache/committees.go
vendored
12
beacon-chain/cache/committees.go
vendored
@@ -10,10 +10,22 @@ import (
|
||||
// a Committee struct.
|
||||
var ErrNotCommittee = errors.New("object is not a committee struct")
|
||||
|
||||
// ErrNonCommitteeKey will be returned when the committee key does not exist in cache.
|
||||
var ErrNonCommitteeKey = errors.New("committee key does not exist")
|
||||
|
||||
// Committees defines the shuffled committees seed.
|
||||
type Committees struct {
|
||||
CommitteeCount uint64
|
||||
Seed [32]byte
|
||||
ShuffledIndices []types.ValidatorIndex
|
||||
SortedIndices []types.ValidatorIndex
|
||||
ActiveBalance *Balance
|
||||
}
|
||||
|
||||
// Balance tracks active balance.
|
||||
// Given default uint64 is 0, `Exist` is used to distinguish whether
|
||||
// this field has been filed.
|
||||
type Balance struct {
|
||||
Exist bool
|
||||
Total uint64
|
||||
}
|
||||
|
||||
9
beacon-chain/cache/error.go
vendored
Normal file
9
beacon-chain/cache/error.go
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
package cache
|
||||
|
||||
import "errors"
|
||||
|
||||
// Sync committee cache related errors
|
||||
|
||||
// ErrNonExistingSyncCommitteeKey when sync committee key (root) does not exist in cache.
|
||||
var ErrNonExistingSyncCommitteeKey = errors.New("does not exist sync committee key")
|
||||
var errNotSyncCommitteeIndexPosition = errors.New("not syncCommitteeIndexPosition struct")
|
||||
187
beacon-chain/cache/sync_committee.go
vendored
Normal file
187
beacon-chain/cache/sync_committee.go
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
// +build !libfuzzer
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
var (
|
||||
maxSyncCommitteeSize = uint64(3) // Allows 3 forks to happen around `EPOCHS_PER_SYNC_COMMITTEE_PERIOD` boundary.
|
||||
|
||||
// SyncCommitteeCacheMiss tracks the number of committee requests that aren't present in the cache.
|
||||
SyncCommitteeCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "sync_committee_index_cache_miss",
|
||||
Help: "The number of committee requests that aren't present in the sync committee index cache.",
|
||||
})
|
||||
// SyncCommitteeCacheHit tracks the number of committee requests that are in the cache.
|
||||
SyncCommitteeCacheHit = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "sync_committee_index_cache_hit",
|
||||
Help: "The number of committee requests that are present in the sync committee index cache.",
|
||||
})
|
||||
)
|
||||
|
||||
// SyncCommitteeCache utilizes a FIFO cache to sufficiently cache validator position within sync committee.
|
||||
// It is thread safe with concurrent read write.
|
||||
type SyncCommitteeCache struct {
|
||||
cache *cache.FIFO
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// Index position of all validators in sync committee where `currentSyncCommitteeRoot` is the
|
||||
// key and `vIndexToPositionMap` is value. Inside `vIndexToPositionMap`, validator positions
|
||||
// are cached where key is the validator index and the value is the `positionInCommittee` struct.
|
||||
type syncCommitteeIndexPosition struct {
|
||||
currentSyncCommitteeRoot [32]byte
|
||||
vIndexToPositionMap map[types.ValidatorIndex]*positionInCommittee
|
||||
}
|
||||
|
||||
// Index position of individual validator of current period and next period sync committee.
|
||||
type positionInCommittee struct {
|
||||
currentPeriod []types.CommitteeIndex
|
||||
nextPeriod []types.CommitteeIndex
|
||||
}
|
||||
|
||||
// NewSyncCommittee initializes and returns a new SyncCommitteeCache.
|
||||
func NewSyncCommittee() *SyncCommitteeCache {
|
||||
return &SyncCommitteeCache{
|
||||
cache: cache.NewFIFO(keyFn),
|
||||
}
|
||||
}
|
||||
|
||||
// CurrentPeriodIndexPosition returns current period index position of a validator index with respect with
|
||||
// sync committee. If the input validator index has no assignment, an empty list will be returned.
|
||||
// If the input root does not exist in cache, ErrNonExistingSyncCommitteeKey is returned.
|
||||
// Then performing manual checking of state for index position in state is recommended.
|
||||
func (s *SyncCommitteeCache) CurrentPeriodIndexPosition(root [32]byte, valIdx types.ValidatorIndex) ([]types.CommitteeIndex, error) {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
|
||||
pos, err := s.idxPositionInCommittee(root, valIdx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pos == nil {
|
||||
return []types.CommitteeIndex{}, nil
|
||||
}
|
||||
|
||||
return pos.currentPeriod, nil
|
||||
}
|
||||
|
||||
// NextPeriodIndexPosition returns next period index position of a validator index in respect with sync committee.
|
||||
// If the input validator index has no assignment, an empty list will be returned.
|
||||
// If the input root does not exist in cache, ErrNonExistingSyncCommitteeKey is returned.
|
||||
// Then performing manual checking of state for index position in state is recommended.
|
||||
func (s *SyncCommitteeCache) NextPeriodIndexPosition(root [32]byte, valIdx types.ValidatorIndex) ([]types.CommitteeIndex, error) {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
|
||||
pos, err := s.idxPositionInCommittee(root, valIdx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pos == nil {
|
||||
return []types.CommitteeIndex{}, nil
|
||||
}
|
||||
return pos.nextPeriod, nil
|
||||
}
|
||||
|
||||
// Helper function for `CurrentPeriodIndexPosition` and `NextPeriodIndexPosition` to return a mapping
|
||||
// of validator index to its index(s) position in the sync committee.
|
||||
func (s *SyncCommitteeCache) idxPositionInCommittee(
|
||||
root [32]byte, valIdx types.ValidatorIndex,
|
||||
) (*positionInCommittee, error) {
|
||||
obj, exists, err := s.cache.GetByKey(key(root))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !exists {
|
||||
SyncCommitteeCacheMiss.Inc()
|
||||
return nil, ErrNonExistingSyncCommitteeKey
|
||||
}
|
||||
item, ok := obj.(*syncCommitteeIndexPosition)
|
||||
if !ok {
|
||||
return nil, errNotSyncCommitteeIndexPosition
|
||||
}
|
||||
idxInCommittee, ok := item.vIndexToPositionMap[valIdx]
|
||||
if !ok {
|
||||
SyncCommitteeCacheMiss.Inc()
|
||||
return nil, nil
|
||||
}
|
||||
SyncCommitteeCacheHit.Inc()
|
||||
return idxInCommittee, nil
|
||||
}
|
||||
|
||||
// UpdatePositionsInCommittee updates caching of validators position in sync committee in respect to
|
||||
// current epoch and next epoch. This should be called when `current_sync_committee` and `next_sync_committee`
|
||||
// change and that happens every `EPOCHS_PER_SYNC_COMMITTEE_PERIOD`.
|
||||
func (s *SyncCommitteeCache) UpdatePositionsInCommittee(syncCommitteeBoundaryRoot [32]byte, st state.BeaconStateAltair) error {
|
||||
csc, err := st.CurrentSyncCommittee()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
positionsMap := make(map[types.ValidatorIndex]*positionInCommittee)
|
||||
for i, pubkey := range csc.Pubkeys {
|
||||
p := bytesutil.ToBytes48(pubkey)
|
||||
validatorIndex, ok := st.ValidatorIndexByPubkey(p)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := positionsMap[validatorIndex]; !ok {
|
||||
m := &positionInCommittee{currentPeriod: []types.CommitteeIndex{types.CommitteeIndex(i)}, nextPeriod: []types.CommitteeIndex{}}
|
||||
positionsMap[validatorIndex] = m
|
||||
} else {
|
||||
positionsMap[validatorIndex].currentPeriod = append(positionsMap[validatorIndex].currentPeriod, types.CommitteeIndex(i))
|
||||
}
|
||||
}
|
||||
|
||||
nsc, err := st.NextSyncCommittee()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i, pubkey := range nsc.Pubkeys {
|
||||
p := bytesutil.ToBytes48(pubkey)
|
||||
validatorIndex, ok := st.ValidatorIndexByPubkey(p)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := positionsMap[validatorIndex]; !ok {
|
||||
m := &positionInCommittee{nextPeriod: []types.CommitteeIndex{types.CommitteeIndex(i)}, currentPeriod: []types.CommitteeIndex{}}
|
||||
positionsMap[validatorIndex] = m
|
||||
} else {
|
||||
positionsMap[validatorIndex].nextPeriod = append(positionsMap[validatorIndex].nextPeriod, types.CommitteeIndex(i))
|
||||
}
|
||||
}
|
||||
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
if err := s.cache.Add(&syncCommitteeIndexPosition{
|
||||
currentSyncCommitteeRoot: syncCommitteeBoundaryRoot,
|
||||
vIndexToPositionMap: positionsMap,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
trim(s.cache, maxSyncCommitteeSize)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Given the `syncCommitteeIndexPosition` object, this returns the key of the object.
|
||||
// The key is the `currentSyncCommitteeRoot` within the field.
|
||||
// Error gets returned if input does not comply with `currentSyncCommitteeRoot` object.
|
||||
func keyFn(obj interface{}) (string, error) {
|
||||
info, ok := obj.(*syncCommitteeIndexPosition)
|
||||
if !ok {
|
||||
return "", errNotSyncCommitteeIndexPosition
|
||||
}
|
||||
|
||||
return string(info.currentSyncCommitteeRoot[:]), nil
|
||||
}
|
||||
32
beacon-chain/cache/sync_committee_disabled.go
vendored
Normal file
32
beacon-chain/cache/sync_committee_disabled.go
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
// +build libfuzzer
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
)
|
||||
|
||||
// FakeSyncCommitteeCache is a fake `SyncCommitteeCache` to satisfy fuzzing.
|
||||
type FakeSyncCommitteeCache struct {
|
||||
}
|
||||
|
||||
// NewSyncCommittee initializes and returns a new SyncCommitteeCache.
|
||||
func NewSyncCommittee() *FakeSyncCommitteeCache {
|
||||
return &FakeSyncCommitteeCache{}
|
||||
}
|
||||
|
||||
// CurrentEpochIndexPosition -- fake.
|
||||
func (s *FakeSyncCommitteeCache) CurrentPeriodIndexPosition(root [32]byte, valIdx types.ValidatorIndex) ([]types.CommitteeIndex, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NextEpochIndexPosition -- fake.
|
||||
func (s *FakeSyncCommitteeCache) NextPeriodIndexPosition(root [32]byte, valIdx types.ValidatorIndex) ([]types.CommitteeIndex, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// UpdatePositionsInCommittee -- fake.
|
||||
func (s *FakeSyncCommitteeCache) UpdatePositionsInCommittee(syncCommitteeBoundaryRoot [32]byte, state state.BeaconStateAltair) error {
|
||||
return nil
|
||||
}
|
||||
222
beacon-chain/cache/sync_committee_test.go
vendored
Normal file
222
beacon-chain/cache/sync_committee_test.go
vendored
Normal file
@@ -0,0 +1,222 @@
|
||||
package cache_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
|
||||
func TestSyncCommitteeCache_CanUpdateAndRetrieve(t *testing.T) {
|
||||
numValidators := 101
|
||||
deterministicState, _ := testutil.DeterministicGenesisStateAltair(t, uint64(numValidators))
|
||||
pubKeys := make([][]byte, deterministicState.NumValidators())
|
||||
for i, val := range deterministicState.Validators() {
|
||||
pubKeys[i] = val.PublicKey
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
currentSyncCommittee *ethpb.SyncCommittee
|
||||
nextSyncCommittee *ethpb.SyncCommittee
|
||||
currentSyncMap map[types.ValidatorIndex][]types.CommitteeIndex
|
||||
nextSyncMap map[types.ValidatorIndex][]types.CommitteeIndex
|
||||
}{
|
||||
{
|
||||
name: "only current epoch",
|
||||
currentSyncCommittee: convertToCommittee([][]byte{
|
||||
pubKeys[1], pubKeys[2], pubKeys[3], pubKeys[2], pubKeys[2],
|
||||
}),
|
||||
nextSyncCommittee: convertToCommittee([][]byte{}),
|
||||
currentSyncMap: map[types.ValidatorIndex][]types.CommitteeIndex{
|
||||
1: {0},
|
||||
2: {1, 3, 4},
|
||||
3: {2},
|
||||
},
|
||||
nextSyncMap: map[types.ValidatorIndex][]types.CommitteeIndex{
|
||||
1: {},
|
||||
2: {},
|
||||
3: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "only next epoch",
|
||||
currentSyncCommittee: convertToCommittee([][]byte{}),
|
||||
nextSyncCommittee: convertToCommittee([][]byte{
|
||||
pubKeys[1], pubKeys[2], pubKeys[3], pubKeys[2], pubKeys[2],
|
||||
}),
|
||||
currentSyncMap: map[types.ValidatorIndex][]types.CommitteeIndex{
|
||||
1: {},
|
||||
2: {},
|
||||
3: {},
|
||||
},
|
||||
nextSyncMap: map[types.ValidatorIndex][]types.CommitteeIndex{
|
||||
1: {0},
|
||||
2: {1, 3, 4},
|
||||
3: {2},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "some current epoch and some next epoch",
|
||||
currentSyncCommittee: convertToCommittee([][]byte{
|
||||
pubKeys[1],
|
||||
pubKeys[2],
|
||||
pubKeys[3],
|
||||
pubKeys[2],
|
||||
pubKeys[2],
|
||||
}),
|
||||
nextSyncCommittee: convertToCommittee([][]byte{
|
||||
pubKeys[7],
|
||||
pubKeys[6],
|
||||
pubKeys[5],
|
||||
pubKeys[4],
|
||||
pubKeys[7],
|
||||
}),
|
||||
currentSyncMap: map[types.ValidatorIndex][]types.CommitteeIndex{
|
||||
1: {0},
|
||||
2: {1, 3, 4},
|
||||
3: {2},
|
||||
},
|
||||
nextSyncMap: map[types.ValidatorIndex][]types.CommitteeIndex{
|
||||
7: {0, 4},
|
||||
6: {1},
|
||||
5: {2},
|
||||
4: {3},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "some current epoch and some next epoch duplicated across",
|
||||
currentSyncCommittee: convertToCommittee([][]byte{
|
||||
pubKeys[1],
|
||||
pubKeys[2],
|
||||
pubKeys[3],
|
||||
pubKeys[2],
|
||||
pubKeys[2],
|
||||
}),
|
||||
nextSyncCommittee: convertToCommittee([][]byte{
|
||||
pubKeys[2],
|
||||
pubKeys[1],
|
||||
pubKeys[3],
|
||||
pubKeys[2],
|
||||
pubKeys[1],
|
||||
}),
|
||||
currentSyncMap: map[types.ValidatorIndex][]types.CommitteeIndex{
|
||||
1: {0},
|
||||
2: {1, 3, 4},
|
||||
3: {2},
|
||||
},
|
||||
nextSyncMap: map[types.ValidatorIndex][]types.CommitteeIndex{
|
||||
1: {1, 4},
|
||||
2: {0, 3},
|
||||
3: {2},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "all duplicated",
|
||||
currentSyncCommittee: convertToCommittee([][]byte{
|
||||
pubKeys[100],
|
||||
pubKeys[100],
|
||||
pubKeys[100],
|
||||
pubKeys[100],
|
||||
}),
|
||||
nextSyncCommittee: convertToCommittee([][]byte{
|
||||
pubKeys[100],
|
||||
pubKeys[100],
|
||||
pubKeys[100],
|
||||
pubKeys[100],
|
||||
}),
|
||||
currentSyncMap: map[types.ValidatorIndex][]types.CommitteeIndex{
|
||||
100: {0, 1, 2, 3},
|
||||
},
|
||||
nextSyncMap: map[types.ValidatorIndex][]types.CommitteeIndex{
|
||||
100: {0, 1, 2, 3},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unknown keys",
|
||||
currentSyncCommittee: convertToCommittee([][]byte{
|
||||
pubKeys[100],
|
||||
pubKeys[100],
|
||||
pubKeys[100],
|
||||
pubKeys[100],
|
||||
}),
|
||||
nextSyncCommittee: convertToCommittee([][]byte{
|
||||
pubKeys[100],
|
||||
pubKeys[100],
|
||||
pubKeys[100],
|
||||
pubKeys[100],
|
||||
}),
|
||||
currentSyncMap: map[types.ValidatorIndex][]types.CommitteeIndex{
|
||||
1: {},
|
||||
},
|
||||
nextSyncMap: map[types.ValidatorIndex][]types.CommitteeIndex{
|
||||
1: {},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s, _ := testutil.DeterministicGenesisStateAltair(t, uint64(numValidators))
|
||||
require.NoError(t, s.SetCurrentSyncCommittee(tt.currentSyncCommittee))
|
||||
require.NoError(t, s.SetNextSyncCommittee(tt.nextSyncCommittee))
|
||||
cache := cache.NewSyncCommittee()
|
||||
r := [32]byte{'a'}
|
||||
require.NoError(t, cache.UpdatePositionsInCommittee(r, s))
|
||||
for key, indices := range tt.currentSyncMap {
|
||||
pos, err := cache.CurrentPeriodIndexPosition(r, key)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, indices, pos)
|
||||
}
|
||||
for key, indices := range tt.nextSyncMap {
|
||||
pos, err := cache.NextPeriodIndexPosition(r, key)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, indices, pos)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncCommitteeCache_RootDoesNotExist(t *testing.T) {
|
||||
c := cache.NewSyncCommittee()
|
||||
_, err := c.CurrentPeriodIndexPosition([32]byte{}, 0)
|
||||
require.Equal(t, cache.ErrNonExistingSyncCommitteeKey, err)
|
||||
}
|
||||
|
||||
func TestSyncCommitteeCache_CanRotate(t *testing.T) {
|
||||
c := cache.NewSyncCommittee()
|
||||
s, _ := testutil.DeterministicGenesisStateAltair(t, 64)
|
||||
require.NoError(t, s.SetCurrentSyncCommittee(convertToCommittee([][]byte{{1}})))
|
||||
require.NoError(t, c.UpdatePositionsInCommittee([32]byte{'a'}, s))
|
||||
require.NoError(t, s.SetCurrentSyncCommittee(convertToCommittee([][]byte{{2}})))
|
||||
require.NoError(t, c.UpdatePositionsInCommittee([32]byte{'b'}, s))
|
||||
require.NoError(t, s.SetCurrentSyncCommittee(convertToCommittee([][]byte{{3}})))
|
||||
require.NoError(t, c.UpdatePositionsInCommittee([32]byte{'c'}, s))
|
||||
require.NoError(t, s.SetCurrentSyncCommittee(convertToCommittee([][]byte{{4}})))
|
||||
require.NoError(t, c.UpdatePositionsInCommittee([32]byte{'d'}, s))
|
||||
|
||||
_, err := c.CurrentPeriodIndexPosition([32]byte{'a'}, 0)
|
||||
require.Equal(t, cache.ErrNonExistingSyncCommitteeKey, err)
|
||||
|
||||
_, err = c.CurrentPeriodIndexPosition([32]byte{'c'}, 0)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func convertToCommittee(inputKeys [][]byte) *ethpb.SyncCommittee {
|
||||
var pubKeys [][]byte
|
||||
for i := uint64(0); i < params.BeaconConfig().SyncCommitteeSize; i++ {
|
||||
if i < uint64(len(inputKeys)) {
|
||||
pubKeys = append(pubKeys, bytesutil.PadTo(inputKeys[i], params.BeaconConfig().BLSPubkeyLength))
|
||||
} else {
|
||||
pubKeys = append(pubKeys, bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength))
|
||||
}
|
||||
}
|
||||
|
||||
return ðpb.SyncCommittee{
|
||||
Pubkeys: pubKeys,
|
||||
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
|
||||
}
|
||||
}
|
||||
120
beacon-chain/cache/sync_subnet_ids.go
vendored
Normal file
120
beacon-chain/cache/sync_subnet_ids.go
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/patrickmn/go-cache"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/rand"
|
||||
"github.com/prysmaticlabs/prysm/shared/sliceutil"
|
||||
)
|
||||
|
||||
type syncSubnetIDs struct {
|
||||
sCommittee *cache.Cache
|
||||
sCommiteeLock sync.RWMutex
|
||||
}
|
||||
|
||||
// SyncSubnetIDs for sync committee participant.
|
||||
var SyncSubnetIDs = newSyncSubnetIDs()
|
||||
|
||||
func newSyncSubnetIDs() *syncSubnetIDs {
|
||||
epochDuration := time.Duration(params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().SecondsPerSlot))
|
||||
subLength := epochDuration * time.Duration(params.BeaconConfig().EpochsPerSyncCommitteePeriod)
|
||||
persistentCache := cache.New(subLength*time.Second, epochDuration*time.Second)
|
||||
return &syncSubnetIDs{sCommittee: persistentCache}
|
||||
}
|
||||
|
||||
// GetSyncCommitteeSubnets retrieves the sync committee subnet and expiration time of that validator's
|
||||
// subscription.
|
||||
func (s *syncSubnetIDs) GetSyncCommitteeSubnets(pubkey []byte, epoch types.Epoch) ([]uint64, types.Epoch, bool, time.Time) {
|
||||
s.sCommiteeLock.RLock()
|
||||
defer s.sCommiteeLock.RUnlock()
|
||||
|
||||
id, duration, ok := s.sCommittee.GetWithExpiration(keyBuilder(pubkey, epoch))
|
||||
if !ok {
|
||||
return []uint64{}, 0, ok, time.Time{}
|
||||
}
|
||||
// Retrieve the slot from the cache.
|
||||
idxs, ok := id.([]uint64)
|
||||
if !ok {
|
||||
return []uint64{}, 0, ok, time.Time{}
|
||||
}
|
||||
// If no committees are saved, we return
|
||||
// nothing.
|
||||
if len(idxs) <= 1 {
|
||||
return []uint64{}, 0, ok, time.Time{}
|
||||
}
|
||||
return idxs[1:], types.Epoch(idxs[0]), ok, duration
|
||||
}
|
||||
|
||||
// GetAllSubnets retrieves all the non-expired subscribed subnets of all the validators
|
||||
// in the cache. This method also takes the epoch as an argument to only retrieve
|
||||
// assingments for epochs that have happened.
|
||||
func (s *syncSubnetIDs) GetAllSubnets(currEpoch types.Epoch) []uint64 {
|
||||
s.sCommiteeLock.RLock()
|
||||
defer s.sCommiteeLock.RUnlock()
|
||||
|
||||
itemsMap := s.sCommittee.Items()
|
||||
var committees []uint64
|
||||
|
||||
for _, v := range itemsMap {
|
||||
if v.Expired() {
|
||||
continue
|
||||
}
|
||||
idxs, ok := v.Object.([]uint64)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if len(idxs) <= 1 {
|
||||
continue
|
||||
}
|
||||
// Check if the subnet is valid in the current epoch.
|
||||
if types.Epoch(idxs[0]) > currEpoch {
|
||||
continue
|
||||
}
|
||||
// Ignore the first index as that represents the
|
||||
// epoch of the validator's assignments.
|
||||
committees = append(committees, idxs[1:]...)
|
||||
}
|
||||
return sliceutil.SetUint64(committees)
|
||||
}
|
||||
|
||||
// AddSyncCommitteeSubnets adds the relevant committee for that particular validator along with its
|
||||
// expiration period. An Epoch argument here denotes the epoch from which the sync committee subnets
|
||||
// will be active.
|
||||
func (s *syncSubnetIDs) AddSyncCommitteeSubnets(pubkey []byte, epoch types.Epoch, comIndex []uint64, duration time.Duration) {
|
||||
s.sCommiteeLock.Lock()
|
||||
defer s.sCommiteeLock.Unlock()
|
||||
subComCount := params.BeaconConfig().SyncCommitteeSubnetCount
|
||||
// To join a sync committee subnet, select a random number of epochs before the end of the
|
||||
// current sync committee period between 1 and SYNC_COMMITTEE_SUBNET_COUNT, inclusive.
|
||||
diff := (rand.NewGenerator().Uint64() % subComCount) + 1
|
||||
joinEpoch, err := epoch.SafeSub(diff)
|
||||
if err != nil {
|
||||
// If we overflow here, we simply set the value to join
|
||||
// at 0.
|
||||
joinEpoch = 0
|
||||
}
|
||||
// Append the epoch of the subnet into the first index here.
|
||||
s.sCommittee.Set(keyBuilder(pubkey, epoch), append([]uint64{uint64(joinEpoch)}, comIndex...), duration)
|
||||
}
|
||||
|
||||
// EmptyAllCaches empties out all the related caches and flushes any stored
|
||||
// entries on them. This should only ever be used for testing, in normal
|
||||
// production, handling of the relevant subnets for each role is done
|
||||
// separately.
|
||||
func (s *syncSubnetIDs) EmptyAllCaches() {
|
||||
// Clear the cache.
|
||||
|
||||
s.sCommiteeLock.Lock()
|
||||
s.sCommittee.Flush()
|
||||
s.sCommiteeLock.Unlock()
|
||||
}
|
||||
|
||||
func keyBuilder(pubkey []byte, epoch types.Epoch) string {
|
||||
epochBytes := bytesutil.Bytes8(uint64(epoch))
|
||||
return string(append(pubkey, epochBytes...))
|
||||
}
|
||||
57
beacon-chain/cache/sync_subnet_ids_test.go
vendored
Normal file
57
beacon-chain/cache/sync_subnet_ids_test.go
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
|
||||
func TestSyncSubnetIDsCache_Roundtrip(t *testing.T) {
|
||||
c := newSyncSubnetIDs()
|
||||
|
||||
for i := 0; i < 20; i++ {
|
||||
pubkey := [48]byte{byte(i)}
|
||||
c.AddSyncCommitteeSubnets(pubkey[:], 100, []uint64{uint64(i)}, 0)
|
||||
}
|
||||
|
||||
for i := uint64(0); i < 20; i++ {
|
||||
pubkey := [48]byte{byte(i)}
|
||||
|
||||
idxs, _, ok, _ := c.GetSyncCommitteeSubnets(pubkey[:], 100)
|
||||
if !ok {
|
||||
t.Errorf("Couldn't find entry in cache for pubkey %#x", pubkey)
|
||||
continue
|
||||
}
|
||||
require.Equal(t, i, idxs[0])
|
||||
}
|
||||
coms := c.GetAllSubnets(100)
|
||||
assert.Equal(t, 20, len(coms))
|
||||
}
|
||||
|
||||
func TestSyncSubnetIDsCache_ValidateCurrentEpoch(t *testing.T) {
|
||||
c := newSyncSubnetIDs()
|
||||
|
||||
for i := 0; i < 20; i++ {
|
||||
pubkey := [48]byte{byte(i)}
|
||||
c.AddSyncCommitteeSubnets(pubkey[:], 100, []uint64{uint64(i)}, 0)
|
||||
}
|
||||
|
||||
coms := c.GetAllSubnets(50)
|
||||
assert.Equal(t, 0, len(coms))
|
||||
|
||||
for i := uint64(0); i < 20; i++ {
|
||||
pubkey := [48]byte{byte(i)}
|
||||
|
||||
_, jEpoch, ok, _ := c.GetSyncCommitteeSubnets(pubkey[:], 100)
|
||||
if !ok {
|
||||
t.Errorf("Couldn't find entry in cache for pubkey %#x", pubkey)
|
||||
continue
|
||||
}
|
||||
require.Equal(t, true, uint64(jEpoch) >= 100-params.BeaconConfig().SyncCommitteeSubnetCount)
|
||||
}
|
||||
|
||||
coms = c.GetAllSubnets(99)
|
||||
assert.Equal(t, 20, len(coms))
|
||||
}
|
||||
@@ -3,19 +3,36 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"attestation.go",
|
||||
"block.go",
|
||||
"deposit.go",
|
||||
"epoch_precompute.go",
|
||||
"epoch_spec.go",
|
||||
"fork_transition.go",
|
||||
"reward.go",
|
||||
"sync_committee.go",
|
||||
"transition.go",
|
||||
"validator.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/altair",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//shared/testutil:__pkg__",
|
||||
"//spectest:__subpackages__",
|
||||
"//validator/client:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/epoch:go_default_library",
|
||||
"//beacon-chain/core/epoch/precompute:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/core/validators:go_default_library",
|
||||
"//beacon-chain/p2p/types:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/v2:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//proto/prysm/v1alpha1/block:go_default_library",
|
||||
"//shared/attestationutil:go_default_library",
|
||||
"//shared/bls:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
@@ -23,28 +40,46 @@ go_library(
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
"@io_opencensus_go//trace:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"attestation_test.go",
|
||||
"block_test.go",
|
||||
"deposit_fuzz_test.go",
|
||||
"deposit_test.go",
|
||||
"epoch_precompute_test.go",
|
||||
"epoch_spec_test.go",
|
||||
"fork_transition_test.go",
|
||||
"reward_test.go",
|
||||
"sync_committee_test.go",
|
||||
"transition_test.go",
|
||||
"validator_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
":go_default_library",
|
||||
"//beacon-chain/core/epoch/precompute:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/p2p/types:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/v2:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//proto/prysm/v1alpha1/wrapper:go_default_library",
|
||||
"//shared/attestationutil:go_default_library",
|
||||
"//shared/bls:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"//shared/testutil/assert:go_default_library",
|
||||
"//shared/testutil/require:go_default_library",
|
||||
"//shared/timeutils:go_default_library",
|
||||
"//shared/trieutil:go_default_library",
|
||||
"@com_github_google_gofuzz//:go_default_library",
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
"@org_golang_google_protobuf//proto:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
284
beacon-chain/core/altair/attestation.go
Normal file
284
beacon-chain/core/altair/attestation.go
Normal file
@@ -0,0 +1,284 @@
|
||||
package altair
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
|
||||
"github.com/prysmaticlabs/prysm/shared/attestationutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/mathutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
type matchingTarget bool
|
||||
type matchingSource bool
|
||||
type matchingHead bool
|
||||
|
||||
// ProcessAttestations applies processing operations to a block's inner attestation
|
||||
// records.
|
||||
func ProcessAttestations(
|
||||
ctx context.Context,
|
||||
beaconState state.BeaconState,
|
||||
b block.SignedBeaconBlock,
|
||||
) (state.BeaconState, error) {
|
||||
if err := helpers.VerifyNilBeaconBlock(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var err error
|
||||
for idx, attestation := range b.Block().Body().Attestations() {
|
||||
beaconState, err = ProcessAttestation(ctx, beaconState, attestation)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not verify attestation at index %d in block", idx)
|
||||
}
|
||||
}
|
||||
return beaconState, nil
|
||||
}
|
||||
|
||||
// ProcessAttestation verifies an input attestation can pass through processing using the given beacon state.
|
||||
//
|
||||
// Spec code:
|
||||
// def process_attestation(state: BeaconState, attestation: Attestation) -> None:
|
||||
// data = attestation.data
|
||||
// assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state))
|
||||
// assert data.target.epoch == compute_epoch_at_slot(data.slot)
|
||||
// assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= data.slot + SLOTS_PER_EPOCH
|
||||
// assert data.index < get_committee_count_per_slot(state, data.target.epoch)
|
||||
//
|
||||
// committee = get_beacon_committee(state, data.slot, data.index)
|
||||
// assert len(attestation.aggregation_bits) == len(committee)
|
||||
//
|
||||
// # Participation flag indices
|
||||
// participation_flag_indices = get_attestation_participation_flag_indices(state, data, state.slot - data.slot)
|
||||
//
|
||||
// # Verify signature
|
||||
// assert is_valid_indexed_attestation(state, get_indexed_attestation(state, attestation))
|
||||
//
|
||||
// # Update epoch participation flags
|
||||
// if data.target.epoch == get_current_epoch(state):
|
||||
// epoch_participation = state.current_epoch_participation
|
||||
// else:
|
||||
// epoch_participation = state.previous_epoch_participation
|
||||
//
|
||||
// proposer_reward_numerator = 0
|
||||
// for index in get_attesting_indices(state, data, attestation.aggregation_bits):
|
||||
// for flag_index, weight in enumerate(PARTICIPATION_FLAG_WEIGHTS):
|
||||
// if flag_index in participation_flag_indices and not has_flag(epoch_participation[index], flag_index):
|
||||
// epoch_participation[index] = add_flag(epoch_participation[index], flag_index)
|
||||
// proposer_reward_numerator += get_base_reward(state, index) * weight
|
||||
//
|
||||
// # Reward proposer
|
||||
// proposer_reward_denominator = (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT
|
||||
// proposer_reward = Gwei(proposer_reward_numerator // proposer_reward_denominator)
|
||||
// increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
|
||||
func ProcessAttestation(
|
||||
ctx context.Context,
|
||||
beaconState state.BeaconStateAltair,
|
||||
att *ethpb.Attestation,
|
||||
) (state.BeaconStateAltair, error) {
|
||||
beaconState, err := ProcessAttestationNoVerifySignature(ctx, beaconState, att)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return beaconState, blocks.VerifyAttestationSignature(ctx, beaconState, att)
|
||||
}
|
||||
|
||||
// ProcessAttestationsNoVerifySignature applies processing operations to a block's inner attestation
|
||||
// records. The only difference would be that the attestation signature would not be verified.
|
||||
func ProcessAttestationsNoVerifySignature(
|
||||
ctx context.Context,
|
||||
beaconState state.BeaconState,
|
||||
b block.SignedBeaconBlock,
|
||||
) (state.BeaconState, error) {
|
||||
if err := helpers.VerifyNilBeaconBlock(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body := b.Block().Body()
|
||||
var err error
|
||||
for idx, attestation := range body.Attestations() {
|
||||
beaconState, err = ProcessAttestationNoVerifySignature(ctx, beaconState, attestation)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not verify attestation at index %d in block", idx)
|
||||
}
|
||||
}
|
||||
return beaconState, nil
|
||||
}
|
||||
|
||||
// ProcessAttestationNoVerifySignature processes the attestation without verifying the attestation signature. This
|
||||
// method is used to validate attestations whose signatures have already been verified or will be verified later.
|
||||
func ProcessAttestationNoVerifySignature(
|
||||
ctx context.Context,
|
||||
beaconState state.BeaconStateAltair,
|
||||
att *ethpb.Attestation,
|
||||
) (state.BeaconStateAltair, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "altair.ProcessAttestationNoVerifySignature")
|
||||
defer span.End()
|
||||
|
||||
if err := blocks.VerifyAttestationNoVerifySignature(ctx, beaconState, att); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
delay, err := beaconState.Slot().SafeSubSlot(att.Data.Slot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
participatedFlags, err := attestationParticipationFlagIndices(
|
||||
beaconState,
|
||||
att.Data,
|
||||
delay)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
committee, err := helpers.BeaconCommitteeFromState(beaconState, att.Data.Slot, att.Data.CommitteeIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
indices, err := attestationutil.AttestingIndices(att.AggregationBits, committee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var epochParticipation []byte
|
||||
currentEpoch := helpers.CurrentEpoch(beaconState)
|
||||
targetEpoch := att.Data.Target.Epoch
|
||||
if targetEpoch == currentEpoch {
|
||||
epochParticipation, err = beaconState.CurrentEpochParticipation()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
epochParticipation, err = beaconState.PreviousEpochParticipation()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
sourceFlagIndex := params.BeaconConfig().TimelySourceFlagIndex
|
||||
targetFlagIndex := params.BeaconConfig().TimelyTargetFlagIndex
|
||||
headFlagIndex := params.BeaconConfig().TimelyHeadFlagIndex
|
||||
proposerRewardNumerator := uint64(0)
|
||||
totalBalance, err := helpers.TotalActiveBalance(beaconState)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not calculate active balance")
|
||||
}
|
||||
for _, index := range indices {
|
||||
br, err := BaseRewardWithTotalBalance(beaconState, types.ValidatorIndex(index), totalBalance)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if participatedFlags[sourceFlagIndex] && !HasValidatorFlag(epochParticipation[index], sourceFlagIndex) {
|
||||
epochParticipation[index] = AddValidatorFlag(epochParticipation[index], sourceFlagIndex)
|
||||
proposerRewardNumerator += br * params.BeaconConfig().TimelySourceWeight
|
||||
}
|
||||
if participatedFlags[targetFlagIndex] && !HasValidatorFlag(epochParticipation[index], targetFlagIndex) {
|
||||
epochParticipation[index] = AddValidatorFlag(epochParticipation[index], targetFlagIndex)
|
||||
proposerRewardNumerator += br * params.BeaconConfig().TimelyTargetWeight
|
||||
}
|
||||
if participatedFlags[headFlagIndex] && !HasValidatorFlag(epochParticipation[index], headFlagIndex) {
|
||||
epochParticipation[index] = AddValidatorFlag(epochParticipation[index], headFlagIndex)
|
||||
proposerRewardNumerator += br * params.BeaconConfig().TimelyHeadWeight
|
||||
}
|
||||
}
|
||||
|
||||
if targetEpoch == currentEpoch {
|
||||
if err := beaconState.SetCurrentParticipationBits(epochParticipation); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err := beaconState.SetPreviousParticipationBits(epochParticipation); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Reward proposer.
|
||||
if err := rewardProposer(beaconState, proposerRewardNumerator); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return beaconState, nil
|
||||
}
|
||||
|
||||
// This returns the matching statues for attestation data's source target and head.
|
||||
func matchingStatus(beaconState state.BeaconState, data *ethpb.AttestationData, cp *ethpb.Checkpoint) (s matchingSource, t matchingTarget, h matchingHead, err error) {
|
||||
s = matchingSource(attestationutil.CheckPointIsEqual(data.Source, cp))
|
||||
|
||||
r, err := helpers.BlockRoot(beaconState, data.Target.Epoch)
|
||||
if err != nil {
|
||||
return false, false, false, err
|
||||
}
|
||||
t = matchingTarget(bytes.Equal(r, data.Target.Root))
|
||||
|
||||
r, err = helpers.BlockRootAtSlot(beaconState, data.Slot)
|
||||
if err != nil {
|
||||
return false, false, false, err
|
||||
}
|
||||
h = matchingHead(bytes.Equal(r, data.BeaconBlockRoot))
|
||||
return
|
||||
}
|
||||
|
||||
// This rewards proposer by increasing proposer's balance with input reward numerator and calculated reward denominator.
|
||||
func rewardProposer(beaconState state.BeaconState, proposerRewardNumerator uint64) error {
|
||||
proposerRewardDenominator := (params.BeaconConfig().WeightDenominator - params.BeaconConfig().ProposerWeight) * params.BeaconConfig().WeightDenominator / params.BeaconConfig().ProposerWeight
|
||||
proposerReward := proposerRewardNumerator / proposerRewardDenominator
|
||||
i, err := helpers.BeaconProposerIndex(beaconState)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return helpers.IncreaseBalance(beaconState, i, proposerReward)
|
||||
}
|
||||
|
||||
// HasValidatorFlag returns true if the flag at position has set.
|
||||
func HasValidatorFlag(flag, flagPosition uint8) bool {
|
||||
return ((flag >> flagPosition) & 1) == 1
|
||||
}
|
||||
|
||||
// AddValidatorFlag adds new validator flag to existing one.
|
||||
func AddValidatorFlag(flag, flagPosition uint8) uint8 {
|
||||
return flag | (1 << flagPosition)
|
||||
}
|
||||
|
||||
// This retrieves a map of attestation scoring based on Altair's participation flag indices.
|
||||
// This is used to facilitate process attestation during state transition.
|
||||
func attestationParticipationFlagIndices(beaconState state.BeaconStateAltair, data *ethpb.AttestationData, delay types.Slot) (map[uint8]bool, error) {
|
||||
currEpoch := helpers.CurrentEpoch(beaconState)
|
||||
var justifiedCheckpt *ethpb.Checkpoint
|
||||
if data.Target.Epoch == currEpoch {
|
||||
justifiedCheckpt = beaconState.CurrentJustifiedCheckpoint()
|
||||
} else {
|
||||
justifiedCheckpt = beaconState.PreviousJustifiedCheckpoint()
|
||||
}
|
||||
|
||||
// Get matching participation flags.
|
||||
matchingSource, matchingTarget, matchingHead, err := matchingStatus(beaconState, data, justifiedCheckpt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !matchingSource {
|
||||
return nil, errors.New("source epoch does not match")
|
||||
}
|
||||
|
||||
// Process matched participation flags.
|
||||
participatedFlags := make(map[uint8]bool)
|
||||
sourceFlagIndex := params.BeaconConfig().TimelySourceFlagIndex
|
||||
targetFlagIndex := params.BeaconConfig().TimelyTargetFlagIndex
|
||||
headFlagIndex := params.BeaconConfig().TimelyHeadFlagIndex
|
||||
if matchingSource && delay <= types.Slot(mathutil.IntegerSquareRoot(uint64(params.BeaconConfig().SlotsPerEpoch))) {
|
||||
participatedFlags[sourceFlagIndex] = true
|
||||
}
|
||||
if matchingTarget && delay <= params.BeaconConfig().SlotsPerEpoch {
|
||||
participatedFlags[targetFlagIndex] = true
|
||||
}
|
||||
matchingHeadTarget := bool(matchingHead) && bool(matchingTarget)
|
||||
if matchingHeadTarget && delay == params.BeaconConfig().MinAttestationInclusionDelay {
|
||||
participatedFlags[headFlagIndex] = true
|
||||
}
|
||||
return participatedFlags, nil
|
||||
}
|
||||
344
beacon-chain/core/altair/attestation_test.go
Normal file
344
beacon-chain/core/altair/attestation_test.go
Normal file
@@ -0,0 +1,344 @@
|
||||
package altair_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
fuzz "github.com/google/gofuzz"
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
altair "github.com/prysmaticlabs/prysm/beacon-chain/core/altair"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
stateAltair "github.com/prysmaticlabs/prysm/beacon-chain/state/v2"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
"github.com/prysmaticlabs/prysm/shared/attestationutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
|
||||
func TestProcessAttestations_InclusionDelayFailure(t *testing.T) {
|
||||
attestations := []*ethpb.Attestation{
|
||||
testutil.HydrateAttestation(ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Target: ðpb.Checkpoint{Epoch: 0, Root: make([]byte, 32)},
|
||||
Slot: 5,
|
||||
},
|
||||
}),
|
||||
}
|
||||
b := testutil.NewBeaconBlockAltair()
|
||||
b.Block = ðpb.BeaconBlockAltair{
|
||||
Body: ðpb.BeaconBlockBodyAltair{
|
||||
Attestations: attestations,
|
||||
},
|
||||
}
|
||||
beaconState, _ := testutil.DeterministicGenesisStateAltair(t, 100)
|
||||
|
||||
want := fmt.Sprintf(
|
||||
"attestation slot %d + inclusion delay %d > state slot %d",
|
||||
attestations[0].Data.Slot,
|
||||
params.BeaconConfig().MinAttestationInclusionDelay,
|
||||
beaconState.Slot(),
|
||||
)
|
||||
wsb, err := wrapper.WrappedAltairSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
_, err = altair.ProcessAttestations(context.Background(), beaconState, wsb)
|
||||
require.ErrorContains(t, want, err)
|
||||
}
|
||||
|
||||
func TestProcessAttestations_NeitherCurrentNorPrevEpoch(t *testing.T) {
|
||||
att := testutil.HydrateAttestation(ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
|
||||
Target: ðpb.Checkpoint{Epoch: 0}}})
|
||||
|
||||
b := testutil.NewBeaconBlockAltair()
|
||||
b.Block = ðpb.BeaconBlockAltair{
|
||||
Body: ðpb.BeaconBlockBodyAltair{
|
||||
Attestations: []*ethpb.Attestation{att},
|
||||
},
|
||||
}
|
||||
beaconState, _ := testutil.DeterministicGenesisStateAltair(t, 100)
|
||||
err := beaconState.SetSlot(beaconState.Slot() + params.BeaconConfig().SlotsPerEpoch*4 + params.BeaconConfig().MinAttestationInclusionDelay)
|
||||
require.NoError(t, err)
|
||||
pfc := beaconState.PreviousJustifiedCheckpoint()
|
||||
pfc.Root = []byte("hello-world")
|
||||
require.NoError(t, beaconState.SetPreviousJustifiedCheckpoint(pfc))
|
||||
|
||||
want := fmt.Sprintf(
|
||||
"expected target epoch (%d) to be the previous epoch (%d) or the current epoch (%d)",
|
||||
att.Data.Target.Epoch,
|
||||
helpers.PrevEpoch(beaconState),
|
||||
helpers.CurrentEpoch(beaconState),
|
||||
)
|
||||
wsb, err := wrapper.WrappedAltairSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
_, err = altair.ProcessAttestations(context.Background(), beaconState, wsb)
|
||||
require.ErrorContains(t, want, err)
|
||||
}
|
||||
|
||||
func TestProcessAttestations_CurrentEpochFFGDataMismatches(t *testing.T) {
|
||||
attestations := []*ethpb.Attestation{
|
||||
{
|
||||
Data: ðpb.AttestationData{
|
||||
Target: ðpb.Checkpoint{Epoch: 0, Root: make([]byte, 32)},
|
||||
Source: ðpb.Checkpoint{Epoch: 1, Root: make([]byte, 32)},
|
||||
},
|
||||
AggregationBits: bitfield.Bitlist{0x09},
|
||||
},
|
||||
}
|
||||
b := testutil.NewBeaconBlockAltair()
|
||||
b.Block = ðpb.BeaconBlockAltair{
|
||||
Body: ðpb.BeaconBlockBodyAltair{
|
||||
Attestations: attestations,
|
||||
},
|
||||
}
|
||||
beaconState, _ := testutil.DeterministicGenesisStateAltair(t, 100)
|
||||
require.NoError(t, beaconState.SetSlot(beaconState.Slot()+params.BeaconConfig().MinAttestationInclusionDelay))
|
||||
cfc := beaconState.CurrentJustifiedCheckpoint()
|
||||
cfc.Root = []byte("hello-world")
|
||||
require.NoError(t, beaconState.SetCurrentJustifiedCheckpoint(cfc))
|
||||
|
||||
want := "source check point not equal to current justified checkpoint"
|
||||
wsb, err := wrapper.WrappedAltairSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
_, err = altair.ProcessAttestations(context.Background(), beaconState, wsb)
|
||||
require.ErrorContains(t, want, err)
|
||||
b.Block.Body.Attestations[0].Data.Source.Epoch = helpers.CurrentEpoch(beaconState)
|
||||
b.Block.Body.Attestations[0].Data.Source.Root = []byte{}
|
||||
wsb, err = wrapper.WrappedAltairSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
_, err = altair.ProcessAttestations(context.Background(), beaconState, wsb)
|
||||
require.ErrorContains(t, want, err)
|
||||
}
|
||||
|
||||
func TestProcessAttestations_PrevEpochFFGDataMismatches(t *testing.T) {
|
||||
beaconState, _ := testutil.DeterministicGenesisStateAltair(t, 100)
|
||||
|
||||
aggBits := bitfield.NewBitlist(3)
|
||||
aggBits.SetBitAt(0, true)
|
||||
attestations := []*ethpb.Attestation{
|
||||
{
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 1, Root: make([]byte, 32)},
|
||||
Target: ðpb.Checkpoint{Epoch: 1, Root: make([]byte, 32)},
|
||||
Slot: params.BeaconConfig().SlotsPerEpoch,
|
||||
},
|
||||
AggregationBits: aggBits,
|
||||
},
|
||||
}
|
||||
b := testutil.NewBeaconBlockAltair()
|
||||
b.Block = ðpb.BeaconBlockAltair{
|
||||
Body: ðpb.BeaconBlockBodyAltair{
|
||||
Attestations: attestations,
|
||||
},
|
||||
}
|
||||
|
||||
err := beaconState.SetSlot(beaconState.Slot() + 2*params.BeaconConfig().SlotsPerEpoch)
|
||||
require.NoError(t, err)
|
||||
pfc := beaconState.PreviousJustifiedCheckpoint()
|
||||
pfc.Root = []byte("hello-world")
|
||||
require.NoError(t, beaconState.SetPreviousJustifiedCheckpoint(pfc))
|
||||
|
||||
want := "source check point not equal to previous justified checkpoint"
|
||||
wsb, err := wrapper.WrappedAltairSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
_, err = altair.ProcessAttestations(context.Background(), beaconState, wsb)
|
||||
require.ErrorContains(t, want, err)
|
||||
b.Block.Body.Attestations[0].Data.Source.Epoch = helpers.PrevEpoch(beaconState)
|
||||
b.Block.Body.Attestations[0].Data.Target.Epoch = helpers.PrevEpoch(beaconState)
|
||||
b.Block.Body.Attestations[0].Data.Source.Root = []byte{}
|
||||
wsb, err = wrapper.WrappedAltairSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
_, err = altair.ProcessAttestations(context.Background(), beaconState, wsb)
|
||||
require.ErrorContains(t, want, err)
|
||||
}
|
||||
|
||||
func TestProcessAttestations_InvalidAggregationBitsLength(t *testing.T) {
|
||||
beaconState, _ := testutil.DeterministicGenesisStateAltair(t, 100)
|
||||
|
||||
aggBits := bitfield.NewBitlist(4)
|
||||
att := ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
|
||||
Target: ðpb.Checkpoint{Epoch: 0}},
|
||||
AggregationBits: aggBits,
|
||||
}
|
||||
|
||||
b := testutil.NewBeaconBlockAltair()
|
||||
b.Block = ðpb.BeaconBlockAltair{
|
||||
Body: ðpb.BeaconBlockBodyAltair{
|
||||
Attestations: []*ethpb.Attestation{att},
|
||||
},
|
||||
}
|
||||
|
||||
err := beaconState.SetSlot(beaconState.Slot() + params.BeaconConfig().MinAttestationInclusionDelay)
|
||||
require.NoError(t, err)
|
||||
|
||||
cfc := beaconState.CurrentJustifiedCheckpoint()
|
||||
cfc.Root = []byte("hello-world")
|
||||
require.NoError(t, beaconState.SetCurrentJustifiedCheckpoint(cfc))
|
||||
|
||||
expected := "failed to verify aggregation bitfield: wanted participants bitfield length 3, got: 4"
|
||||
wsb, err := wrapper.WrappedAltairSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
_, err = altair.ProcessAttestations(context.Background(), beaconState, wsb)
|
||||
require.ErrorContains(t, expected, err)
|
||||
}
|
||||
|
||||
func TestProcessAttestations_OK(t *testing.T) {
|
||||
beaconState, privKeys := testutil.DeterministicGenesisStateAltair(t, 100)
|
||||
|
||||
aggBits := bitfield.NewBitlist(3)
|
||||
aggBits.SetBitAt(0, true)
|
||||
var mockRoot [32]byte
|
||||
copy(mockRoot[:], "hello-world")
|
||||
att := testutil.HydrateAttestation(ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Root: mockRoot[:]},
|
||||
Target: ðpb.Checkpoint{Root: mockRoot[:]},
|
||||
},
|
||||
AggregationBits: aggBits,
|
||||
})
|
||||
|
||||
cfc := beaconState.CurrentJustifiedCheckpoint()
|
||||
cfc.Root = mockRoot[:]
|
||||
require.NoError(t, beaconState.SetCurrentJustifiedCheckpoint(cfc))
|
||||
|
||||
committee, err := helpers.BeaconCommitteeFromState(beaconState, att.Data.Slot, att.Data.CommitteeIndex)
|
||||
require.NoError(t, err)
|
||||
attestingIndices, err := attestationutil.AttestingIndices(att.AggregationBits, committee)
|
||||
require.NoError(t, err)
|
||||
sigs := make([]bls.Signature, len(attestingIndices))
|
||||
for i, indice := range attestingIndices {
|
||||
sb, err := helpers.ComputeDomainAndSign(beaconState, 0, att.Data, params.BeaconConfig().DomainBeaconAttester, privKeys[indice])
|
||||
require.NoError(t, err)
|
||||
sig, err := bls.SignatureFromBytes(sb)
|
||||
require.NoError(t, err)
|
||||
sigs[i] = sig
|
||||
}
|
||||
att.Signature = bls.AggregateSignatures(sigs).Marshal()
|
||||
|
||||
block := testutil.NewBeaconBlockAltair()
|
||||
block.Block.Body.Attestations = []*ethpb.Attestation{att}
|
||||
|
||||
err = beaconState.SetSlot(beaconState.Slot() + params.BeaconConfig().MinAttestationInclusionDelay)
|
||||
require.NoError(t, err)
|
||||
wsb, err := wrapper.WrappedAltairSignedBeaconBlock(block)
|
||||
require.NoError(t, err)
|
||||
_, err = altair.ProcessAttestations(context.Background(), beaconState, wsb)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestProcessAttestationNoVerify_SourceTargetHead(t *testing.T) {
|
||||
beaconState, _ := testutil.DeterministicGenesisStateAltair(t, 64)
|
||||
err := beaconState.SetSlot(beaconState.Slot() + params.BeaconConfig().MinAttestationInclusionDelay)
|
||||
require.NoError(t, err)
|
||||
|
||||
aggBits := bitfield.NewBitlist(2)
|
||||
aggBits.SetBitAt(0, true)
|
||||
aggBits.SetBitAt(1, true)
|
||||
r, err := helpers.BlockRootAtSlot(beaconState, 0)
|
||||
require.NoError(t, err)
|
||||
att := ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
BeaconBlockRoot: r,
|
||||
Source: ðpb.Checkpoint{Epoch: 0, Root: make([]byte, 32)},
|
||||
Target: ðpb.Checkpoint{Epoch: 0, Root: make([]byte, 32)},
|
||||
},
|
||||
AggregationBits: aggBits,
|
||||
}
|
||||
zeroSig := [96]byte{}
|
||||
att.Signature = zeroSig[:]
|
||||
|
||||
ckp := beaconState.CurrentJustifiedCheckpoint()
|
||||
copy(ckp.Root, make([]byte, 32))
|
||||
require.NoError(t, beaconState.SetCurrentJustifiedCheckpoint(ckp))
|
||||
|
||||
beaconState, err = altair.ProcessAttestationNoVerifySignature(context.Background(), beaconState, att)
|
||||
require.NoError(t, err)
|
||||
|
||||
p, err := beaconState.CurrentEpochParticipation()
|
||||
require.NoError(t, err)
|
||||
|
||||
committee, err := helpers.BeaconCommitteeFromState(beaconState, att.Data.Slot, att.Data.CommitteeIndex)
|
||||
require.NoError(t, err)
|
||||
indices, err := attestationutil.AttestingIndices(att.AggregationBits, committee)
|
||||
require.NoError(t, err)
|
||||
for _, index := range indices {
|
||||
require.Equal(t, true, altair.HasValidatorFlag(p[index], params.BeaconConfig().TimelyHeadFlagIndex))
|
||||
require.Equal(t, true, altair.HasValidatorFlag(p[index], params.BeaconConfig().TimelyTargetFlagIndex))
|
||||
require.Equal(t, true, altair.HasValidatorFlag(p[index], params.BeaconConfig().TimelySourceFlagIndex))
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatorFlag_AddHas(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
set []uint8
|
||||
expectedTrue []uint8
|
||||
expectedFalse []uint8
|
||||
}{
|
||||
{name: "none",
|
||||
set: []uint8{},
|
||||
expectedTrue: []uint8{},
|
||||
expectedFalse: []uint8{params.BeaconConfig().TimelySourceFlagIndex, params.BeaconConfig().TimelyTargetFlagIndex, params.BeaconConfig().TimelyHeadFlagIndex},
|
||||
},
|
||||
{
|
||||
name: "source",
|
||||
set: []uint8{params.BeaconConfig().TimelySourceFlagIndex},
|
||||
expectedTrue: []uint8{params.BeaconConfig().TimelySourceFlagIndex},
|
||||
expectedFalse: []uint8{params.BeaconConfig().TimelyTargetFlagIndex, params.BeaconConfig().TimelyHeadFlagIndex},
|
||||
},
|
||||
{
|
||||
name: "source, target",
|
||||
set: []uint8{params.BeaconConfig().TimelySourceFlagIndex, params.BeaconConfig().TimelyTargetFlagIndex},
|
||||
expectedTrue: []uint8{params.BeaconConfig().TimelySourceFlagIndex, params.BeaconConfig().TimelyTargetFlagIndex},
|
||||
expectedFalse: []uint8{params.BeaconConfig().TimelyHeadFlagIndex},
|
||||
},
|
||||
{name: "source, target, head",
|
||||
set: []uint8{params.BeaconConfig().TimelySourceFlagIndex, params.BeaconConfig().TimelyTargetFlagIndex, params.BeaconConfig().TimelyHeadFlagIndex},
|
||||
expectedTrue: []uint8{params.BeaconConfig().TimelySourceFlagIndex, params.BeaconConfig().TimelyTargetFlagIndex, params.BeaconConfig().TimelyHeadFlagIndex},
|
||||
expectedFalse: []uint8{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
b := uint8(0)
|
||||
for _, f := range tt.set {
|
||||
b = altair.AddValidatorFlag(b, f)
|
||||
}
|
||||
for _, f := range tt.expectedFalse {
|
||||
require.Equal(t, false, altair.HasValidatorFlag(b, f))
|
||||
}
|
||||
for _, f := range tt.expectedTrue {
|
||||
require.Equal(t, true, altair.HasValidatorFlag(b, f))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFuzzProcessAttestationsNoVerify_10000(t *testing.T) {
|
||||
fuzzer := fuzz.NewWithSeed(0)
|
||||
state := ðpb.BeaconStateAltair{}
|
||||
b := ðpb.SignedBeaconBlockAltair{Block: ðpb.BeaconBlockAltair{}}
|
||||
ctx := context.Background()
|
||||
for i := 0; i < 10000; i++ {
|
||||
fuzzer.Fuzz(state)
|
||||
fuzzer.Fuzz(b)
|
||||
if b.Block == nil {
|
||||
b.Block = ðpb.BeaconBlockAltair{}
|
||||
}
|
||||
s, err := stateAltair.InitializeFromProtoUnsafe(state)
|
||||
require.NoError(t, err)
|
||||
wsb, err := wrapper.WrappedAltairSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
r, err := altair.ProcessAttestationsNoVerifySignature(ctx, s, wsb)
|
||||
if err != nil && r != nil {
|
||||
t.Fatalf("return value should be nil on err. found: %v on error: %v for state: %v and block: %v", r, err, state, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
130
beacon-chain/core/altair/block.go
Normal file
130
beacon-chain/core/altair/block.go
Normal file
@@ -0,0 +1,130 @@
|
||||
package altair
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
p2pType "github.com/prysmaticlabs/prysm/beacon-chain/p2p/types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
// ProcessSyncAggregate verifies sync committee aggregate signature signing over the previous slot block root.
|
||||
//
|
||||
// Spec code:
|
||||
// def process_sync_aggregate(state: BeaconState, sync_aggregate: SyncAggregate) -> None:
|
||||
// # Verify sync committee aggregate signature signing over the previous slot block root
|
||||
// committee_pubkeys = state.current_sync_committee.pubkeys
|
||||
// participant_pubkeys = [pubkey for pubkey, bit in zip(committee_pubkeys, sync_aggregate.sync_committee_bits) if bit]
|
||||
// previous_slot = max(state.slot, Slot(1)) - Slot(1)
|
||||
// domain = get_domain(state, DOMAIN_SYNC_COMMITTEE, compute_epoch_at_slot(previous_slot))
|
||||
// signing_root = compute_signing_root(get_block_root_at_slot(state, previous_slot), domain)
|
||||
// assert eth2_fast_aggregate_verify(participant_pubkeys, signing_root, sync_aggregate.sync_committee_signature)
|
||||
//
|
||||
// # Compute participant and proposer rewards
|
||||
// total_active_increments = get_total_active_balance(state) // EFFECTIVE_BALANCE_INCREMENT
|
||||
// total_base_rewards = Gwei(get_base_reward_per_increment(state) * total_active_increments)
|
||||
// max_participant_rewards = Gwei(total_base_rewards * SYNC_REWARD_WEIGHT // WEIGHT_DENOMINATOR // SLOTS_PER_EPOCH)
|
||||
// participant_reward = Gwei(max_participant_rewards // SYNC_COMMITTEE_SIZE)
|
||||
// proposer_reward = Gwei(participant_reward * PROPOSER_WEIGHT // (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT))
|
||||
//
|
||||
// # Apply participant and proposer rewards
|
||||
// all_pubkeys = [v.pubkey for v in state.validators]
|
||||
// committee_indices = [ValidatorIndex(all_pubkeys.index(pubkey)) for pubkey in state.current_sync_committee.pubkeys]
|
||||
// for participant_index, participation_bit in zip(committee_indices, sync_aggregate.sync_committee_bits):
|
||||
// if participation_bit:
|
||||
// increase_balance(state, participant_index, participant_reward)
|
||||
// increase_balance(state, get_beacon_proposer_index(state), proposer_reward)
|
||||
// else:
|
||||
// decrease_balance(state, participant_index, participant_reward)
|
||||
func ProcessSyncAggregate(state state.BeaconStateAltair, sync *ethpb.SyncAggregate) (state.BeaconStateAltair, error) {
|
||||
currentSyncCommittee, err := state.CurrentSyncCommittee()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
committeeKeys := currentSyncCommittee.Pubkeys
|
||||
votedKeys := make([]bls.PublicKey, 0, len(committeeKeys))
|
||||
votedIndices := make([]types.ValidatorIndex, 0, len(committeeKeys))
|
||||
didntVoteIndices := make([]types.ValidatorIndex, 0, len(committeeKeys))
|
||||
// Verify sync committee signature.
|
||||
for i := uint64(0); i < sync.SyncCommitteeBits.Len(); i++ {
|
||||
vIdx, exists := state.ValidatorIndexByPubkey(bytesutil.ToBytes48(committeeKeys[i]))
|
||||
// Impossible scenario.
|
||||
if !exists {
|
||||
return nil, errors.New("validator public key does not exist in state")
|
||||
}
|
||||
|
||||
if sync.SyncCommitteeBits.BitAt(i) {
|
||||
pubKey, err := bls.PublicKeyFromBytes(committeeKeys[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
votedKeys = append(votedKeys, pubKey)
|
||||
votedIndices = append(votedIndices, vIdx)
|
||||
} else {
|
||||
didntVoteIndices = append(didntVoteIndices, vIdx)
|
||||
}
|
||||
}
|
||||
ps := helpers.PrevSlot(state.Slot())
|
||||
d, err := helpers.Domain(state.Fork(), helpers.SlotToEpoch(ps), params.BeaconConfig().DomainSyncCommittee, state.GenesisValidatorRoot())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pbr, err := helpers.BlockRootAtSlot(state, ps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sszBytes := p2pType.SSZBytes(pbr)
|
||||
r, err := helpers.ComputeSigningRoot(&sszBytes, d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sig, err := bls.SignatureFromBytes(sync.SyncCommitteeSignature)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !sig.Eth2FastAggregateVerify(votedKeys, r) {
|
||||
return nil, errors.New("could not verify sync committee signature")
|
||||
}
|
||||
|
||||
// Calculate sync committee and proposer rewards
|
||||
activeBalance, err := helpers.TotalActiveBalance(state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
totalActiveIncrements := activeBalance / params.BeaconConfig().EffectiveBalanceIncrement
|
||||
totalBaseRewards := baseRewardPerIncrement(activeBalance) * totalActiveIncrements
|
||||
maxParticipantRewards := totalBaseRewards * params.BeaconConfig().SyncRewardWeight / params.BeaconConfig().WeightDenominator / uint64(params.BeaconConfig().SlotsPerEpoch)
|
||||
participantReward := maxParticipantRewards / params.BeaconConfig().SyncCommitteeSize
|
||||
proposerReward := participantReward * params.BeaconConfig().ProposerWeight / (params.BeaconConfig().WeightDenominator - params.BeaconConfig().ProposerWeight)
|
||||
|
||||
// Apply sync committee rewards.
|
||||
earnedProposerReward := uint64(0)
|
||||
for _, index := range votedIndices {
|
||||
if err := helpers.IncreaseBalance(state, index, participantReward); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
earnedProposerReward += proposerReward
|
||||
}
|
||||
// Apply proposer rewards.
|
||||
proposerIndex, err := helpers.BeaconProposerIndex(state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := helpers.IncreaseBalance(state, proposerIndex, earnedProposerReward); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Apply sync committee penalties.
|
||||
for _, index := range didntVoteIndices {
|
||||
if err := helpers.DecreaseBalance(state, index, participantReward); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return state, nil
|
||||
}
|
||||
91
beacon-chain/core/altair/block_test.go
Normal file
91
beacon-chain/core/altair/block_test.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package altair_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/altair"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
p2pType "github.com/prysmaticlabs/prysm/beacon-chain/p2p/types"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
|
||||
func TestProcessSyncCommittee_OK(t *testing.T) {
|
||||
beaconState, privKeys := testutil.DeterministicGenesisStateAltair(t, params.BeaconConfig().MaxValidatorsPerCommittee)
|
||||
require.NoError(t, beaconState.SetSlot(1))
|
||||
committee, err := altair.NextSyncCommittee(context.Background(), beaconState)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconState.SetCurrentSyncCommittee(committee))
|
||||
|
||||
syncBits := bitfield.NewBitvector512()
|
||||
for i := range syncBits {
|
||||
syncBits[i] = 0xff
|
||||
}
|
||||
indices, err := altair.NextSyncCommitteeIndices(context.Background(), beaconState)
|
||||
require.NoError(t, err)
|
||||
ps := helpers.PrevSlot(beaconState.Slot())
|
||||
pbr, err := helpers.BlockRootAtSlot(beaconState, ps)
|
||||
require.NoError(t, err)
|
||||
sigs := make([]bls.Signature, len(indices))
|
||||
for i, indice := range indices {
|
||||
b := p2pType.SSZBytes(pbr)
|
||||
sb, err := helpers.ComputeDomainAndSign(beaconState, helpers.CurrentEpoch(beaconState), &b, params.BeaconConfig().DomainSyncCommittee, privKeys[indice])
|
||||
require.NoError(t, err)
|
||||
sig, err := bls.SignatureFromBytes(sb)
|
||||
require.NoError(t, err)
|
||||
sigs[i] = sig
|
||||
}
|
||||
aggregatedSig := bls.AggregateSignatures(sigs).Marshal()
|
||||
syncAggregate := ðpb.SyncAggregate{
|
||||
SyncCommitteeBits: syncBits,
|
||||
SyncCommitteeSignature: aggregatedSig,
|
||||
}
|
||||
|
||||
beaconState, err = altair.ProcessSyncAggregate(beaconState, syncAggregate)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Use a non-sync committee index to compare profitability.
|
||||
syncCommittee := make(map[types.ValidatorIndex]bool)
|
||||
for _, index := range indices {
|
||||
syncCommittee[index] = true
|
||||
}
|
||||
nonSyncIndex := types.ValidatorIndex(params.BeaconConfig().MaxValidatorsPerCommittee + 1)
|
||||
for i := types.ValidatorIndex(0); uint64(i) < params.BeaconConfig().MaxValidatorsPerCommittee; i++ {
|
||||
if !syncCommittee[i] {
|
||||
nonSyncIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Sync committee should be more profitable than non sync committee
|
||||
balances := beaconState.Balances()
|
||||
require.Equal(t, true, balances[indices[0]] > balances[nonSyncIndex])
|
||||
|
||||
// Proposer should be more profitable than rest of the sync committee
|
||||
proposerIndex, err := helpers.BeaconProposerIndex(beaconState)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, balances[proposerIndex] > balances[indices[0]])
|
||||
|
||||
// Sync committee should have the same profits, except you are a proposer
|
||||
for i := 1; i < len(indices); i++ {
|
||||
if proposerIndex == indices[i-1] || proposerIndex == indices[i] {
|
||||
continue
|
||||
}
|
||||
require.Equal(t, balances[indices[i-1]], balances[indices[i]])
|
||||
}
|
||||
|
||||
// Increased balance validator count should equal to sync committee count
|
||||
increased := uint64(0)
|
||||
for _, balance := range balances {
|
||||
if balance > params.BeaconConfig().MaxEffectiveBalance {
|
||||
increased++
|
||||
}
|
||||
}
|
||||
require.Equal(t, params.BeaconConfig().SyncCommitteeSize, increased)
|
||||
}
|
||||
@@ -8,13 +8,12 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/altair"
|
||||
stateAltair "github.com/prysmaticlabs/prysm/beacon-chain/state/v2"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
statepb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
|
||||
func TestFuzzProcessDeposits_10000(t *testing.T) {
|
||||
fuzzer := fuzz.NewWithSeed(0)
|
||||
state := &statepb.BeaconStateAltair{}
|
||||
state := ðpb.BeaconStateAltair{}
|
||||
deposits := make([]*ethpb.Deposit, 100)
|
||||
ctx := context.Background()
|
||||
for i := 0; i < 10000; i++ {
|
||||
@@ -33,7 +32,7 @@ func TestFuzzProcessDeposits_10000(t *testing.T) {
|
||||
|
||||
func TestFuzzProcessDeposit_10000(t *testing.T) {
|
||||
fuzzer := fuzz.NewWithSeed(0)
|
||||
state := &statepb.BeaconStateAltair{}
|
||||
state := ðpb.BeaconStateAltair{}
|
||||
deposit := ðpb.Deposit{}
|
||||
|
||||
for i := 0; i < 10000; i++ {
|
||||
|
||||
257
beacon-chain/core/altair/epoch_precompute.go
Normal file
257
beacon-chain/core/altair/epoch_precompute.go
Normal file
@@ -0,0 +1,257 @@
|
||||
package altair
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch/precompute"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/shared/mathutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
// InitializeEpochValidators gets called at the beginning of process epoch cycle to return
|
||||
// pre computed instances of validators attesting records and total
|
||||
// balances attested in an epoch.
|
||||
func InitializeEpochValidators(ctx context.Context, st state.BeaconStateAltair) ([]*precompute.Validator, *precompute.Balance, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "altair.InitializeEpochValidators")
|
||||
defer span.End()
|
||||
pValidators := make([]*precompute.Validator, st.NumValidators())
|
||||
bal := &precompute.Balance{}
|
||||
prevEpoch := helpers.PrevEpoch(st)
|
||||
|
||||
inactivityScores, err := st.InactivityScores()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// This shouldn't happen with a correct beacon state,
|
||||
// but rather be safe to defend against index out of bound panics.
|
||||
if st.NumValidators() > len(inactivityScores) {
|
||||
return nil, nil, errors.New("num of validators can't be greater than length of inactivity scores")
|
||||
}
|
||||
if err := st.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error {
|
||||
// Was validator withdrawable or slashed
|
||||
withdrawable := prevEpoch+1 >= val.WithdrawableEpoch()
|
||||
pVal := &precompute.Validator{
|
||||
IsSlashed: val.Slashed(),
|
||||
IsWithdrawableCurrentEpoch: withdrawable,
|
||||
CurrentEpochEffectiveBalance: val.EffectiveBalance(),
|
||||
InactivityScore: inactivityScores[idx],
|
||||
}
|
||||
// Validator active current epoch
|
||||
if helpers.IsActiveValidatorUsingTrie(val, helpers.CurrentEpoch(st)) {
|
||||
pVal.IsActiveCurrentEpoch = true
|
||||
bal.ActiveCurrentEpoch += val.EffectiveBalance()
|
||||
}
|
||||
// Validator active previous epoch
|
||||
if helpers.IsActiveValidatorUsingTrie(val, prevEpoch) {
|
||||
pVal.IsActivePrevEpoch = true
|
||||
bal.ActivePrevEpoch += val.EffectiveBalance()
|
||||
}
|
||||
|
||||
pValidators[idx] = pVal
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not initialize epoch validator")
|
||||
}
|
||||
return pValidators, bal, nil
|
||||
}
|
||||
|
||||
// ProcessInactivityScores of beacon chain. This updates inactivity scores of beacon chain and
|
||||
// updates the precompute validator struct for later processing.
|
||||
func ProcessInactivityScores(
|
||||
ctx context.Context,
|
||||
state state.BeaconState,
|
||||
vals []*precompute.Validator,
|
||||
) (state.BeaconState, []*precompute.Validator, error) {
|
||||
if helpers.CurrentEpoch(state) == params.BeaconConfig().GenesisEpoch {
|
||||
return state, vals, nil
|
||||
}
|
||||
|
||||
inactivityScores, err := state.InactivityScores()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for i, v := range vals {
|
||||
if v.IsPrevEpochTargetAttester && !v.IsSlashed {
|
||||
// Decrease inactivity score when validator gets target correct.
|
||||
if v.InactivityScore > 0 {
|
||||
score := uint64(1)
|
||||
if score > v.InactivityScore {
|
||||
score = v.InactivityScore
|
||||
}
|
||||
v.InactivityScore -= score
|
||||
}
|
||||
} else {
|
||||
v.InactivityScore += params.BeaconConfig().InactivityScoreBias
|
||||
}
|
||||
if !helpers.IsInInactivityLeak(helpers.PrevEpoch(state), state.FinalizedCheckpointEpoch()) {
|
||||
score := params.BeaconConfig().InactivityScoreRecoveryRate
|
||||
if score > v.InactivityScore {
|
||||
score = v.InactivityScore
|
||||
}
|
||||
v.InactivityScore -= score
|
||||
|
||||
}
|
||||
inactivityScores[i] = v.InactivityScore
|
||||
}
|
||||
|
||||
if err := state.SetInactivityScores(inactivityScores); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return state, vals, nil
|
||||
}
|
||||
|
||||
// ProcessEpochParticipation processes the epoch participation in state and updates individual validator's pre computes,
|
||||
// it also tracks and updates epoch attesting balances.
|
||||
func ProcessEpochParticipation(
|
||||
ctx context.Context,
|
||||
state state.BeaconState,
|
||||
bal *precompute.Balance,
|
||||
vals []*precompute.Validator,
|
||||
) ([]*precompute.Validator, *precompute.Balance, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "altair.ProcessEpochParticipation")
|
||||
defer span.End()
|
||||
|
||||
cp, err := state.CurrentEpochParticipation()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
for i, b := range cp {
|
||||
if HasValidatorFlag(b, params.BeaconConfig().TimelyTargetFlagIndex) {
|
||||
vals[i].IsCurrentEpochTargetAttester = true
|
||||
}
|
||||
}
|
||||
pp, err := state.PreviousEpochParticipation()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
for i, b := range pp {
|
||||
if HasValidatorFlag(b, params.BeaconConfig().TimelySourceFlagIndex) {
|
||||
vals[i].IsPrevEpochAttester = true
|
||||
}
|
||||
if HasValidatorFlag(b, params.BeaconConfig().TimelyTargetFlagIndex) {
|
||||
vals[i].IsPrevEpochTargetAttester = true
|
||||
}
|
||||
if HasValidatorFlag(b, params.BeaconConfig().TimelyHeadFlagIndex) {
|
||||
vals[i].IsPrevEpochHeadAttester = true
|
||||
}
|
||||
}
|
||||
bal = precompute.UpdateBalance(vals, bal)
|
||||
return vals, bal, nil
|
||||
}
|
||||
|
||||
// ProcessRewardsAndPenaltiesPrecompute processes the rewards and penalties of individual validator.
|
||||
// This is an optimized version by passing in precomputed validator attesting records and and total epoch balances.
|
||||
func ProcessRewardsAndPenaltiesPrecompute(
|
||||
state state.BeaconStateAltair,
|
||||
bal *precompute.Balance,
|
||||
vals []*precompute.Validator,
|
||||
) (state.BeaconStateAltair, error) {
|
||||
// Don't process rewards and penalties in genesis epoch.
|
||||
if helpers.CurrentEpoch(state) == 0 {
|
||||
return state, nil
|
||||
}
|
||||
|
||||
numOfVals := state.NumValidators()
|
||||
// Guard against an out-of-bounds using validator balance precompute.
|
||||
if len(vals) != numOfVals || len(vals) != state.BalancesLength() {
|
||||
return state, errors.New("validator registries not the same length as state's validator registries")
|
||||
}
|
||||
|
||||
attsRewards, attsPenalties, err := AttestationsDelta(state, bal, vals)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get attestation delta")
|
||||
}
|
||||
|
||||
balances := state.Balances()
|
||||
for i := 0; i < numOfVals; i++ {
|
||||
vals[i].BeforeEpochTransitionBalance = balances[i]
|
||||
|
||||
// Compute the post balance of the validator after accounting for the
|
||||
// attester and proposer rewards and penalties.
|
||||
balances[i] = helpers.IncreaseBalanceWithVal(balances[i], attsRewards[i])
|
||||
balances[i] = helpers.DecreaseBalanceWithVal(balances[i], attsPenalties[i])
|
||||
|
||||
vals[i].AfterEpochTransitionBalance = balances[i]
|
||||
}
|
||||
|
||||
if err := state.SetBalances(balances); err != nil {
|
||||
return nil, errors.Wrap(err, "could not set validator balances")
|
||||
}
|
||||
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// AttestationsDelta computes and returns the rewards and penalties differences for individual validators based on the
|
||||
// voting records.
|
||||
func AttestationsDelta(state state.BeaconStateAltair, bal *precompute.Balance, vals []*precompute.Validator) (rewards, penalties []uint64, err error) {
|
||||
numOfVals := state.NumValidators()
|
||||
rewards = make([]uint64, numOfVals)
|
||||
penalties = make([]uint64, numOfVals)
|
||||
prevEpoch := helpers.PrevEpoch(state)
|
||||
finalizedEpoch := state.FinalizedCheckpointEpoch()
|
||||
|
||||
for i, v := range vals {
|
||||
rewards[i], penalties[i] = attestationDelta(bal, v, prevEpoch, finalizedEpoch)
|
||||
}
|
||||
|
||||
return rewards, penalties, nil
|
||||
}
|
||||
|
||||
func attestationDelta(bal *precompute.Balance, v *precompute.Validator, prevEpoch, finalizedEpoch types.Epoch) (r, p uint64) {
|
||||
eligible := v.IsActivePrevEpoch || (v.IsSlashed && !v.IsWithdrawableCurrentEpoch)
|
||||
if !eligible || bal.ActiveCurrentEpoch == 0 {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
ebi := params.BeaconConfig().EffectiveBalanceIncrement
|
||||
eb := v.CurrentEpochEffectiveBalance
|
||||
br := (eb / ebi) * (ebi * params.BeaconConfig().BaseRewardFactor / mathutil.IntegerSquareRoot(bal.ActiveCurrentEpoch))
|
||||
activeCurrentEpochIncrements := bal.ActiveCurrentEpoch / ebi
|
||||
|
||||
r, p = uint64(0), uint64(0)
|
||||
// Process source reward / penalty
|
||||
if v.IsPrevEpochAttester && !v.IsSlashed {
|
||||
if !helpers.IsInInactivityLeak(prevEpoch, finalizedEpoch) {
|
||||
rewardNumerator := br * params.BeaconConfig().TimelySourceWeight * (bal.PrevEpochAttested / ebi)
|
||||
r += rewardNumerator / (activeCurrentEpochIncrements * params.BeaconConfig().WeightDenominator)
|
||||
}
|
||||
} else {
|
||||
p += br * params.BeaconConfig().TimelySourceWeight / params.BeaconConfig().WeightDenominator
|
||||
}
|
||||
|
||||
// Process target reward / penalty
|
||||
if v.IsPrevEpochTargetAttester && !v.IsSlashed {
|
||||
if !helpers.IsInInactivityLeak(prevEpoch, finalizedEpoch) {
|
||||
rewardNumerator := br * params.BeaconConfig().TimelyTargetWeight * (bal.PrevEpochTargetAttested / ebi)
|
||||
r += rewardNumerator / (activeCurrentEpochIncrements * params.BeaconConfig().WeightDenominator)
|
||||
}
|
||||
} else {
|
||||
p += br * params.BeaconConfig().TimelyTargetWeight / params.BeaconConfig().WeightDenominator
|
||||
}
|
||||
|
||||
// Process head reward / penalty
|
||||
if v.IsPrevEpochHeadAttester && !v.IsSlashed {
|
||||
if !helpers.IsInInactivityLeak(prevEpoch, finalizedEpoch) {
|
||||
rewardNumerator := br * params.BeaconConfig().TimelyHeadWeight * (bal.PrevEpochHeadAttested / ebi)
|
||||
r += rewardNumerator / (activeCurrentEpochIncrements * params.BeaconConfig().WeightDenominator)
|
||||
}
|
||||
}
|
||||
|
||||
// Process finality delay penalty
|
||||
// Apply an additional penalty to validators that did not vote on the correct target or slashed.
|
||||
if !v.IsPrevEpochTargetAttester || v.IsSlashed {
|
||||
penaltyNumerator := eb * v.InactivityScore
|
||||
penaltyDenominator := params.BeaconConfig().InactivityScoreBias * params.BeaconConfig().InactivityPenaltyQuotientAltair
|
||||
p += penaltyNumerator / penaltyDenominator
|
||||
}
|
||||
|
||||
return r, p
|
||||
}
|
||||
283
beacon-chain/core/altair/epoch_precompute_test.go
Normal file
283
beacon-chain/core/altair/epoch_precompute_test.go
Normal file
@@ -0,0 +1,283 @@
|
||||
package altair
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch/precompute"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
stateAltair "github.com/prysmaticlabs/prysm/beacon-chain/state/v2"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
|
||||
func TestInitializeEpochValidators_Ok(t *testing.T) {
|
||||
ffe := params.BeaconConfig().FarFutureEpoch
|
||||
s, err := stateAltair.InitializeFromProto(ðpb.BeaconStateAltair{
|
||||
Slot: params.BeaconConfig().SlotsPerEpoch,
|
||||
// Validator 0 is slashed
|
||||
// Validator 1 is withdrawable
|
||||
// Validator 2 is active prev epoch and current epoch
|
||||
// Validator 3 is active prev epoch
|
||||
Validators: []*ethpb.Validator{
|
||||
{Slashed: true, WithdrawableEpoch: ffe, EffectiveBalance: 100},
|
||||
{EffectiveBalance: 100},
|
||||
{WithdrawableEpoch: ffe, ExitEpoch: ffe, EffectiveBalance: 100},
|
||||
{WithdrawableEpoch: ffe, ExitEpoch: 1, EffectiveBalance: 100},
|
||||
},
|
||||
InactivityScores: []uint64{0, 1, 2, 3},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
v, b, err := InitializeEpochValidators(context.Background(), s)
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, &precompute.Validator{
|
||||
IsSlashed: true,
|
||||
CurrentEpochEffectiveBalance: 100,
|
||||
InactivityScore: 0,
|
||||
}, v[0], "Incorrect validator 0 status")
|
||||
assert.DeepEqual(t, &precompute.Validator{
|
||||
IsWithdrawableCurrentEpoch: true,
|
||||
CurrentEpochEffectiveBalance: 100,
|
||||
InactivityScore: 1,
|
||||
}, v[1], "Incorrect validator 1 status")
|
||||
assert.DeepEqual(t, &precompute.Validator{
|
||||
IsActivePrevEpoch: true,
|
||||
IsActiveCurrentEpoch: true,
|
||||
CurrentEpochEffectiveBalance: 100,
|
||||
InactivityScore: 2,
|
||||
}, v[2], "Incorrect validator 2 status")
|
||||
assert.DeepEqual(t, &precompute.Validator{
|
||||
IsActivePrevEpoch: true,
|
||||
CurrentEpochEffectiveBalance: 100,
|
||||
InactivityScore: 3,
|
||||
}, v[3], "Incorrect validator 3 status")
|
||||
|
||||
wantedBalances := &precompute.Balance{
|
||||
ActiveCurrentEpoch: 100,
|
||||
ActivePrevEpoch: 200,
|
||||
}
|
||||
assert.DeepEqual(t, wantedBalances, b, "Incorrect wanted balance")
|
||||
}
|
||||
|
||||
func TestInitializeEpochValidators_BadState(t *testing.T) {
|
||||
s, err := stateAltair.InitializeFromProto(ðpb.BeaconStateAltair{
|
||||
Validators: []*ethpb.Validator{{}},
|
||||
InactivityScores: []uint64{},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, _, err = InitializeEpochValidators(context.Background(), s)
|
||||
require.ErrorContains(t, "num of validators can't be greater than length of inactivity scores", err)
|
||||
}
|
||||
|
||||
func TestProcessEpochParticipation(t *testing.T) {
|
||||
s, err := testState()
|
||||
require.NoError(t, err)
|
||||
validators, balance, err := InitializeEpochValidators(context.Background(), s)
|
||||
require.NoError(t, err)
|
||||
validators, balance, err = ProcessEpochParticipation(context.Background(), s, balance, validators)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, &precompute.Validator{
|
||||
IsActiveCurrentEpoch: true,
|
||||
IsActivePrevEpoch: true,
|
||||
IsWithdrawableCurrentEpoch: true,
|
||||
CurrentEpochEffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
||||
}, validators[0])
|
||||
require.DeepEqual(t, &precompute.Validator{
|
||||
IsActiveCurrentEpoch: true,
|
||||
IsActivePrevEpoch: true,
|
||||
IsWithdrawableCurrentEpoch: true,
|
||||
CurrentEpochEffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
||||
IsPrevEpochAttester: true,
|
||||
}, validators[1])
|
||||
require.DeepEqual(t, &precompute.Validator{
|
||||
IsActiveCurrentEpoch: true,
|
||||
IsActivePrevEpoch: true,
|
||||
IsWithdrawableCurrentEpoch: true,
|
||||
CurrentEpochEffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
||||
IsPrevEpochAttester: true,
|
||||
IsCurrentEpochTargetAttester: true,
|
||||
IsPrevEpochTargetAttester: true,
|
||||
}, validators[2])
|
||||
require.DeepEqual(t, &precompute.Validator{
|
||||
IsActiveCurrentEpoch: true,
|
||||
IsActivePrevEpoch: true,
|
||||
IsWithdrawableCurrentEpoch: true,
|
||||
CurrentEpochEffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
||||
IsPrevEpochAttester: true,
|
||||
IsCurrentEpochTargetAttester: true,
|
||||
IsPrevEpochTargetAttester: true,
|
||||
IsPrevEpochHeadAttester: true,
|
||||
}, validators[3])
|
||||
require.Equal(t, params.BeaconConfig().MaxEffectiveBalance*3, balance.PrevEpochAttested)
|
||||
require.Equal(t, balance.CurrentEpochTargetAttested, params.BeaconConfig().MaxEffectiveBalance*2)
|
||||
require.Equal(t, balance.PrevEpochTargetAttested, params.BeaconConfig().MaxEffectiveBalance*2)
|
||||
require.Equal(t, balance.PrevEpochHeadAttested, params.BeaconConfig().MaxEffectiveBalance*1)
|
||||
}
|
||||
|
||||
func TestAttestationsDelta(t *testing.T) {
|
||||
s, err := testState()
|
||||
require.NoError(t, err)
|
||||
validators, balance, err := InitializeEpochValidators(context.Background(), s)
|
||||
require.NoError(t, err)
|
||||
validators, balance, err = ProcessEpochParticipation(context.Background(), s, balance, validators)
|
||||
require.NoError(t, err)
|
||||
rewards, penalties, err := AttestationsDelta(s, balance, validators)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Reward amount should increase as validator index increases due to setup.
|
||||
for i := 1; i < len(rewards); i++ {
|
||||
require.Equal(t, true, rewards[i] > rewards[i-1])
|
||||
}
|
||||
|
||||
// Penalty amount should decrease as validator index increases due to setup.
|
||||
for i := 1; i < len(penalties); i++ {
|
||||
require.Equal(t, true, penalties[i] <= penalties[i-1])
|
||||
}
|
||||
|
||||
// First index should have 0 reward.
|
||||
require.Equal(t, uint64(0), rewards[0])
|
||||
// Last index should have 0 penalty.
|
||||
require.Equal(t, uint64(0), penalties[len(penalties)-1])
|
||||
}
|
||||
|
||||
func TestProcessRewardsAndPenaltiesPrecompute_Ok(t *testing.T) {
|
||||
s, err := testState()
|
||||
require.NoError(t, err)
|
||||
validators, balance, err := InitializeEpochValidators(context.Background(), s)
|
||||
require.NoError(t, err)
|
||||
validators, balance, err = ProcessEpochParticipation(context.Background(), s, balance, validators)
|
||||
require.NoError(t, err)
|
||||
s, err = ProcessRewardsAndPenaltiesPrecompute(s, balance, validators)
|
||||
require.NoError(t, err)
|
||||
|
||||
balances := s.Balances()
|
||||
// Reward amount should increase as validator index increases due to setup.
|
||||
for i := 1; i < len(balances); i++ {
|
||||
require.Equal(t, true, balances[i] >= balances[i-1])
|
||||
}
|
||||
|
||||
wanted := make([]uint64, s.NumValidators())
|
||||
rewards, penalties, err := AttestationsDelta(s, balance, validators)
|
||||
require.NoError(t, err)
|
||||
for i := range rewards {
|
||||
wanted[i] += rewards[i]
|
||||
}
|
||||
for i := range penalties {
|
||||
if wanted[i] > penalties[i] {
|
||||
wanted[i] -= penalties[i]
|
||||
} else {
|
||||
wanted[i] = 0
|
||||
}
|
||||
}
|
||||
require.DeepEqual(t, wanted, balances)
|
||||
}
|
||||
|
||||
func TestProcessRewardsAndPenaltiesPrecompute_InactivityLeak(t *testing.T) {
|
||||
s, err := testState()
|
||||
require.NoError(t, err)
|
||||
validators, balance, err := InitializeEpochValidators(context.Background(), s)
|
||||
require.NoError(t, err)
|
||||
validators, balance, err = ProcessEpochParticipation(context.Background(), s, balance, validators)
|
||||
require.NoError(t, err)
|
||||
sCopy := s.Copy()
|
||||
s, err = ProcessRewardsAndPenaltiesPrecompute(s, balance, validators)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Copied state where finality happened long ago
|
||||
require.NoError(t, sCopy.SetSlot(params.BeaconConfig().SlotsPerEpoch*1000))
|
||||
sCopy, err = ProcessRewardsAndPenaltiesPrecompute(sCopy, balance, validators)
|
||||
require.NoError(t, err)
|
||||
|
||||
balances := s.Balances()
|
||||
inactivityBalances := sCopy.Balances()
|
||||
// Balances should be much less in inactivity leak cases.
|
||||
for i := 0; i < len(balances); i++ {
|
||||
require.Equal(t, true, balances[i] >= inactivityBalances[i])
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessInactivityScores_CanProcess(t *testing.T) {
|
||||
s, err := testState()
|
||||
require.NoError(t, err)
|
||||
defaultScore := uint64(5)
|
||||
require.NoError(t, s.SetInactivityScores([]uint64{defaultScore, defaultScore, defaultScore, defaultScore}))
|
||||
require.NoError(t, s.SetSlot(params.BeaconConfig().SlotsPerEpoch*types.Slot(params.BeaconConfig().MinEpochsToInactivityPenalty+2)))
|
||||
validators, balance, err := InitializeEpochValidators(context.Background(), s)
|
||||
require.NoError(t, err)
|
||||
validators, _, err = ProcessEpochParticipation(context.Background(), s, balance, validators)
|
||||
require.NoError(t, err)
|
||||
s, _, err = ProcessInactivityScores(context.Background(), s, validators)
|
||||
require.NoError(t, err)
|
||||
inactivityScores, err := s.InactivityScores()
|
||||
require.NoError(t, err)
|
||||
// V0 and V1 didn't vote head. V2 and V3 did.
|
||||
require.Equal(t, defaultScore+params.BeaconConfig().InactivityScoreBias, inactivityScores[0])
|
||||
require.Equal(t, defaultScore+params.BeaconConfig().InactivityScoreBias, inactivityScores[1])
|
||||
require.Equal(t, defaultScore-1, inactivityScores[2])
|
||||
require.Equal(t, defaultScore-1, inactivityScores[3])
|
||||
}
|
||||
|
||||
func TestProcessRewardsAndPenaltiesPrecompute_GenesisEpoch(t *testing.T) {
|
||||
s, err := testState()
|
||||
require.NoError(t, err)
|
||||
validators, balance, err := InitializeEpochValidators(context.Background(), s)
|
||||
require.NoError(t, err)
|
||||
validators, balance, err = ProcessEpochParticipation(context.Background(), s, balance, validators)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.SetSlot(0))
|
||||
s, err = ProcessRewardsAndPenaltiesPrecompute(s, balance, validators)
|
||||
require.NoError(t, err)
|
||||
|
||||
balances := s.Balances()
|
||||
// Nothing should happen at genesis epoch
|
||||
for i := 1; i < len(balances); i++ {
|
||||
require.Equal(t, true, balances[i] == balances[i-1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessRewardsAndPenaltiesPrecompute_BadState(t *testing.T) {
|
||||
s, err := testState()
|
||||
require.NoError(t, err)
|
||||
validators, balance, err := InitializeEpochValidators(context.Background(), s)
|
||||
require.NoError(t, err)
|
||||
_, balance, err = ProcessEpochParticipation(context.Background(), s, balance, validators)
|
||||
require.NoError(t, err)
|
||||
_, err = ProcessRewardsAndPenaltiesPrecompute(s, balance, []*precompute.Validator{})
|
||||
require.ErrorContains(t, "validator registries not the same length as state's validator registries", err)
|
||||
}
|
||||
|
||||
func testState() (state.BeaconState, error) {
|
||||
generateParticipation := func(flags ...uint8) byte {
|
||||
b := byte(0)
|
||||
for _, flag := range flags {
|
||||
b = AddValidatorFlag(b, flag)
|
||||
}
|
||||
return b
|
||||
}
|
||||
return stateAltair.InitializeFromProto(ðpb.BeaconStateAltair{
|
||||
Slot: 2 * params.BeaconConfig().SlotsPerEpoch,
|
||||
Validators: []*ethpb.Validator{
|
||||
{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance, ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance, ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance, ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance, ExitEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
},
|
||||
CurrentEpochParticipation: []byte{
|
||||
0,
|
||||
generateParticipation(params.BeaconConfig().TimelySourceFlagIndex),
|
||||
generateParticipation(params.BeaconConfig().TimelySourceFlagIndex, params.BeaconConfig().TimelyTargetFlagIndex),
|
||||
generateParticipation(params.BeaconConfig().TimelySourceFlagIndex, params.BeaconConfig().TimelyTargetFlagIndex, params.BeaconConfig().TimelyHeadFlagIndex),
|
||||
},
|
||||
PreviousEpochParticipation: []byte{
|
||||
0,
|
||||
generateParticipation(params.BeaconConfig().TimelySourceFlagIndex),
|
||||
generateParticipation(params.BeaconConfig().TimelySourceFlagIndex, params.BeaconConfig().TimelyTargetFlagIndex),
|
||||
generateParticipation(params.BeaconConfig().TimelySourceFlagIndex, params.BeaconConfig().TimelyTargetFlagIndex, params.BeaconConfig().TimelyHeadFlagIndex),
|
||||
},
|
||||
InactivityScores: []uint64{0, 0, 0, 0},
|
||||
Balances: []uint64{0, 0, 0, 0},
|
||||
})
|
||||
}
|
||||
116
beacon-chain/core/altair/epoch_spec.go
Normal file
116
beacon-chain/core/altair/epoch_spec.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package altair
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/mathutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
// ProcessSyncCommitteeUpdates processes sync client committee updates for the beacon state.
|
||||
//
|
||||
// Spec code:
|
||||
// def process_sync_committee_updates(state: BeaconState) -> None:
|
||||
// next_epoch = get_current_epoch(state) + Epoch(1)
|
||||
// if next_epoch % EPOCHS_PER_SYNC_COMMITTEE_PERIOD == 0:
|
||||
// state.current_sync_committee = state.next_sync_committee
|
||||
// state.next_sync_committee = get_next_sync_committee(state)
|
||||
func ProcessSyncCommitteeUpdates(ctx context.Context, beaconState state.BeaconStateAltair) (state.BeaconStateAltair, error) {
|
||||
nextEpoch := helpers.NextEpoch(beaconState)
|
||||
if nextEpoch%params.BeaconConfig().EpochsPerSyncCommitteePeriod == 0 {
|
||||
currentSyncCommittee, err := beaconState.NextSyncCommittee()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := beaconState.SetCurrentSyncCommittee(currentSyncCommittee); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nextCommittee, err := NextSyncCommittee(ctx, beaconState)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := beaconState.SetNextSyncCommittee(nextCommittee); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := helpers.UpdateSyncCommitteeCache(beaconState); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return beaconState, nil
|
||||
}
|
||||
|
||||
// ProcessParticipationFlagUpdates processes participation flag updates by rotating current to previous.
|
||||
//
|
||||
// Spec code:
|
||||
// def process_participation_flag_updates(state: BeaconState) -> None:
|
||||
// state.previous_epoch_participation = state.current_epoch_participation
|
||||
// state.current_epoch_participation = [ParticipationFlags(0b0000_0000) for _ in range(len(state.validators))]
|
||||
func ProcessParticipationFlagUpdates(beaconState state.BeaconStateAltair) (state.BeaconStateAltair, error) {
|
||||
c, err := beaconState.CurrentEpochParticipation()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := beaconState.SetPreviousParticipationBits(c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := beaconState.SetCurrentParticipationBits(make([]byte, beaconState.NumValidators())); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return beaconState, nil
|
||||
}
|
||||
|
||||
// ProcessSlashings processes the slashed validators during epoch processing,
|
||||
// The function is modified to use PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR.
|
||||
//
|
||||
// Spec code:
|
||||
// def process_slashings(state: BeaconState) -> None:
|
||||
// epoch = get_current_epoch(state)
|
||||
// total_balance = get_total_active_balance(state)
|
||||
// adjusted_total_slashing_balance = min(sum(state.slashings) * PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR, total_balance)
|
||||
// for index, validator in enumerate(state.validators):
|
||||
// if validator.slashed and epoch + EPOCHS_PER_SLASHINGS_VECTOR // 2 == validator.withdrawable_epoch:
|
||||
// increment = EFFECTIVE_BALANCE_INCREMENT # Factored out from penalty numerator to avoid uint64 overflow
|
||||
// penalty_numerator = validator.effective_balance // increment * adjusted_total_slashing_balance
|
||||
// penalty = penalty_numerator // total_balance * increment
|
||||
// decrease_balance(state, ValidatorIndex(index), penalty)
|
||||
// decrease_balance(state, ValidatorIndex(index), penalty)
|
||||
func ProcessSlashings(state state.BeaconState) (state.BeaconState, error) {
|
||||
currentEpoch := helpers.CurrentEpoch(state)
|
||||
totalBalance, err := helpers.TotalActiveBalance(state)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get total active balance")
|
||||
}
|
||||
|
||||
// Compute slashed balances in the current epoch
|
||||
exitLength := params.BeaconConfig().EpochsPerSlashingsVector
|
||||
|
||||
// Compute the sum of state slashings
|
||||
slashings := state.Slashings()
|
||||
totalSlashing := uint64(0)
|
||||
for _, slashing := range slashings {
|
||||
totalSlashing += slashing
|
||||
}
|
||||
|
||||
// a callback is used here to apply the following actions to all validators
|
||||
// below equally.
|
||||
increment := params.BeaconConfig().EffectiveBalanceIncrement
|
||||
minSlashing := mathutil.Min(totalSlashing*params.BeaconConfig().ProportionalSlashingMultiplierAltair, totalBalance)
|
||||
err = state.ApplyToEveryValidator(func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error) {
|
||||
correctEpoch := (currentEpoch + exitLength/2) == val.WithdrawableEpoch
|
||||
if val.Slashed && correctEpoch {
|
||||
penaltyNumerator := val.EffectiveBalance / increment * minSlashing
|
||||
penalty := penaltyNumerator / totalBalance * increment
|
||||
if err := helpers.DecreaseBalance(state, types.ValidatorIndex(idx), penalty); err != nil {
|
||||
return false, val, err
|
||||
}
|
||||
return true, val, nil
|
||||
}
|
||||
return false, val, nil
|
||||
})
|
||||
return state, err
|
||||
}
|
||||
181
beacon-chain/core/altair/epoch_spec_test.go
Normal file
181
beacon-chain/core/altair/epoch_spec_test.go
Normal file
@@ -0,0 +1,181 @@
|
||||
package altair_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/altair"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
stateAltair "github.com/prysmaticlabs/prysm/beacon-chain/state/v2"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func TestProcessSyncCommitteeUpdates_CanRotate(t *testing.T) {
|
||||
s, _ := testutil.DeterministicGenesisStateAltair(t, params.BeaconConfig().MaxValidatorsPerCommittee)
|
||||
h := ðpb.BeaconBlockHeader{
|
||||
StateRoot: bytesutil.PadTo([]byte{'a'}, 32),
|
||||
ParentRoot: bytesutil.PadTo([]byte{'b'}, 32),
|
||||
BodyRoot: bytesutil.PadTo([]byte{'c'}, 32),
|
||||
}
|
||||
require.NoError(t, s.SetLatestBlockHeader(h))
|
||||
postState, err := altair.ProcessSyncCommitteeUpdates(context.Background(), s)
|
||||
require.NoError(t, err)
|
||||
current, err := postState.CurrentSyncCommittee()
|
||||
require.NoError(t, err)
|
||||
next, err := postState.NextSyncCommittee()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, current, next)
|
||||
|
||||
require.NoError(t, s.SetSlot(params.BeaconConfig().SlotsPerEpoch))
|
||||
postState, err = altair.ProcessSyncCommitteeUpdates(context.Background(), s)
|
||||
require.NoError(t, err)
|
||||
c, err := postState.CurrentSyncCommittee()
|
||||
require.NoError(t, err)
|
||||
n, err := postState.NextSyncCommittee()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, current, c)
|
||||
require.DeepEqual(t, next, n)
|
||||
|
||||
require.NoError(t, s.SetSlot(types.Slot(params.BeaconConfig().EpochsPerSyncCommitteePeriod)*params.BeaconConfig().SlotsPerEpoch-1))
|
||||
postState, err = altair.ProcessSyncCommitteeUpdates(context.Background(), s)
|
||||
require.NoError(t, err)
|
||||
c, err = postState.CurrentSyncCommittee()
|
||||
require.NoError(t, err)
|
||||
n, err = postState.NextSyncCommittee()
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, current, c)
|
||||
require.NotEqual(t, next, n)
|
||||
require.DeepEqual(t, next, c)
|
||||
|
||||
// Test boundary condition.
|
||||
slot := params.BeaconConfig().SlotsPerEpoch * types.Slot(helpers.CurrentEpoch(s)+params.BeaconConfig().EpochsPerSyncCommitteePeriod)
|
||||
require.NoError(t, s.SetSlot(slot))
|
||||
boundaryCommittee, err := altair.NextSyncCommittee(context.Background(), s)
|
||||
require.NoError(t, err)
|
||||
require.DeepNotEqual(t, boundaryCommittee, n)
|
||||
}
|
||||
|
||||
func TestProcessParticipationFlagUpdates_CanRotate(t *testing.T) {
|
||||
s, _ := testutil.DeterministicGenesisStateAltair(t, params.BeaconConfig().MaxValidatorsPerCommittee)
|
||||
c, err := s.CurrentEpochParticipation()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, make([]byte, params.BeaconConfig().MaxValidatorsPerCommittee), c)
|
||||
p, err := s.PreviousEpochParticipation()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, make([]byte, params.BeaconConfig().MaxValidatorsPerCommittee), p)
|
||||
|
||||
newC := []byte{'a'}
|
||||
newP := []byte{'b'}
|
||||
require.NoError(t, s.SetCurrentParticipationBits(newC))
|
||||
require.NoError(t, s.SetPreviousParticipationBits(newP))
|
||||
c, err = s.CurrentEpochParticipation()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, newC, c)
|
||||
p, err = s.PreviousEpochParticipation()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, newP, p)
|
||||
|
||||
s, err = altair.ProcessParticipationFlagUpdates(s)
|
||||
require.NoError(t, err)
|
||||
c, err = s.CurrentEpochParticipation()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, make([]byte, params.BeaconConfig().MaxValidatorsPerCommittee), c)
|
||||
p, err = s.PreviousEpochParticipation()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, newC, p)
|
||||
}
|
||||
|
||||
func TestProcessSlashings_NotSlashed(t *testing.T) {
|
||||
base := ðpb.BeaconStateAltair{
|
||||
Slot: 0,
|
||||
Validators: []*ethpb.Validator{{Slashed: true}},
|
||||
Balances: []uint64{params.BeaconConfig().MaxEffectiveBalance},
|
||||
Slashings: []uint64{0, 1e9},
|
||||
}
|
||||
s, err := stateAltair.InitializeFromProto(base)
|
||||
require.NoError(t, err)
|
||||
newState, err := altair.ProcessSlashings(s)
|
||||
require.NoError(t, err)
|
||||
wanted := params.BeaconConfig().MaxEffectiveBalance
|
||||
assert.Equal(t, wanted, newState.Balances()[0], "Unexpected slashed balance")
|
||||
}
|
||||
|
||||
func TestProcessSlashings_SlashedLess(t *testing.T) {
|
||||
tests := []struct {
|
||||
state *ethpb.BeaconStateAltair
|
||||
want uint64
|
||||
}{
|
||||
{
|
||||
state: ðpb.BeaconStateAltair{
|
||||
Validators: []*ethpb.Validator{
|
||||
{Slashed: true,
|
||||
WithdrawableEpoch: params.BeaconConfig().EpochsPerSlashingsVector / 2,
|
||||
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch, EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}},
|
||||
Balances: []uint64{params.BeaconConfig().MaxEffectiveBalance, params.BeaconConfig().MaxEffectiveBalance},
|
||||
Slashings: []uint64{0, 1e9},
|
||||
},
|
||||
want: uint64(30000000000),
|
||||
},
|
||||
{
|
||||
state: ðpb.BeaconStateAltair{
|
||||
Validators: []*ethpb.Validator{
|
||||
{Slashed: true,
|
||||
WithdrawableEpoch: params.BeaconConfig().EpochsPerSlashingsVector / 2,
|
||||
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch, EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch, EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance},
|
||||
},
|
||||
Balances: []uint64{params.BeaconConfig().MaxEffectiveBalance, params.BeaconConfig().MaxEffectiveBalance},
|
||||
Slashings: []uint64{0, 1e9},
|
||||
},
|
||||
want: uint64(31000000000),
|
||||
},
|
||||
{
|
||||
state: ðpb.BeaconStateAltair{
|
||||
Validators: []*ethpb.Validator{
|
||||
{Slashed: true,
|
||||
WithdrawableEpoch: params.BeaconConfig().EpochsPerSlashingsVector / 2,
|
||||
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch, EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch, EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance},
|
||||
},
|
||||
Balances: []uint64{params.BeaconConfig().MaxEffectiveBalance, params.BeaconConfig().MaxEffectiveBalance},
|
||||
Slashings: []uint64{0, 2 * 1e9},
|
||||
},
|
||||
want: uint64(30000000000),
|
||||
},
|
||||
{
|
||||
state: ðpb.BeaconStateAltair{
|
||||
Validators: []*ethpb.Validator{
|
||||
{Slashed: true,
|
||||
WithdrawableEpoch: params.BeaconConfig().EpochsPerSlashingsVector / 2,
|
||||
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance - params.BeaconConfig().EffectiveBalanceIncrement},
|
||||
{ExitEpoch: params.BeaconConfig().FarFutureEpoch, EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance - params.BeaconConfig().EffectiveBalanceIncrement}},
|
||||
Balances: []uint64{params.BeaconConfig().MaxEffectiveBalance - params.BeaconConfig().EffectiveBalanceIncrement, params.BeaconConfig().MaxEffectiveBalance - params.BeaconConfig().EffectiveBalanceIncrement},
|
||||
Slashings: []uint64{0, 1e9},
|
||||
},
|
||||
want: uint64(29000000000),
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
t.Run(fmt.Sprint(i), func(t *testing.T) {
|
||||
helpers.ClearCache()
|
||||
original := proto.Clone(tt.state)
|
||||
s, err := stateAltair.InitializeFromProto(tt.state)
|
||||
require.NoError(t, err)
|
||||
newState, err := altair.ProcessSlashings(s)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.want, newState.Balances()[0], "ProcessSlashings({%v}) = newState; newState.Balances[0] = %d", original, newState.Balances()[0])
|
||||
})
|
||||
}
|
||||
}
|
||||
115
beacon-chain/core/altair/fork_transition.go
Normal file
115
beacon-chain/core/altair/fork_transition.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package altair
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
statealtair "github.com/prysmaticlabs/prysm/beacon-chain/state/v2"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/attestationutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
// UpgradeToAltair updates input state to return the version Altair state.
|
||||
func UpgradeToAltair(ctx context.Context, state state.BeaconState) (state.BeaconStateAltair, error) {
|
||||
epoch := helpers.CurrentEpoch(state)
|
||||
|
||||
s := ðpb.BeaconStateAltair{
|
||||
GenesisTime: state.GenesisTime(),
|
||||
GenesisValidatorsRoot: state.GenesisValidatorRoot(),
|
||||
Slot: state.Slot(),
|
||||
Fork: ðpb.Fork{
|
||||
PreviousVersion: state.Fork().CurrentVersion,
|
||||
CurrentVersion: params.BeaconConfig().AltairForkVersion,
|
||||
Epoch: epoch,
|
||||
},
|
||||
LatestBlockHeader: state.LatestBlockHeader(),
|
||||
BlockRoots: state.BlockRoots(),
|
||||
StateRoots: state.StateRoots(),
|
||||
HistoricalRoots: state.HistoricalRoots(),
|
||||
Eth1Data: state.Eth1Data(),
|
||||
Eth1DataVotes: state.Eth1DataVotes(),
|
||||
Eth1DepositIndex: state.Eth1DepositIndex(),
|
||||
Validators: state.Validators(),
|
||||
Balances: state.Balances(),
|
||||
RandaoMixes: state.RandaoMixes(),
|
||||
Slashings: state.Slashings(),
|
||||
PreviousEpochParticipation: make([]byte, state.NumValidators()),
|
||||
CurrentEpochParticipation: make([]byte, state.NumValidators()),
|
||||
JustificationBits: state.JustificationBits(),
|
||||
PreviousJustifiedCheckpoint: state.PreviousJustifiedCheckpoint(),
|
||||
CurrentJustifiedCheckpoint: state.CurrentJustifiedCheckpoint(),
|
||||
FinalizedCheckpoint: state.FinalizedCheckpoint(),
|
||||
InactivityScores: make([]uint64, state.NumValidators()),
|
||||
}
|
||||
|
||||
newState, err := statealtair.InitializeFromProto(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
prevEpochAtts, err := state.PreviousEpochAttestations()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newState, err = TranslateParticipation(newState, prevEpochAtts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
committee, err := NextSyncCommittee(ctx, newState)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := newState.SetCurrentSyncCommittee(committee); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := newState.SetNextSyncCommittee(committee); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newState, nil
|
||||
}
|
||||
|
||||
// TranslateParticipation translates pending attestations into participation bits, then inserts the bits into beacon state.
|
||||
// This is helper function t o convert phase 0 beacon state(pending attestations) to Altair beacon state(participation bits).
|
||||
func TranslateParticipation(state *statealtair.BeaconState, atts []*ethpb.PendingAttestation) (*statealtair.BeaconState, error) {
|
||||
for _, att := range atts {
|
||||
epochParticipation, err := state.PreviousEpochParticipation()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
participatedFlags, err := attestationParticipationFlagIndices(state, att.Data, att.InclusionDelay)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
committee, err := helpers.BeaconCommitteeFromState(state, att.Data.Slot, att.Data.CommitteeIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
indices, err := attestationutil.AttestingIndices(att.AggregationBits, committee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sourceFlagIndex := params.BeaconConfig().TimelySourceFlagIndex
|
||||
targetFlagIndex := params.BeaconConfig().TimelyTargetFlagIndex
|
||||
headFlagIndex := params.BeaconConfig().TimelyHeadFlagIndex
|
||||
for _, index := range indices {
|
||||
if participatedFlags[sourceFlagIndex] && !HasValidatorFlag(epochParticipation[index], sourceFlagIndex) {
|
||||
epochParticipation[index] = AddValidatorFlag(epochParticipation[index], sourceFlagIndex)
|
||||
}
|
||||
if participatedFlags[targetFlagIndex] && !HasValidatorFlag(epochParticipation[index], targetFlagIndex) {
|
||||
epochParticipation[index] = AddValidatorFlag(epochParticipation[index], targetFlagIndex)
|
||||
}
|
||||
if participatedFlags[headFlagIndex] && !HasValidatorFlag(epochParticipation[index], headFlagIndex) {
|
||||
epochParticipation[index] = AddValidatorFlag(epochParticipation[index], headFlagIndex)
|
||||
}
|
||||
}
|
||||
|
||||
if err := state.SetPreviousParticipationBits(epochParticipation); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return state, nil
|
||||
}
|
||||
87
beacon-chain/core/altair/fork_transition_test.go
Normal file
87
beacon-chain/core/altair/fork_transition_test.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package altair_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/altair"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
stateAltair "github.com/prysmaticlabs/prysm/beacon-chain/state/v2"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/attestationutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
|
||||
func TestTranslateParticipation(t *testing.T) {
|
||||
s, _ := testutil.DeterministicGenesisStateAltair(t, 64)
|
||||
st, ok := s.(*stateAltair.BeaconState)
|
||||
require.Equal(t, true, ok)
|
||||
require.NoError(t, st.SetSlot(st.Slot()+params.BeaconConfig().MinAttestationInclusionDelay))
|
||||
|
||||
var err error
|
||||
newState, err := altair.TranslateParticipation(st, nil)
|
||||
require.NoError(t, err)
|
||||
participation, err := newState.PreviousEpochParticipation()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, make([]byte, 64), participation)
|
||||
|
||||
aggBits := bitfield.NewBitlist(2)
|
||||
aggBits.SetBitAt(0, true)
|
||||
aggBits.SetBitAt(1, true)
|
||||
r, err := helpers.BlockRootAtSlot(s, 0)
|
||||
require.NoError(t, err)
|
||||
var pendingAtts []*ethpb.PendingAttestation
|
||||
for i := 0; i < 3; i++ {
|
||||
pendingAtts = append(pendingAtts, ðpb.PendingAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
CommitteeIndex: types.CommitteeIndex(i),
|
||||
BeaconBlockRoot: r,
|
||||
Source: ðpb.Checkpoint{Epoch: 0, Root: make([]byte, 32)},
|
||||
Target: ðpb.Checkpoint{Epoch: 0, Root: make([]byte, 32)},
|
||||
},
|
||||
AggregationBits: aggBits,
|
||||
InclusionDelay: 1,
|
||||
})
|
||||
}
|
||||
|
||||
newState, err = altair.TranslateParticipation(newState, pendingAtts)
|
||||
require.NoError(t, err)
|
||||
participation, err = newState.PreviousEpochParticipation()
|
||||
require.NoError(t, err)
|
||||
require.DeepNotSSZEqual(t, make([]byte, 64), participation)
|
||||
|
||||
committee, err := helpers.BeaconCommitteeFromState(st, pendingAtts[0].Data.Slot, pendingAtts[0].Data.CommitteeIndex)
|
||||
require.NoError(t, err)
|
||||
indices, err := attestationutil.AttestingIndices(pendingAtts[0].AggregationBits, committee)
|
||||
require.NoError(t, err)
|
||||
for _, index := range indices {
|
||||
require.Equal(t, true, altair.HasValidatorFlag(participation[index], params.BeaconConfig().TimelyHeadFlagIndex))
|
||||
require.Equal(t, true, altair.HasValidatorFlag(participation[index], params.BeaconConfig().TimelyTargetFlagIndex))
|
||||
require.Equal(t, true, altair.HasValidatorFlag(participation[index], params.BeaconConfig().TimelySourceFlagIndex))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpgradeToAltair(t *testing.T) {
|
||||
st, _ := testutil.DeterministicGenesisState(t, params.BeaconConfig().MaxValidatorsPerCommittee)
|
||||
aState, err := altair.UpgradeToAltair(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
_, ok := aState.(state.BeaconStateAltair)
|
||||
require.Equal(t, true, ok)
|
||||
|
||||
f := aState.Fork()
|
||||
require.DeepSSZEqual(t, ðpb.Fork{
|
||||
PreviousVersion: st.Fork().CurrentVersion,
|
||||
CurrentVersion: params.BeaconConfig().AltairForkVersion,
|
||||
Epoch: helpers.CurrentEpoch(st),
|
||||
}, f)
|
||||
csc, err := aState.CurrentSyncCommittee()
|
||||
require.NoError(t, err)
|
||||
nsc, err := aState.NextSyncCommittee()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, nsc, csc)
|
||||
}
|
||||
51
beacon-chain/core/altair/reward.go
Normal file
51
beacon-chain/core/altair/reward.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package altair
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/shared/mathutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
// BaseReward takes state and validator index and calculate
|
||||
// individual validator's base reward quotient.
|
||||
//
|
||||
// Spec code:
|
||||
// def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei:
|
||||
// """
|
||||
// Return the base reward for the validator defined by ``index`` with respect to the current ``state``.
|
||||
//
|
||||
// Note: An optimally performing validator can earn one base reward per epoch over a long time horizon.
|
||||
// This takes into account both per-epoch (e.g. attestation) and intermittent duties (e.g. block proposal
|
||||
// and sync committees).
|
||||
// """
|
||||
// increments = state.validators[index].effective_balance // EFFECTIVE_BALANCE_INCREMENT
|
||||
// return Gwei(increments * get_base_reward_per_increment(state))
|
||||
func BaseReward(state state.ReadOnlyBeaconState, index types.ValidatorIndex) (uint64, error) {
|
||||
totalBalance, err := helpers.TotalActiveBalance(state)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not calculate active balance")
|
||||
}
|
||||
return BaseRewardWithTotalBalance(state, index, totalBalance)
|
||||
}
|
||||
|
||||
// BaseRewardWithTotalBalance calculates the base reward with the provided total balance.
|
||||
func BaseRewardWithTotalBalance(state state.ReadOnlyBeaconState, index types.ValidatorIndex, totalBalance uint64) (uint64, error) {
|
||||
val, err := state.ValidatorAtIndexReadOnly(index)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
increments := val.EffectiveBalance() / params.BeaconConfig().EffectiveBalanceIncrement
|
||||
return increments * baseRewardPerIncrement(totalBalance), nil
|
||||
}
|
||||
|
||||
// baseRewardPerIncrement of the beacon state
|
||||
//
|
||||
// Spec code:
|
||||
// def get_base_reward_per_increment(state: BeaconState) -> Gwei:
|
||||
// return Gwei(EFFECTIVE_BALANCE_INCREMENT * BASE_REWARD_FACTOR // integer_squareroot(get_total_active_balance(state)))
|
||||
func baseRewardPerIncrement(activeBalance uint64) uint64 {
|
||||
return params.BeaconConfig().EffectiveBalanceIncrement * params.BeaconConfig().BaseRewardFactor / mathutil.IntegerSquareRoot(activeBalance)
|
||||
}
|
||||
28
beacon-chain/core/altair/reward_test.go
Normal file
28
beacon-chain/core/altair/reward_test.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package altair_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
altair "github.com/prysmaticlabs/prysm/beacon-chain/core/altair"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
|
||||
func TestBaseReward(t *testing.T) {
|
||||
s, _ := testutil.DeterministicGenesisStateAltair(t, params.BeaconConfig().MaxValidatorsPerCommittee)
|
||||
r0, err := altair.BaseReward(s, 0)
|
||||
require.NoError(t, err)
|
||||
r1, err := altair.BaseReward(s, 1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, r0, r1)
|
||||
|
||||
v, err := s.ValidatorAtIndex(0)
|
||||
require.NoError(t, err)
|
||||
v.EffectiveBalance = v.EffectiveBalance + params.BeaconConfig().EffectiveBalanceIncrement
|
||||
require.NoError(t, s.UpdateValidatorAtIndex(0, v))
|
||||
|
||||
r0, err = altair.BaseReward(s, 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, r0 > r1)
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package altair
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
@@ -170,7 +172,7 @@ func SyncSubCommitteePubkeys(syncCommittee *ethpb.SyncCommittee, subComIdx types
|
||||
// modulo = max(1, SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT // TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE)
|
||||
// return bytes_to_uint64(hash(signature)[0:8]) % modulo == 0
|
||||
func IsSyncCommitteeAggregator(sig []byte) (bool, error) {
|
||||
if len(sig) != params.BeaconConfig().BLSPubkeyLength {
|
||||
if len(sig) != params.BeaconConfig().BLSSignatureLength {
|
||||
return false, errors.New("incorrect sig length")
|
||||
}
|
||||
|
||||
@@ -179,3 +181,41 @@ func IsSyncCommitteeAggregator(sig []byte) (bool, error) {
|
||||
hashedSig := hashutil.Hash(sig)
|
||||
return bytesutil.FromBytes8(hashedSig[:8])%modulo == 0, nil
|
||||
}
|
||||
|
||||
// Validate Sync Message to ensure that the provided slot is valid.
|
||||
func ValidateSyncMessageTime(slot types.Slot, genesisTime time.Time, clockDisparity time.Duration) error {
|
||||
if err := helpers.ValidateSlotClock(slot, uint64(genesisTime.Unix())); err != nil {
|
||||
return err
|
||||
}
|
||||
messageTime, err := helpers.SlotToTime(uint64(genesisTime.Unix()), slot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentSlot := helpers.SlotsSince(genesisTime)
|
||||
slotStartTime, err := helpers.SlotToTime(uint64(genesisTime.Unix()), currentSlot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lowestSlotBound := slotStartTime.Add(-clockDisparity)
|
||||
currentLowerBound := time.Now().Add(-clockDisparity)
|
||||
// In the event the Slot's start time, is before the
|
||||
// current allowable bound, we set the slot's start
|
||||
// time as the bound.
|
||||
if slotStartTime.Before(currentLowerBound) {
|
||||
lowestSlotBound = slotStartTime
|
||||
}
|
||||
|
||||
lowerBound := lowestSlotBound
|
||||
upperBound := time.Now().Add(clockDisparity)
|
||||
// Verify sync message slot is within the time range.
|
||||
if messageTime.Before(lowerBound) || messageTime.After(upperBound) {
|
||||
return fmt.Errorf(
|
||||
"sync message slot %d not within allowable range of %d to %d (current slot)",
|
||||
slot,
|
||||
lowerBound.Unix(),
|
||||
upperBound.Unix(),
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package altair_test
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/altair"
|
||||
@@ -11,7 +12,9 @@ import (
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
"github.com/prysmaticlabs/prysm/shared/timeutils"
|
||||
)
|
||||
|
||||
func TestSyncCommitteeIndices_CanGet(t *testing.T) {
|
||||
@@ -278,6 +281,107 @@ func TestSyncSubCommitteePubkeys_CanGet(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func Test_ValidateSyncMessageTime(t *testing.T) {
|
||||
if params.BeaconNetworkConfig().MaximumGossipClockDisparity < 200*time.Millisecond {
|
||||
t.Fatal("This test expects the maximum clock disparity to be at least 200ms")
|
||||
}
|
||||
|
||||
type args struct {
|
||||
syncMessageSlot types.Slot
|
||||
genesisTime time.Time
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantedErr string
|
||||
}{
|
||||
{
|
||||
name: "sync_message.slot == current_slot",
|
||||
args: args{
|
||||
syncMessageSlot: 15,
|
||||
genesisTime: timeutils.Now().Add(-15 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sync_message.slot == current_slot, received in middle of slot",
|
||||
args: args{
|
||||
syncMessageSlot: 15,
|
||||
genesisTime: timeutils.Now().Add(
|
||||
-15 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second,
|
||||
).Add(-(time.Duration(params.BeaconConfig().SecondsPerSlot/2) * time.Second)),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sync_message.slot == current_slot, received 200ms early",
|
||||
args: args{
|
||||
syncMessageSlot: 16,
|
||||
genesisTime: timeutils.Now().Add(
|
||||
-16 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second,
|
||||
).Add(-200 * time.Millisecond),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sync_message.slot > current_slot",
|
||||
args: args{
|
||||
syncMessageSlot: 16,
|
||||
genesisTime: timeutils.Now().Add(-(15 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second)),
|
||||
},
|
||||
wantedErr: "sync message slot 16 not within allowable range of",
|
||||
},
|
||||
{
|
||||
name: "sync_message.slot == current_slot+CLOCK_DISPARITY",
|
||||
args: args{
|
||||
syncMessageSlot: 100,
|
||||
genesisTime: timeutils.Now().Add(-(100*time.Duration(params.BeaconConfig().SecondsPerSlot)*time.Second - params.BeaconNetworkConfig().MaximumGossipClockDisparity)),
|
||||
},
|
||||
wantedErr: "",
|
||||
},
|
||||
{
|
||||
name: "sync_message.slot == current_slot+CLOCK_DISPARITY+200ms",
|
||||
args: args{
|
||||
syncMessageSlot: 100,
|
||||
genesisTime: timeutils.Now().Add(-(100*time.Duration(params.BeaconConfig().SecondsPerSlot)*time.Second - params.BeaconNetworkConfig().MaximumGossipClockDisparity - 200*time.Millisecond)),
|
||||
},
|
||||
wantedErr: "sync message slot 100 not within allowable range of",
|
||||
},
|
||||
{
|
||||
name: "sync_message.slot == current_slot-CLOCK_DISPARITY",
|
||||
args: args{
|
||||
syncMessageSlot: 100,
|
||||
genesisTime: timeutils.Now().Add(-(100*time.Duration(params.BeaconConfig().SecondsPerSlot)*time.Second + params.BeaconNetworkConfig().MaximumGossipClockDisparity)),
|
||||
},
|
||||
wantedErr: "",
|
||||
},
|
||||
{
|
||||
name: "sync_message.slot > current_slot+CLOCK_DISPARITY",
|
||||
args: args{
|
||||
syncMessageSlot: 101,
|
||||
genesisTime: timeutils.Now().Add(-(100*time.Duration(params.BeaconConfig().SecondsPerSlot)*time.Second + params.BeaconNetworkConfig().MaximumGossipClockDisparity)),
|
||||
},
|
||||
wantedErr: "sync message slot 101 not within allowable range of",
|
||||
},
|
||||
{
|
||||
name: "sync_message.slot is well beyond current slot",
|
||||
args: args{
|
||||
syncMessageSlot: 1 << 32,
|
||||
genesisTime: timeutils.Now().Add(-15 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second),
|
||||
},
|
||||
wantedErr: "which exceeds max allowed value relative to the local clock",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := altair.ValidateSyncMessageTime(tt.args.syncMessageSlot, tt.args.genesisTime,
|
||||
params.BeaconNetworkConfig().MaximumGossipClockDisparity)
|
||||
if tt.wantedErr != "" {
|
||||
assert.ErrorContains(t, tt.wantedErr, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func getState(t *testing.T, count uint64) *stateAltair.BeaconState {
|
||||
validators := make([]*ethpb.Validator, count)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
|
||||
110
beacon-chain/core/altair/transition.go
Normal file
110
beacon-chain/core/altair/transition.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package altair
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
e "github.com/prysmaticlabs/prysm/beacon-chain/core/epoch"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch/precompute"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
// ProcessEpoch describes the per epoch operations that are performed on the beacon state.
|
||||
// It's optimized by pre computing validator attested info and epoch total/attested balances upfront.
|
||||
//
|
||||
// Spec code:
|
||||
// def process_epoch(state: BeaconState) -> None:
|
||||
// process_justification_and_finalization(state) # [Modified in Altair]
|
||||
// process_inactivity_updates(state) # [New in Altair]
|
||||
// process_rewards_and_penalties(state) # [Modified in Altair]
|
||||
// process_registry_updates(state)
|
||||
// process_slashings(state) # [Modified in Altair]
|
||||
// process_eth1_data_reset(state)
|
||||
// process_effective_balance_updates(state)
|
||||
// process_slashings_reset(state)
|
||||
// process_randao_mixes_reset(state)
|
||||
// process_historical_roots_update(state)
|
||||
// process_participation_flag_updates(state) # [New in Altair]
|
||||
// process_sync_committee_updates(state) # [New in Altair]
|
||||
func ProcessEpoch(ctx context.Context, state state.BeaconStateAltair) (state.BeaconStateAltair, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "altair.ProcessEpoch")
|
||||
defer span.End()
|
||||
|
||||
if state == nil || state.IsNil() {
|
||||
return nil, errors.New("nil state")
|
||||
}
|
||||
vp, bp, err := InitializeEpochValidators(ctx, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// New in Altair.
|
||||
vp, bp, err = ProcessEpochParticipation(ctx, state, bp, vp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
state, err = precompute.ProcessJustificationAndFinalizationPreCompute(state, bp)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process justification")
|
||||
}
|
||||
|
||||
// New in Altair.
|
||||
state, vp, err = ProcessInactivityScores(ctx, state, vp)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process inactivity updates")
|
||||
}
|
||||
|
||||
// New in Altair.
|
||||
state, err = ProcessRewardsAndPenaltiesPrecompute(state, bp, vp)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process rewards and penalties")
|
||||
}
|
||||
|
||||
state, err = e.ProcessRegistryUpdates(state)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process registry updates")
|
||||
}
|
||||
|
||||
// Modified in Altair.
|
||||
state, err = ProcessSlashings(state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
state, err = e.ProcessEth1DataReset(state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
state, err = e.ProcessEffectiveBalanceUpdates(state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
state, err = e.ProcessSlashingsReset(state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
state, err = e.ProcessRandaoMixesReset(state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
state, err = e.ProcessHistoricalRootsUpdate(state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// New in Altair.
|
||||
state, err = ProcessParticipationFlagUpdates(state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// New in Altair.
|
||||
state, err = ProcessSyncCommitteeUpdates(ctx, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return state, nil
|
||||
}
|
||||
34
beacon-chain/core/altair/transition_test.go
Normal file
34
beacon-chain/core/altair/transition_test.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package altair_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/altair"
|
||||
stateAltair "github.com/prysmaticlabs/prysm/beacon-chain/state/v2"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
|
||||
func TestProcessEpoch_CanProcess(t *testing.T) {
|
||||
epoch := types.Epoch(1)
|
||||
slashing := make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector)
|
||||
base := ðpb.BeaconStateAltair{
|
||||
Slot: params.BeaconConfig().SlotsPerEpoch.Mul(uint64(epoch)) + 1,
|
||||
BlockRoots: make([][]byte, 128),
|
||||
Slashings: slashing,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{Root: make([]byte, 32)},
|
||||
JustificationBits: bitfield.Bitvector4{0x00},
|
||||
CurrentJustifiedCheckpoint: ðpb.Checkpoint{Root: make([]byte, 32)},
|
||||
}
|
||||
s, err := stateAltair.InitializeFromProto(base)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.SetValidators([]*ethpb.Validator{}))
|
||||
newState, err := altair.ProcessEpoch(context.Background(), s)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(0), newState.Slashings()[2], "Unexpected slashed balance")
|
||||
}
|
||||
86
beacon-chain/core/altair/validator.go
Normal file
86
beacon-chain/core/altair/validator.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package altair
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/validators"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
// SlashValidator with slashed index.
|
||||
// The function is modified to use MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR and use PROPOSER_WEIGHT when calculating the proposer reward.
|
||||
//
|
||||
// def slash_validator(state: BeaconState,
|
||||
// slashed_index: ValidatorIndex,
|
||||
// whistleblower_index: ValidatorIndex=None) -> None:
|
||||
// """
|
||||
// Slash the validator with index ``slashed_index``.
|
||||
// """
|
||||
// epoch = get_current_epoch(state)
|
||||
// initiate_validator_exit(state, slashed_index)
|
||||
// validator = state.validators[slashed_index]
|
||||
// validator.slashed = True
|
||||
// validator.withdrawable_epoch = max(validator.withdrawable_epoch, Epoch(epoch + EPOCHS_PER_SLASHINGS_VECTOR))
|
||||
// state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effective_balance
|
||||
// decrease_balance(state, slashed_index, validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR)
|
||||
//
|
||||
// # Apply proposer and whistleblower rewards
|
||||
// proposer_index = get_beacon_proposer_index(state)
|
||||
// if whistleblower_index is None:
|
||||
// whistleblower_index = proposer_index
|
||||
// whistleblower_reward = Gwei(validator.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT)
|
||||
// proposer_reward = Gwei(whistleblower_reward * PROPOSER_WEIGHT // WEIGHT_DENOMINATOR)
|
||||
// increase_balance(state, proposer_index, proposer_reward)
|
||||
// increase_balance(state, whistleblower_index, Gwei(whistleblower_reward - proposer_reward))
|
||||
func SlashValidator(state state.BeaconState, slashedIdx types.ValidatorIndex) (state.BeaconState, error) {
|
||||
state, err := validators.InitiateValidatorExit(state, slashedIdx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not initiate validator %d exit", slashedIdx)
|
||||
}
|
||||
currentEpoch := helpers.SlotToEpoch(state.Slot())
|
||||
validator, err := state.ValidatorAtIndex(slashedIdx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
validator.Slashed = true
|
||||
maxWithdrawableEpoch := types.MaxEpoch(validator.WithdrawableEpoch, currentEpoch+params.BeaconConfig().EpochsPerSlashingsVector)
|
||||
validator.WithdrawableEpoch = maxWithdrawableEpoch
|
||||
|
||||
if err := state.UpdateValidatorAtIndex(slashedIdx, validator); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// The slashing amount is represented by epochs per slashing vector. The validator's effective balance is then applied to that amount.
|
||||
slashings := state.Slashings()
|
||||
currentSlashing := slashings[currentEpoch%params.BeaconConfig().EpochsPerSlashingsVector]
|
||||
if err := state.UpdateSlashingsAtIndex(
|
||||
uint64(currentEpoch%params.BeaconConfig().EpochsPerSlashingsVector),
|
||||
currentSlashing+validator.EffectiveBalance,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := helpers.DecreaseBalance(state, slashedIdx, validator.EffectiveBalance/params.BeaconConfig().MinSlashingPenaltyQuotientAltair); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
proposerIdx, err := helpers.BeaconProposerIndex(state)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get proposer idx")
|
||||
}
|
||||
|
||||
// In this implementation, proposer is the whistleblower.
|
||||
whistleBlowerIdx := proposerIdx
|
||||
whistleblowerReward := validator.EffectiveBalance / params.BeaconConfig().WhistleBlowerRewardQuotient
|
||||
proposerReward := whistleblowerReward * params.BeaconConfig().ProposerWeight / params.BeaconConfig().WeightDenominator
|
||||
err = helpers.IncreaseBalance(state, proposerIdx, proposerReward)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = helpers.IncreaseBalance(state, whistleBlowerIdx, whistleblowerReward-proposerReward)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return state, nil
|
||||
}
|
||||
68
beacon-chain/core/altair/validator_test.go
Normal file
68
beacon-chain/core/altair/validator_test.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package altair_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/altair"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
stateAltair "github.com/prysmaticlabs/prysm/beacon-chain/state/v2"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
|
||||
func TestSlashValidator_OK(t *testing.T) {
|
||||
validatorCount := params.BeaconConfig().MinGenesisActiveValidatorCount
|
||||
registry := make([]*ethpb.Validator, 0, validatorCount)
|
||||
balances := make([]uint64, 0, validatorCount)
|
||||
for i := uint64(0); i < validatorCount; i++ {
|
||||
registry = append(registry, ðpb.Validator{
|
||||
ActivationEpoch: 0,
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
||||
})
|
||||
balances = append(balances, params.BeaconConfig().MaxEffectiveBalance)
|
||||
}
|
||||
|
||||
base := ðpb.BeaconStateAltair{
|
||||
Validators: registry,
|
||||
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
Balances: balances,
|
||||
}
|
||||
state, err := stateAltair.InitializeFromProto(base)
|
||||
require.NoError(t, err)
|
||||
|
||||
slashedIdx := types.ValidatorIndex(2)
|
||||
|
||||
proposer, err := helpers.BeaconProposerIndex(state)
|
||||
require.NoError(t, err, "Could not get proposer")
|
||||
proposerBal, err := state.BalanceAtIndex(proposer)
|
||||
require.NoError(t, err)
|
||||
slashedState, err := altair.SlashValidator(state, slashedIdx)
|
||||
require.NoError(t, err, "Could not slash validator")
|
||||
state, ok := slashedState.(*stateAltair.BeaconState)
|
||||
require.Equal(t, true, ok)
|
||||
|
||||
v, err := state.ValidatorAtIndex(slashedIdx)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, v.Slashed, "Validator not slashed despite supposed to being slashed")
|
||||
assert.Equal(t, helpers.CurrentEpoch(state)+params.BeaconConfig().EpochsPerSlashingsVector, v.WithdrawableEpoch, "Withdrawable epoch not the expected value")
|
||||
|
||||
maxBalance := params.BeaconConfig().MaxEffectiveBalance
|
||||
slashedBalance := state.Slashings()[state.Slot().Mod(uint64(params.BeaconConfig().EpochsPerSlashingsVector))]
|
||||
assert.Equal(t, maxBalance, slashedBalance, "Slashed balance isnt the expected amount")
|
||||
|
||||
whistleblowerReward := slashedBalance / params.BeaconConfig().WhistleBlowerRewardQuotient
|
||||
bal, err := state.BalanceAtIndex(proposer)
|
||||
require.NoError(t, err)
|
||||
// The proposer is the whistleblower in phase 0.
|
||||
assert.Equal(t, proposerBal+whistleblowerReward, bal, "Did not get expected balance for proposer")
|
||||
bal, err = state.BalanceAtIndex(slashedIdx)
|
||||
require.NoError(t, err)
|
||||
v, err = state.ValidatorAtIndex(slashedIdx)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, maxBalance-(v.EffectiveBalance/params.BeaconConfig().MinSlashingPenaltyQuotientAltair), bal, "Did not get expected balance for slashed validator")
|
||||
}
|
||||
@@ -36,6 +36,7 @@ go_library(
|
||||
"//shared/depositutil:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/mathutil:go_default_library",
|
||||
"//shared/p2putils:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/slashutil:go_default_library",
|
||||
"//shared/sliceutil:go_default_library",
|
||||
@@ -65,6 +66,7 @@ go_test(
|
||||
"proposer_slashing_regression_test.go",
|
||||
"proposer_slashing_test.go",
|
||||
"randao_test.go",
|
||||
"signature_test.go",
|
||||
],
|
||||
data = glob(["testdata/**"]),
|
||||
embed = [":go_default_library"],
|
||||
|
||||
@@ -9,9 +9,10 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
statepb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
|
||||
"github.com/prysmaticlabs/prysm/shared/attestationutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/p2putils"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
@@ -21,7 +22,7 @@ func signatureSet(signedData, pub, signature, domain []byte) (*bls.SignatureSet,
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not convert bytes to public key")
|
||||
}
|
||||
signingData := &statepb.SigningData{
|
||||
signingData := ðpb.SigningData{
|
||||
ObjectRoot: signedData,
|
||||
Domain: domain,
|
||||
}
|
||||
@@ -77,6 +78,27 @@ func VerifyBlockSignature(beaconState state.ReadOnlyBeaconState,
|
||||
return helpers.VerifyBlockSigningRoot(proposerPubKey, sig, domain, rootFunc)
|
||||
}
|
||||
|
||||
// VerifyBlockSignatureUsingCurrentFork verifies the proposer signature of a beacon block. This differs
|
||||
// from the above method by not using fork data from the state and instead retrieving it
|
||||
// via the respective epoch.
|
||||
func VerifyBlockSignatureUsingCurrentFork(beaconState state.ReadOnlyBeaconState, blk block.SignedBeaconBlock) error {
|
||||
currentEpoch := helpers.SlotToEpoch(blk.Block().Slot())
|
||||
fork, err := p2putils.Fork(currentEpoch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
domain, err := helpers.Domain(fork, currentEpoch, params.BeaconConfig().DomainBeaconProposer, beaconState.GenesisValidatorRoot())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
proposer, err := beaconState.ValidatorAtIndex(blk.Block().ProposerIndex())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
proposerPubKey := proposer.PublicKey
|
||||
return helpers.VerifyBlockSigningRoot(proposerPubKey, blk.Signature(), domain, blk.Block().HashTreeRoot)
|
||||
}
|
||||
|
||||
// BlockSignatureSet retrieves the block signature set from the provided block and its corresponding state.
|
||||
func BlockSignatureSet(beaconState state.ReadOnlyBeaconState,
|
||||
proposerIndex types.ValidatorIndex,
|
||||
|
||||
41
beacon-chain/core/blocks/signature_test.go
Normal file
41
beacon-chain/core/blocks/signature_test.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package blocks_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
wrapperv2 "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
|
||||
func TestVerifyBlockSignatureUsingCurrentFork(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
bCfg := params.BeaconConfig()
|
||||
bCfg.AltairForkEpoch = 100
|
||||
bCfg.ForkVersionSchedule[bytesutil.ToBytes4(bCfg.AltairForkVersion)] = 100
|
||||
params.OverrideBeaconConfig(bCfg)
|
||||
bState, keys := testutil.DeterministicGenesisState(t, 100)
|
||||
altairBlk := testutil.NewBeaconBlockAltair()
|
||||
altairBlk.Block.ProposerIndex = 0
|
||||
altairBlk.Block.Slot = params.BeaconConfig().SlotsPerEpoch * 100
|
||||
fData := ðpb.Fork{
|
||||
Epoch: 100,
|
||||
CurrentVersion: params.BeaconConfig().AltairForkVersion,
|
||||
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
|
||||
}
|
||||
domain, err := helpers.Domain(fData, 100, params.BeaconConfig().DomainBeaconProposer, bState.GenesisValidatorRoot())
|
||||
assert.NoError(t, err)
|
||||
rt, err := helpers.ComputeSigningRoot(altairBlk.Block, domain)
|
||||
assert.NoError(t, err)
|
||||
sig := keys[0].Sign(rt[:]).Marshal()
|
||||
altairBlk.Signature = sig
|
||||
wsb, err := wrapperv2.WrappedAltairSignedBeaconBlock(altairBlk)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, blocks.VerifyBlockSignatureUsingCurrentFork(bState, wsb))
|
||||
}
|
||||
@@ -51,6 +51,7 @@ go_library(
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -75,6 +76,7 @@ go_test(
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/v1:go_default_library",
|
||||
"//beacon-chain/state/v2:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//shared/bls:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
|
||||
@@ -17,10 +17,12 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/sliceutil"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var committeeCache = cache.NewCommitteesCache()
|
||||
var proposerIndicesCache = cache.NewProposerIndicesCache()
|
||||
var syncCommitteeCache = cache.NewSyncCommittee()
|
||||
|
||||
// SlotCommitteeCount returns the number of crosslink committees of a slot. The
|
||||
// active validator count is provided as an argument rather than a imported implementation
|
||||
@@ -271,26 +273,21 @@ func VerifyAttestationBitfieldLengths(state state.ReadOnlyBeaconState, att *ethp
|
||||
return nil
|
||||
}
|
||||
|
||||
// ShuffledIndices uses input beacon state and returns the shuffled indices of the input epoch,
|
||||
// the shuffled indices then can be used to break up into committees.
|
||||
func ShuffledIndices(s state.ReadOnlyBeaconState, epoch types.Epoch) ([]types.ValidatorIndex, error) {
|
||||
seed, err := Seed(s, epoch, params.BeaconConfig().DomainBeaconAttester)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not get seed for epoch %d", epoch)
|
||||
}
|
||||
|
||||
// Returns the active indices and the total active balance of the validators in input `state` and during input `epoch`.
|
||||
func activeIndicesAndBalance(s state.ReadOnlyBeaconState, epoch types.Epoch) ([]types.ValidatorIndex, uint64, error) {
|
||||
balances := uint64(0)
|
||||
indices := make([]types.ValidatorIndex, 0, s.NumValidators())
|
||||
if err := s.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error {
|
||||
if IsActiveValidatorUsingTrie(val, epoch) {
|
||||
balances += val.EffectiveBalance()
|
||||
indices = append(indices, types.ValidatorIndex(idx))
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// UnshuffleList is used as an optimized implementation for raw speed.
|
||||
return UnshuffleList(indices, seed)
|
||||
return indices, balances, nil
|
||||
}
|
||||
|
||||
// UpdateCommitteeCache gets called at the beginning of every epoch to cache the committee shuffled indices
|
||||
@@ -306,7 +303,13 @@ func UpdateCommitteeCache(state state.ReadOnlyBeaconState, epoch types.Epoch) er
|
||||
return nil
|
||||
}
|
||||
|
||||
shuffledIndices, err := ShuffledIndices(state, e)
|
||||
indices, balance, err := activeIndicesAndBalance(state, e)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the shuffled indices based on the seed.
|
||||
shuffledIndices, err := UnshuffleList(indices, seed)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -322,11 +325,23 @@ func UpdateCommitteeCache(state state.ReadOnlyBeaconState, epoch types.Epoch) er
|
||||
return sortedIndices[i] < sortedIndices[j]
|
||||
})
|
||||
|
||||
// Only update active balance field in cache if it's current epoch.
|
||||
// Using current epoch state to update next epoch field will cause insert an invalid
|
||||
// active balance value.
|
||||
b := &cache.Balance{}
|
||||
if e == epoch {
|
||||
b = &cache.Balance{
|
||||
Exist: true,
|
||||
Total: balance,
|
||||
}
|
||||
}
|
||||
|
||||
if err := committeeCache.AddCommitteeShuffledList(&cache.Committees{
|
||||
ShuffledIndices: shuffledIndices,
|
||||
CommitteeCount: uint64(params.BeaconConfig().SlotsPerEpoch.Mul(count)),
|
||||
Seed: seed,
|
||||
SortedIndices: sortedIndices,
|
||||
ActiveBalance: b,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -383,10 +398,200 @@ func UpdateProposerIndicesInCache(state state.ReadOnlyBeaconState) error {
|
||||
})
|
||||
}
|
||||
|
||||
// ClearCache clears the committee cache
|
||||
// ClearCache clears the beacon committee cache and sync committee cache.
|
||||
func ClearCache() {
|
||||
committeeCache = cache.NewCommitteesCache()
|
||||
proposerIndicesCache = cache.NewProposerIndicesCache()
|
||||
syncCommitteeCache = cache.NewSyncCommittee()
|
||||
}
|
||||
|
||||
// IsCurrentPeriodSyncCommittee returns true if the input validator index belongs in the current period sync committee
|
||||
// along with the sync committee root.
|
||||
// 1.) Checks if the public key exists in the sync committee cache
|
||||
// 2.) If 1 fails, checks if the public key exists in the input current sync committee object
|
||||
func IsCurrentPeriodSyncCommittee(
|
||||
st state.BeaconStateAltair, valIdx types.ValidatorIndex,
|
||||
) (bool, error) {
|
||||
root, err := syncPeriodBoundaryRoot(st)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
indices, err := syncCommitteeCache.CurrentPeriodIndexPosition(bytesutil.ToBytes32(root), valIdx)
|
||||
if err == cache.ErrNonExistingSyncCommitteeKey {
|
||||
val, err := st.ValidatorAtIndex(valIdx)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
committee, err := st.CurrentSyncCommittee()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Fill in the cache on miss.
|
||||
go func() {
|
||||
if err := syncCommitteeCache.UpdatePositionsInCommittee(bytesutil.ToBytes32(root), st); err != nil {
|
||||
log.Errorf("Could not fill sync committee cache on miss: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return len(findSubCommitteeIndices(val.PublicKey, committee.Pubkeys)) > 0, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return len(indices) > 0, nil
|
||||
}
|
||||
|
||||
// IsNextPeriodSyncCommittee returns true if the input validator index belongs in the next period sync committee
|
||||
// along with the sync period boundary root.
|
||||
// 1.) Checks if the public key exists in the sync committee cache
|
||||
// 2.) If 1 fails, checks if the public key exists in the input next sync committee object
|
||||
func IsNextPeriodSyncCommittee(
|
||||
st state.BeaconStateAltair, valIdx types.ValidatorIndex,
|
||||
) (bool, error) {
|
||||
root, err := syncPeriodBoundaryRoot(st)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
indices, err := syncCommitteeCache.NextPeriodIndexPosition(bytesutil.ToBytes32(root), valIdx)
|
||||
if err == cache.ErrNonExistingSyncCommitteeKey {
|
||||
val, err := st.ValidatorAtIndex(valIdx)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
committee, err := st.NextSyncCommittee()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return len(findSubCommitteeIndices(val.PublicKey, committee.Pubkeys)) > 0, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return len(indices) > 0, nil
|
||||
}
|
||||
|
||||
// CurrentPeriodSyncSubcommitteeIndices returns the subcommittee indices of the
|
||||
// current period sync committee for input validator.
|
||||
func CurrentPeriodSyncSubcommitteeIndices(
|
||||
st state.BeaconStateAltair, valIdx types.ValidatorIndex,
|
||||
) ([]types.CommitteeIndex, error) {
|
||||
root, err := syncPeriodBoundaryRoot(st)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
indices, err := syncCommitteeCache.CurrentPeriodIndexPosition(bytesutil.ToBytes32(root), valIdx)
|
||||
if err == cache.ErrNonExistingSyncCommitteeKey {
|
||||
val, err := st.ValidatorAtIndex(valIdx)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
committee, err := st.CurrentSyncCommittee()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Fill in the cache on miss.
|
||||
go func() {
|
||||
if err := syncCommitteeCache.UpdatePositionsInCommittee(bytesutil.ToBytes32(root), st); err != nil {
|
||||
log.Errorf("Could not fill sync committee cache on miss: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return findSubCommitteeIndices(val.PublicKey, committee.Pubkeys), nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return indices, nil
|
||||
}
|
||||
|
||||
// NextPeriodSyncSubcommitteeIndices returns the subcommittee indices of the next period sync committee for input validator.
|
||||
func NextPeriodSyncSubcommitteeIndices(
|
||||
st state.BeaconStateAltair, valIdx types.ValidatorIndex,
|
||||
) ([]types.CommitteeIndex, error) {
|
||||
root, err := syncPeriodBoundaryRoot(st)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
indices, err := syncCommitteeCache.NextPeriodIndexPosition(bytesutil.ToBytes32(root), valIdx)
|
||||
if err == cache.ErrNonExistingSyncCommitteeKey {
|
||||
val, err := st.ValidatorAtIndex(valIdx)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
committee, err := st.NextSyncCommittee()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return findSubCommitteeIndices(val.PublicKey, committee.Pubkeys), nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return indices, nil
|
||||
}
|
||||
|
||||
// UpdateSyncCommitteeCache updates sync committee cache.
|
||||
// It uses `state`'s latest block header root as key. To avoid miss usage, it disallows
|
||||
// block header with state root zeroed out.
|
||||
func UpdateSyncCommitteeCache(st state.BeaconStateAltair) error {
|
||||
nextSlot := st.Slot() + 1
|
||||
if nextSlot%params.BeaconConfig().SlotsPerEpoch != 0 {
|
||||
return errors.New("not at the end of the epoch to update cache")
|
||||
}
|
||||
if SlotToEpoch(nextSlot)%params.BeaconConfig().EpochsPerSyncCommitteePeriod != 0 {
|
||||
return errors.New("not at sync committee period boundary to update cache")
|
||||
}
|
||||
|
||||
header := st.LatestBlockHeader()
|
||||
if bytes.Equal(header.StateRoot, params.BeaconConfig().ZeroHash[:]) {
|
||||
return errors.New("zero hash state root can't be used to update cache")
|
||||
}
|
||||
|
||||
prevBlockRoot, err := header.HashTreeRoot()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return syncCommitteeCache.UpdatePositionsInCommittee(prevBlockRoot, st)
|
||||
}
|
||||
|
||||
// Loop through `pubKeys` for matching `pubKey` and get the indices where it matches.
|
||||
func findSubCommitteeIndices(pubKey []byte, pubKeys [][]byte) []types.CommitteeIndex {
|
||||
var indices []types.CommitteeIndex
|
||||
for i, k := range pubKeys {
|
||||
if bytes.Equal(k, pubKey) {
|
||||
indices = append(indices, types.CommitteeIndex(i))
|
||||
}
|
||||
}
|
||||
return indices
|
||||
}
|
||||
|
||||
// Retrieve the current sync period boundary root by calculating sync period start epoch
|
||||
// and calling `BlockRoot`.
|
||||
// It uses the boundary slot - 1 for block root. (Ex: SlotsPerEpoch * EpochsPerSyncCommitteePeriod - 1)
|
||||
func syncPeriodBoundaryRoot(st state.ReadOnlyBeaconState) ([]byte, error) {
|
||||
// Can't call `BlockRoot` until the first slot.
|
||||
if st.Slot() == params.BeaconConfig().GenesisSlot {
|
||||
return params.BeaconConfig().ZeroHash[:], nil
|
||||
}
|
||||
|
||||
startEpoch, err := SyncCommitteePeriodStartEpoch(CurrentEpoch(st))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
startEpochSlot, err := StartSlot(startEpoch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Prevent underflow
|
||||
if startEpochSlot >= 1 {
|
||||
startEpochSlot--
|
||||
}
|
||||
|
||||
return BlockRootAtSlot(st, startEpochSlot)
|
||||
}
|
||||
|
||||
// This computes proposer indices of the current epoch and returns a list of proposer indices,
|
||||
|
||||
@@ -2,15 +2,16 @@ package helpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
|
||||
v2 "github.com/prysmaticlabs/prysm/beacon-chain/state/v2"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
statepb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
@@ -35,7 +36,7 @@ func TestComputeCommittee_WithoutCache(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Validators: validators,
|
||||
Slot: 200,
|
||||
BlockRoots: make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot),
|
||||
@@ -91,7 +92,7 @@ func TestVerifyBitfieldLength_OK(t *testing.T) {
|
||||
func TestCommitteeAssignments_CannotRetrieveFutureEpoch(t *testing.T) {
|
||||
ClearCache()
|
||||
epoch := types.Epoch(1)
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Slot: 0, // Epoch 0.
|
||||
})
|
||||
require.NoError(t, err)
|
||||
@@ -111,7 +112,7 @@ func TestCommitteeAssignments_NoProposerForSlot0(t *testing.T) {
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
}
|
||||
}
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Validators: validators,
|
||||
Slot: 2 * params.BeaconConfig().SlotsPerEpoch, // epoch 2
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
@@ -142,7 +143,7 @@ func TestCommitteeAssignments_CanRetrieve(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Validators: validators,
|
||||
Slot: 2 * params.BeaconConfig().SlotsPerEpoch, // epoch 2
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
@@ -219,7 +220,7 @@ func TestCommitteeAssignments_CannotRetrieveFuture(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Validators: validators,
|
||||
Slot: 2 * params.BeaconConfig().SlotsPerEpoch, // epoch 2
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
@@ -243,7 +244,7 @@ func TestCommitteeAssignments_EverySlotHasMin1Proposer(t *testing.T) {
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
}
|
||||
}
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Validators: validators,
|
||||
Slot: 2 * params.BeaconConfig().SlotsPerEpoch, // epoch 2
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
@@ -280,7 +281,7 @@ func TestVerifyAttestationBitfieldLengths_OK(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Validators: validators,
|
||||
RandaoMixes: activeRoots,
|
||||
})
|
||||
@@ -368,38 +369,6 @@ func TestVerifyAttestationBitfieldLengths_OK(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestShuffledIndices_ShuffleRightLength(t *testing.T) {
|
||||
valiatorCount := 1000
|
||||
validators := make([]*ethpb.Validator, valiatorCount)
|
||||
indices := make([]uint64, valiatorCount)
|
||||
for i := 0; i < valiatorCount; i++ {
|
||||
validators[i] = ðpb.Validator{
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
}
|
||||
indices[i] = uint64(i)
|
||||
}
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
// Test for current epoch
|
||||
shuffledIndices, err := ShuffledIndices(state, 0)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, valiatorCount, len(shuffledIndices), "Incorrect shuffled indices count")
|
||||
if reflect.DeepEqual(indices, shuffledIndices) {
|
||||
t.Error("Shuffling did not happen")
|
||||
}
|
||||
|
||||
// Test for next epoch
|
||||
shuffledIndices, err = ShuffledIndices(state, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, valiatorCount, len(shuffledIndices), "Incorrect shuffled indices count")
|
||||
if reflect.DeepEqual(indices, shuffledIndices) {
|
||||
t.Error("Shuffling did not happen")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateCommitteeCache_CanUpdate(t *testing.T) {
|
||||
ClearCache()
|
||||
validatorCount := params.BeaconConfig().MinGenesisActiveValidatorCount
|
||||
@@ -407,11 +376,12 @@ func TestUpdateCommitteeCache_CanUpdate(t *testing.T) {
|
||||
indices := make([]types.ValidatorIndex, validatorCount)
|
||||
for i := types.ValidatorIndex(0); uint64(i) < validatorCount; i++ {
|
||||
validators[i] = ðpb.Validator{
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
EffectiveBalance: 1,
|
||||
}
|
||||
indices[i] = i
|
||||
}
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
})
|
||||
@@ -426,6 +396,13 @@ func TestUpdateCommitteeCache_CanUpdate(t *testing.T) {
|
||||
indices, err = committeeCache.Committee(params.BeaconConfig().SlotsPerEpoch.Mul(uint64(epoch)), seed, idx)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, params.BeaconConfig().TargetCommitteeSize, uint64(len(indices)), "Did not save correct indices lengths")
|
||||
|
||||
// Total active balance should be `MinGenesisActiveValidatorCount` given each validator has effective balance of 1.
|
||||
seed, err = Seed(state, 0, params.BeaconConfig().DomainBeaconAttester)
|
||||
require.NoError(t, err)
|
||||
balance, err := committeeCache.ActiveBalance(seed)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, validatorCount, balance)
|
||||
}
|
||||
|
||||
func BenchmarkComputeCommittee300000_WithPreCache(b *testing.B) {
|
||||
@@ -435,7 +412,7 @@ func BenchmarkComputeCommittee300000_WithPreCache(b *testing.B) {
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
}
|
||||
}
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
})
|
||||
@@ -469,7 +446,7 @@ func BenchmarkComputeCommittee3000000_WithPreCache(b *testing.B) {
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
}
|
||||
}
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
})
|
||||
@@ -503,7 +480,7 @@ func BenchmarkComputeCommittee128000_WithOutPreCache(b *testing.B) {
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
}
|
||||
}
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
})
|
||||
@@ -538,7 +515,7 @@ func BenchmarkComputeCommittee1000000_WithOutCache(b *testing.B) {
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
}
|
||||
}
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
})
|
||||
@@ -573,7 +550,7 @@ func BenchmarkComputeCommittee4000000_WithOutCache(b *testing.B) {
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
}
|
||||
}
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
})
|
||||
@@ -610,7 +587,7 @@ func TestBeaconCommitteeFromState_UpdateCacheForPreviousEpoch(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Slot: params.BeaconConfig().SlotsPerEpoch,
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
@@ -635,7 +612,7 @@ func TestPrecomputeProposerIndices_Ok(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
})
|
||||
@@ -659,3 +636,373 @@ func TestPrecomputeProposerIndices_Ok(t *testing.T) {
|
||||
}
|
||||
assert.DeepEqual(t, wantedProposerIndices, proposerIndices, "Did not precompute proposer indices correctly")
|
||||
}
|
||||
|
||||
func TestIsCurrentEpochSyncCommittee_UsingCache(t *testing.T) {
|
||||
validators := make([]*ethpb.Validator, params.BeaconConfig().SyncCommitteeSize)
|
||||
syncCommittee := ðpb.SyncCommittee{
|
||||
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
|
||||
}
|
||||
for i := 0; i < len(validators); i++ {
|
||||
k := make([]byte, 48)
|
||||
copy(k, strconv.Itoa(i))
|
||||
validators[i] = ðpb.Validator{
|
||||
PublicKey: k,
|
||||
}
|
||||
syncCommittee.Pubkeys = append(syncCommittee.Pubkeys, bytesutil.PadTo(k, 48))
|
||||
}
|
||||
|
||||
state, err := v2.InitializeFromProto(ðpb.BeaconStateAltair{
|
||||
Validators: validators,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, state.SetCurrentSyncCommittee(syncCommittee))
|
||||
require.NoError(t, state.SetNextSyncCommittee(syncCommittee))
|
||||
|
||||
ClearCache()
|
||||
r := [32]byte{'a'}
|
||||
require.NoError(t, err, syncCommitteeCache.UpdatePositionsInCommittee(r, state))
|
||||
|
||||
ok, err := IsCurrentPeriodSyncCommittee(state, 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, ok)
|
||||
}
|
||||
|
||||
func TestIsCurrentEpochSyncCommittee_UsingCommittee(t *testing.T) {
|
||||
validators := make([]*ethpb.Validator, params.BeaconConfig().SyncCommitteeSize)
|
||||
syncCommittee := ðpb.SyncCommittee{
|
||||
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
|
||||
}
|
||||
for i := 0; i < len(validators); i++ {
|
||||
k := make([]byte, 48)
|
||||
copy(k, strconv.Itoa(i))
|
||||
validators[i] = ðpb.Validator{
|
||||
PublicKey: k,
|
||||
}
|
||||
syncCommittee.Pubkeys = append(syncCommittee.Pubkeys, bytesutil.PadTo(k, 48))
|
||||
}
|
||||
|
||||
state, err := v2.InitializeFromProto(ðpb.BeaconStateAltair{
|
||||
Validators: validators,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, state.SetCurrentSyncCommittee(syncCommittee))
|
||||
require.NoError(t, state.SetNextSyncCommittee(syncCommittee))
|
||||
|
||||
ok, err := IsCurrentPeriodSyncCommittee(state, 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, ok)
|
||||
}
|
||||
|
||||
func TestIsCurrentEpochSyncCommittee_DoesNotExist(t *testing.T) {
|
||||
validators := make([]*ethpb.Validator, params.BeaconConfig().SyncCommitteeSize)
|
||||
syncCommittee := ðpb.SyncCommittee{
|
||||
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
|
||||
}
|
||||
for i := 0; i < len(validators); i++ {
|
||||
k := make([]byte, 48)
|
||||
copy(k, strconv.Itoa(i))
|
||||
validators[i] = ðpb.Validator{
|
||||
PublicKey: k,
|
||||
}
|
||||
syncCommittee.Pubkeys = append(syncCommittee.Pubkeys, bytesutil.PadTo(k, 48))
|
||||
}
|
||||
|
||||
state, err := v2.InitializeFromProto(ðpb.BeaconStateAltair{
|
||||
Validators: validators,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, state.SetCurrentSyncCommittee(syncCommittee))
|
||||
require.NoError(t, state.SetNextSyncCommittee(syncCommittee))
|
||||
|
||||
ok, err := IsCurrentPeriodSyncCommittee(state, 12390192)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, ok)
|
||||
}
|
||||
|
||||
func TestIsNextEpochSyncCommittee_UsingCache(t *testing.T) {
|
||||
validators := make([]*ethpb.Validator, params.BeaconConfig().SyncCommitteeSize)
|
||||
syncCommittee := ðpb.SyncCommittee{
|
||||
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
|
||||
}
|
||||
for i := 0; i < len(validators); i++ {
|
||||
k := make([]byte, 48)
|
||||
copy(k, strconv.Itoa(i))
|
||||
validators[i] = ðpb.Validator{
|
||||
PublicKey: k,
|
||||
}
|
||||
syncCommittee.Pubkeys = append(syncCommittee.Pubkeys, bytesutil.PadTo(k, 48))
|
||||
}
|
||||
|
||||
state, err := v2.InitializeFromProto(ðpb.BeaconStateAltair{
|
||||
Validators: validators,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, state.SetCurrentSyncCommittee(syncCommittee))
|
||||
require.NoError(t, state.SetNextSyncCommittee(syncCommittee))
|
||||
|
||||
ClearCache()
|
||||
r := [32]byte{'a'}
|
||||
require.NoError(t, err, syncCommitteeCache.UpdatePositionsInCommittee(r, state))
|
||||
|
||||
ok, err := IsNextPeriodSyncCommittee(state, 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, ok)
|
||||
}
|
||||
|
||||
func TestIsNextEpochSyncCommittee_UsingCommittee(t *testing.T) {
|
||||
validators := make([]*ethpb.Validator, params.BeaconConfig().SyncCommitteeSize)
|
||||
syncCommittee := ðpb.SyncCommittee{
|
||||
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
|
||||
}
|
||||
for i := 0; i < len(validators); i++ {
|
||||
k := make([]byte, 48)
|
||||
copy(k, strconv.Itoa(i))
|
||||
validators[i] = ðpb.Validator{
|
||||
PublicKey: k,
|
||||
}
|
||||
syncCommittee.Pubkeys = append(syncCommittee.Pubkeys, bytesutil.PadTo(k, 48))
|
||||
}
|
||||
|
||||
state, err := v2.InitializeFromProto(ðpb.BeaconStateAltair{
|
||||
Validators: validators,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, state.SetCurrentSyncCommittee(syncCommittee))
|
||||
require.NoError(t, state.SetNextSyncCommittee(syncCommittee))
|
||||
|
||||
ok, err := IsNextPeriodSyncCommittee(state, 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, ok)
|
||||
}
|
||||
|
||||
func TestIsNextEpochSyncCommittee_DoesNotExist(t *testing.T) {
|
||||
validators := make([]*ethpb.Validator, params.BeaconConfig().SyncCommitteeSize)
|
||||
syncCommittee := ðpb.SyncCommittee{
|
||||
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
|
||||
}
|
||||
for i := 0; i < len(validators); i++ {
|
||||
k := make([]byte, 48)
|
||||
copy(k, strconv.Itoa(i))
|
||||
validators[i] = ðpb.Validator{
|
||||
PublicKey: k,
|
||||
}
|
||||
syncCommittee.Pubkeys = append(syncCommittee.Pubkeys, bytesutil.PadTo(k, 48))
|
||||
}
|
||||
|
||||
state, err := v2.InitializeFromProto(ðpb.BeaconStateAltair{
|
||||
Validators: validators,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, state.SetCurrentSyncCommittee(syncCommittee))
|
||||
require.NoError(t, state.SetNextSyncCommittee(syncCommittee))
|
||||
|
||||
ok, err := IsNextPeriodSyncCommittee(state, 120391029)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, ok)
|
||||
}
|
||||
|
||||
func TestCurrentEpochSyncSubcommitteeIndices_UsingCache(t *testing.T) {
|
||||
validators := make([]*ethpb.Validator, params.BeaconConfig().SyncCommitteeSize)
|
||||
syncCommittee := ðpb.SyncCommittee{
|
||||
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
|
||||
}
|
||||
for i := 0; i < len(validators); i++ {
|
||||
k := make([]byte, 48)
|
||||
copy(k, strconv.Itoa(i))
|
||||
validators[i] = ðpb.Validator{
|
||||
PublicKey: k,
|
||||
}
|
||||
syncCommittee.Pubkeys = append(syncCommittee.Pubkeys, bytesutil.PadTo(k, 48))
|
||||
}
|
||||
|
||||
state, err := v2.InitializeFromProto(ðpb.BeaconStateAltair{
|
||||
Validators: validators,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, state.SetCurrentSyncCommittee(syncCommittee))
|
||||
require.NoError(t, state.SetNextSyncCommittee(syncCommittee))
|
||||
|
||||
ClearCache()
|
||||
r := [32]byte{'a'}
|
||||
require.NoError(t, err, syncCommitteeCache.UpdatePositionsInCommittee(r, state))
|
||||
|
||||
index, err := CurrentPeriodSyncSubcommitteeIndices(state, 0)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, []types.CommitteeIndex{0}, index)
|
||||
}
|
||||
|
||||
func TestCurrentEpochSyncSubcommitteeIndices_UsingCommittee(t *testing.T) {
|
||||
validators := make([]*ethpb.Validator, params.BeaconConfig().SyncCommitteeSize)
|
||||
syncCommittee := ðpb.SyncCommittee{
|
||||
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
|
||||
}
|
||||
for i := 0; i < len(validators); i++ {
|
||||
k := make([]byte, 48)
|
||||
copy(k, strconv.Itoa(i))
|
||||
validators[i] = ðpb.Validator{
|
||||
PublicKey: k,
|
||||
}
|
||||
syncCommittee.Pubkeys = append(syncCommittee.Pubkeys, bytesutil.PadTo(k, 48))
|
||||
}
|
||||
|
||||
state, err := v2.InitializeFromProto(ðpb.BeaconStateAltair{
|
||||
Validators: validators,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, state.SetCurrentSyncCommittee(syncCommittee))
|
||||
require.NoError(t, state.SetNextSyncCommittee(syncCommittee))
|
||||
|
||||
root, err := syncPeriodBoundaryRoot(state)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test that cache was empty.
|
||||
_, err = syncCommitteeCache.CurrentPeriodIndexPosition(bytesutil.ToBytes32(root), 0)
|
||||
require.Equal(t, cache.ErrNonExistingSyncCommitteeKey, err)
|
||||
|
||||
// Test that helper can retrieve the index given empty cache.
|
||||
index, err := CurrentPeriodSyncSubcommitteeIndices(state, 0)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, []types.CommitteeIndex{0}, index)
|
||||
|
||||
// Test that cache was able to fill on miss.
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
index, err = syncCommitteeCache.CurrentPeriodIndexPosition(bytesutil.ToBytes32(root), 0)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, []types.CommitteeIndex{0}, index)
|
||||
}
|
||||
|
||||
func TestCurrentEpochSyncSubcommitteeIndices_DoesNotExist(t *testing.T) {
|
||||
ClearCache()
|
||||
validators := make([]*ethpb.Validator, params.BeaconConfig().SyncCommitteeSize)
|
||||
syncCommittee := ðpb.SyncCommittee{
|
||||
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
|
||||
}
|
||||
for i := 0; i < len(validators); i++ {
|
||||
k := make([]byte, 48)
|
||||
copy(k, strconv.Itoa(i))
|
||||
validators[i] = ðpb.Validator{
|
||||
PublicKey: k,
|
||||
}
|
||||
syncCommittee.Pubkeys = append(syncCommittee.Pubkeys, bytesutil.PadTo(k, 48))
|
||||
}
|
||||
|
||||
state, err := v2.InitializeFromProto(ðpb.BeaconStateAltair{
|
||||
Validators: validators,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, state.SetCurrentSyncCommittee(syncCommittee))
|
||||
require.NoError(t, state.SetNextSyncCommittee(syncCommittee))
|
||||
|
||||
index, err := CurrentPeriodSyncSubcommitteeIndices(state, 129301923)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, []types.CommitteeIndex(nil), index)
|
||||
}
|
||||
|
||||
func TestNextEpochSyncSubcommitteeIndices_UsingCache(t *testing.T) {
|
||||
validators := make([]*ethpb.Validator, params.BeaconConfig().SyncCommitteeSize)
|
||||
syncCommittee := ðpb.SyncCommittee{
|
||||
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
|
||||
}
|
||||
for i := 0; i < len(validators); i++ {
|
||||
k := make([]byte, 48)
|
||||
copy(k, strconv.Itoa(i))
|
||||
validators[i] = ðpb.Validator{
|
||||
PublicKey: k,
|
||||
}
|
||||
syncCommittee.Pubkeys = append(syncCommittee.Pubkeys, bytesutil.PadTo(k, 48))
|
||||
}
|
||||
|
||||
state, err := v2.InitializeFromProto(ðpb.BeaconStateAltair{
|
||||
Validators: validators,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, state.SetCurrentSyncCommittee(syncCommittee))
|
||||
require.NoError(t, state.SetNextSyncCommittee(syncCommittee))
|
||||
|
||||
ClearCache()
|
||||
r := [32]byte{'a'}
|
||||
require.NoError(t, err, syncCommitteeCache.UpdatePositionsInCommittee(r, state))
|
||||
|
||||
index, err := NextPeriodSyncSubcommitteeIndices(state, 0)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, []types.CommitteeIndex{0}, index)
|
||||
}
|
||||
|
||||
func TestNextEpochSyncSubcommitteeIndices_UsingCommittee(t *testing.T) {
|
||||
validators := make([]*ethpb.Validator, params.BeaconConfig().SyncCommitteeSize)
|
||||
syncCommittee := ðpb.SyncCommittee{
|
||||
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
|
||||
}
|
||||
for i := 0; i < len(validators); i++ {
|
||||
k := make([]byte, 48)
|
||||
copy(k, strconv.Itoa(i))
|
||||
validators[i] = ðpb.Validator{
|
||||
PublicKey: k,
|
||||
}
|
||||
syncCommittee.Pubkeys = append(syncCommittee.Pubkeys, bytesutil.PadTo(k, 48))
|
||||
}
|
||||
|
||||
state, err := v2.InitializeFromProto(ðpb.BeaconStateAltair{
|
||||
Validators: validators,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, state.SetCurrentSyncCommittee(syncCommittee))
|
||||
require.NoError(t, state.SetNextSyncCommittee(syncCommittee))
|
||||
|
||||
index, err := NextPeriodSyncSubcommitteeIndices(state, 0)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, []types.CommitteeIndex{0}, index)
|
||||
}
|
||||
|
||||
func TestNextEpochSyncSubcommitteeIndices_DoesNotExist(t *testing.T) {
|
||||
ClearCache()
|
||||
validators := make([]*ethpb.Validator, params.BeaconConfig().SyncCommitteeSize)
|
||||
syncCommittee := ðpb.SyncCommittee{
|
||||
AggregatePubkey: bytesutil.PadTo([]byte{}, params.BeaconConfig().BLSPubkeyLength),
|
||||
}
|
||||
for i := 0; i < len(validators); i++ {
|
||||
k := make([]byte, 48)
|
||||
copy(k, strconv.Itoa(i))
|
||||
validators[i] = ðpb.Validator{
|
||||
PublicKey: k,
|
||||
}
|
||||
syncCommittee.Pubkeys = append(syncCommittee.Pubkeys, bytesutil.PadTo(k, 48))
|
||||
}
|
||||
|
||||
state, err := v2.InitializeFromProto(ðpb.BeaconStateAltair{
|
||||
Validators: validators,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, state.SetCurrentSyncCommittee(syncCommittee))
|
||||
require.NoError(t, state.SetNextSyncCommittee(syncCommittee))
|
||||
|
||||
index, err := NextPeriodSyncSubcommitteeIndices(state, 21093019)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, []types.CommitteeIndex(nil), index)
|
||||
}
|
||||
|
||||
func TestUpdateSyncCommitteeCache_BadSlot(t *testing.T) {
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Slot: 1,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
err = UpdateSyncCommitteeCache(state)
|
||||
require.ErrorContains(t, "not at the end of the epoch to update cache", err)
|
||||
|
||||
state, err = v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Slot: params.BeaconConfig().SlotsPerEpoch - 1,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
err = UpdateSyncCommitteeCache(state)
|
||||
require.ErrorContains(t, "not at sync committee period boundary to update cache", err)
|
||||
}
|
||||
|
||||
func TestUpdateSyncCommitteeCache_BadRoot(t *testing.T) {
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Slot: types.Slot(params.BeaconConfig().EpochsPerSyncCommitteePeriod)*params.BeaconConfig().SlotsPerEpoch - 1,
|
||||
LatestBlockHeader: ðpb.BeaconBlockHeader{StateRoot: params.BeaconConfig().ZeroHash[:]},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
err = UpdateSyncCommitteeCache(state)
|
||||
require.ErrorContains(t, "zero hash state root can't be used to update cache", err)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
@@ -47,8 +49,23 @@ func TotalBalance(state state.ReadOnlyValidators, indices []types.ValidatorIndex
|
||||
// """
|
||||
// return get_total_balance(state, set(get_active_validator_indices(state, get_current_epoch(state))))
|
||||
func TotalActiveBalance(s state.ReadOnlyBeaconState) (uint64, error) {
|
||||
total := uint64(0)
|
||||
// Check if the active balance exists in cache.
|
||||
epoch := SlotToEpoch(s.Slot())
|
||||
|
||||
seed, err := Seed(s, epoch, params.BeaconConfig().DomainBeaconAttester)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not get seed")
|
||||
}
|
||||
activeBalance, err := committeeCache.ActiveBalance(seed)
|
||||
if err == nil {
|
||||
return activeBalance, nil
|
||||
}
|
||||
if err != cache.ErrNonCommitteeKey {
|
||||
return 0, errors.Wrap(err, "could not interface with committee cache")
|
||||
}
|
||||
|
||||
// Cache miss. Manually compute the active balance and fill the cache.
|
||||
total := uint64(0)
|
||||
if err := s.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error {
|
||||
if IsActiveValidatorUsingTrie(val, epoch) {
|
||||
total += val.EffectiveBalance()
|
||||
@@ -57,6 +74,7 @@ func TotalActiveBalance(s state.ReadOnlyBeaconState) (uint64, error) {
|
||||
}); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return total, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -4,16 +4,16 @@ import (
|
||||
"testing"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
statepb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
|
||||
func TestTotalBalance_OK(t *testing.T) {
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{Validators: []*ethpb.Validator{
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{Validators: []*ethpb.Validator{
|
||||
{EffectiveBalance: 27 * 1e9}, {EffectiveBalance: 28 * 1e9},
|
||||
{EffectiveBalance: 32 * 1e9}, {EffectiveBalance: 40 * 1e9},
|
||||
}})
|
||||
@@ -26,7 +26,7 @@ func TestTotalBalance_OK(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTotalBalance_ReturnsEffectiveBalanceIncrement(t *testing.T) {
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{Validators: []*ethpb.Validator{}})
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{Validators: []*ethpb.Validator{}})
|
||||
require.NoError(t, err)
|
||||
|
||||
balance := TotalBalance(state, []types.ValidatorIndex{})
|
||||
@@ -35,7 +35,7 @@ func TestTotalBalance_ReturnsEffectiveBalanceIncrement(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTotalActiveBalance_OK(t *testing.T) {
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{Validators: []*ethpb.Validator{
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{Validators: []*ethpb.Validator{
|
||||
{
|
||||
EffectiveBalance: 32 * 1e9,
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
@@ -55,6 +55,14 @@ func TestTotalActiveBalance_OK(t *testing.T) {
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Validate that cache miss to start with.
|
||||
epoch := SlotToEpoch(state.Slot())
|
||||
seed, err := Seed(state, epoch, params.BeaconConfig().DomainBeaconAttester)
|
||||
require.NoError(t, err)
|
||||
_, err = committeeCache.ActiveBalance(seed)
|
||||
require.Equal(t, cache.ErrNonCommitteeKey, err)
|
||||
|
||||
// Validate manual calculation passes.
|
||||
balance, err := TotalActiveBalance(state)
|
||||
assert.NoError(t, err)
|
||||
wanted := state.Validators()[0].EffectiveBalance + state.Validators()[1].EffectiveBalance +
|
||||
@@ -74,7 +82,7 @@ func TestGetBalance_OK(t *testing.T) {
|
||||
{i: 2, b: []uint64{0, 0, 0}},
|
||||
}
|
||||
for _, test := range tests {
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{Balances: test.b})
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{Balances: test.b})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.b[test.i], state.Balances()[test.i], "Incorrect Validator balance")
|
||||
}
|
||||
@@ -92,7 +100,7 @@ func TestIncreaseBalance_OK(t *testing.T) {
|
||||
{i: 2, b: []uint64{27 * 1e9, 28 * 1e9, 32 * 1e9}, nb: 33 * 1e9, eb: 65 * 1e9},
|
||||
}
|
||||
for _, test := range tests {
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Validators: []*ethpb.Validator{
|
||||
{EffectiveBalance: 4}, {EffectiveBalance: 4}, {EffectiveBalance: 4}},
|
||||
Balances: test.b,
|
||||
@@ -116,7 +124,7 @@ func TestDecreaseBalance_OK(t *testing.T) {
|
||||
{i: 3, b: []uint64{27 * 1e9, 28 * 1e9, 1, 28 * 1e9}, nb: 28 * 1e9, eb: 0},
|
||||
}
|
||||
for _, test := range tests {
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Validators: []*ethpb.Validator{
|
||||
{EffectiveBalance: 4}, {EffectiveBalance: 4}, {EffectiveBalance: 4}, {EffectiveBalance: 3}},
|
||||
Balances: test.b,
|
||||
@@ -179,7 +187,7 @@ func TestIsInInactivityLeak(t *testing.T) {
|
||||
assert.Equal(t, false, IsInInactivityLeak(prevEpoch, finalizedEpoch), "Wanted inactivity leak false")
|
||||
}
|
||||
|
||||
func buildState(slot types.Slot, validatorCount uint64) *statepb.BeaconState {
|
||||
func buildState(slot types.Slot, validatorCount uint64) *ethpb.BeaconState {
|
||||
validators := make([]*ethpb.Validator, validatorCount)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = ðpb.Validator{
|
||||
@@ -205,7 +213,7 @@ func buildState(slot types.Slot, validatorCount uint64) *statepb.BeaconState {
|
||||
for i := 0; i < len(latestRandaoMixes); i++ {
|
||||
latestRandaoMixes[i] = params.BeaconConfig().ZeroHash[:]
|
||||
}
|
||||
return &statepb.BeaconState{
|
||||
return ðpb.BeaconState{
|
||||
Slot: slot,
|
||||
Balances: validatorBalances,
|
||||
Validators: validators,
|
||||
|
||||
@@ -208,8 +208,8 @@ func PrevSlot(slot types.Slot) types.Slot {
|
||||
// Spec code:
|
||||
// def compute_sync_committee_period(epoch: Epoch) -> uint64:
|
||||
// return epoch // EPOCHS_PER_SYNC_COMMITTEE_PERIOD
|
||||
func SyncCommitteePeriod(e types.Epoch) uint64 {
|
||||
return uint64(e / params.BeaconConfig().EpochsPerSyncCommitteePeriod)
|
||||
func SyncCommitteePeriod(epoch types.Epoch) uint64 {
|
||||
return uint64(epoch / params.BeaconConfig().EpochsPerSyncCommitteePeriod)
|
||||
}
|
||||
|
||||
// SyncCommitteePeriodStartEpoch returns the start epoch of a sync committee period.
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
statepb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
@@ -45,7 +44,7 @@ func TestIsActiveValidatorUsingTrie_OK(t *testing.T) {
|
||||
{a: 64, b: true},
|
||||
}
|
||||
val := ðpb.Validator{ActivationEpoch: 10, ExitEpoch: 100}
|
||||
beaconState, err := v1.InitializeFromProto(&statepb.BeaconState{Validators: []*ethpb.Validator{val}})
|
||||
beaconState, err := v1.InitializeFromProto(ðpb.BeaconState{Validators: []*ethpb.Validator{val}})
|
||||
require.NoError(t, err)
|
||||
for _, test := range tests {
|
||||
readOnlyVal, err := beaconState.ValidatorAtIndexReadOnly(0)
|
||||
@@ -210,7 +209,7 @@ func TestIsSlashableValidatorUsingTrie_OK(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
beaconState, err := v1.InitializeFromProto(&statepb.BeaconState{Validators: []*ethpb.Validator{test.validator}})
|
||||
beaconState, err := v1.InitializeFromProto(ðpb.BeaconState{Validators: []*ethpb.Validator{test.validator}})
|
||||
require.NoError(t, err)
|
||||
readOnlyVal, err := beaconState.ValidatorAtIndexReadOnly(0)
|
||||
require.NoError(t, err)
|
||||
@@ -234,7 +233,7 @@ func TestBeaconProposerIndex_OK(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Validators: validators,
|
||||
Slot: 0,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
@@ -293,7 +292,7 @@ func TestBeaconProposerIndex_BadState(t *testing.T) {
|
||||
roots[i] = make([]byte, 32)
|
||||
}
|
||||
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Validators: validators,
|
||||
Slot: 0,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
@@ -317,7 +316,7 @@ func TestComputeProposerIndex_Compatibility(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
state, err := v1.InitializeFromProto(&statepb.BeaconState{
|
||||
state, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
})
|
||||
@@ -364,7 +363,7 @@ func TestActiveValidatorCount_Genesis(t *testing.T) {
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
}
|
||||
}
|
||||
beaconState, err := v1.InitializeFromProto(&statepb.BeaconState{
|
||||
beaconState, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Slot: 0,
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
@@ -400,7 +399,7 @@ func TestChurnLimit_OK(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
beaconState, err := v1.InitializeFromProto(&statepb.BeaconState{
|
||||
beaconState, err := v1.InitializeFromProto(ðpb.BeaconState{
|
||||
Slot: 1,
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
@@ -415,8 +414,8 @@ func TestChurnLimit_OK(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDomain_OK(t *testing.T) {
|
||||
state := &statepb.BeaconState{
|
||||
Fork: &statepb.Fork{
|
||||
state := ðpb.BeaconState{
|
||||
Fork: ðpb.Fork{
|
||||
Epoch: 3,
|
||||
PreviousVersion: []byte{0, 0, 0, 2},
|
||||
CurrentVersion: []byte{0, 0, 0, 3},
|
||||
@@ -445,7 +444,7 @@ func TestDomain_OK(t *testing.T) {
|
||||
func TestActiveValidatorIndices(t *testing.T) {
|
||||
farFutureEpoch := params.BeaconConfig().FarFutureEpoch
|
||||
type args struct {
|
||||
state *statepb.BeaconState
|
||||
state *ethpb.BeaconState
|
||||
epoch types.Epoch
|
||||
}
|
||||
tests := []struct {
|
||||
@@ -457,7 +456,7 @@ func TestActiveValidatorIndices(t *testing.T) {
|
||||
{
|
||||
name: "all_active_epoch_10",
|
||||
args: args{
|
||||
state: &statepb.BeaconState{
|
||||
state: ðpb.BeaconState{
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
Validators: []*ethpb.Validator{
|
||||
{
|
||||
@@ -481,7 +480,7 @@ func TestActiveValidatorIndices(t *testing.T) {
|
||||
{
|
||||
name: "some_active_epoch_10",
|
||||
args: args{
|
||||
state: &statepb.BeaconState{
|
||||
state: ðpb.BeaconState{
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
Validators: []*ethpb.Validator{
|
||||
{
|
||||
@@ -505,7 +504,7 @@ func TestActiveValidatorIndices(t *testing.T) {
|
||||
{
|
||||
name: "some_active_with_recent_new_epoch_10",
|
||||
args: args{
|
||||
state: &statepb.BeaconState{
|
||||
state: ðpb.BeaconState{
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
Validators: []*ethpb.Validator{
|
||||
{
|
||||
@@ -533,7 +532,7 @@ func TestActiveValidatorIndices(t *testing.T) {
|
||||
{
|
||||
name: "some_active_with_recent_new_epoch_10",
|
||||
args: args{
|
||||
state: &statepb.BeaconState{
|
||||
state: ðpb.BeaconState{
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
Validators: []*ethpb.Validator{
|
||||
{
|
||||
@@ -561,7 +560,7 @@ func TestActiveValidatorIndices(t *testing.T) {
|
||||
{
|
||||
name: "some_active_with_recent_new_epoch_10",
|
||||
args: args{
|
||||
state: &statepb.BeaconState{
|
||||
state: ðpb.BeaconState{
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
Validators: []*ethpb.Validator{
|
||||
{
|
||||
@@ -698,7 +697,7 @@ func TestComputeProposerIndex(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bState := &statepb.BeaconState{Validators: tt.args.validators}
|
||||
bState := ðpb.BeaconState{Validators: tt.args.validators}
|
||||
stTrie, err := v1.InitializeFromProtoUnsafe(bState)
|
||||
require.NoError(t, err)
|
||||
got, err := ComputeProposerIndex(stTrie, tt.args.indices, tt.args.seed)
|
||||
@@ -739,20 +738,20 @@ func TestIsIsEligibleForActivation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
validator *ethpb.Validator
|
||||
state *statepb.BeaconState
|
||||
state *ethpb.BeaconState
|
||||
want bool
|
||||
}{
|
||||
{"Eligible",
|
||||
ðpb.Validator{ActivationEligibilityEpoch: 1, ActivationEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
&statepb.BeaconState{FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 2}},
|
||||
ðpb.BeaconState{FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 2}},
|
||||
true},
|
||||
{"Not yet finalized",
|
||||
ðpb.Validator{ActivationEligibilityEpoch: 1, ActivationEpoch: params.BeaconConfig().FarFutureEpoch},
|
||||
&statepb.BeaconState{FinalizedCheckpoint: ðpb.Checkpoint{Root: make([]byte, 32)}},
|
||||
ðpb.BeaconState{FinalizedCheckpoint: ðpb.Checkpoint{Root: make([]byte, 32)}},
|
||||
false},
|
||||
{"Incorrect activation epoch",
|
||||
ðpb.Validator{ActivationEligibilityEpoch: 1},
|
||||
&statepb.BeaconState{FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 2}},
|
||||
ðpb.BeaconState{FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 2}},
|
||||
false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -192,6 +192,7 @@ func TestWeakSubjectivity_IsWithinWeakSubjectivityPeriod(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
helpers.ClearCache()
|
||||
got, err := helpers.IsWithinWeakSubjectivityPeriod(tt.epoch, tt.genWsState(), tt.genWsCheckpoint())
|
||||
if tt.wantedErr != "" {
|
||||
assert.Equal(t, false, got)
|
||||
|
||||
@@ -26,6 +26,7 @@ go_library(
|
||||
],
|
||||
deps = [
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/core/altair:go_default_library",
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/epoch:go_default_library",
|
||||
"//beacon-chain/core/epoch/precompute:go_default_library",
|
||||
@@ -43,6 +44,7 @@ go_library(
|
||||
"//shared/mathutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/traceutil:go_default_library",
|
||||
"//shared/version:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
|
||||
@@ -56,6 +58,7 @@ go_test(
|
||||
name = "go_default_test",
|
||||
size = "small",
|
||||
srcs = [
|
||||
"altair_transition_no_verify_sig_test.go",
|
||||
"benchmarks_test.go",
|
||||
"skip_slot_cache_test.go",
|
||||
"state_fuzz_test.go",
|
||||
@@ -71,8 +74,10 @@ go_test(
|
||||
embed = [":go_default_library"],
|
||||
shard_count = 3,
|
||||
deps = [
|
||||
"//beacon-chain/core/altair:go_default_library",
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/p2p/types:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/v1:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
@@ -81,6 +86,7 @@ go_test(
|
||||
"//shared/benchutil:go_default_library",
|
||||
"//shared/bls:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/copyutil:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
|
||||
221
beacon-chain/core/state/altair_transition_no_verify_sig_test.go
Normal file
221
beacon-chain/core/state/altair_transition_no_verify_sig_test.go
Normal file
@@ -0,0 +1,221 @@
|
||||
package state_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/altair"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
core "github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
||||
p2pType "github.com/prysmaticlabs/prysm/beacon-chain/p2p/types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/copyutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
|
||||
func TestExecuteAltairStateTransitionNoVerify_FullProcess(t *testing.T) {
|
||||
beaconState, privKeys := testutil.DeterministicGenesisStateAltair(t, 100)
|
||||
|
||||
syncCommittee, err := altair.NextSyncCommittee(context.Background(), beaconState)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconState.SetCurrentSyncCommittee(syncCommittee))
|
||||
|
||||
eth1Data := ðpb.Eth1Data{
|
||||
DepositCount: 100,
|
||||
DepositRoot: bytesutil.PadTo([]byte{2}, 32),
|
||||
BlockHash: make([]byte, 32),
|
||||
}
|
||||
require.NoError(t, beaconState.SetSlot(params.BeaconConfig().SlotsPerEpoch-1))
|
||||
e := beaconState.Eth1Data()
|
||||
e.DepositCount = 100
|
||||
require.NoError(t, beaconState.SetEth1Data(e))
|
||||
bh := beaconState.LatestBlockHeader()
|
||||
bh.Slot = beaconState.Slot()
|
||||
require.NoError(t, beaconState.SetLatestBlockHeader(bh))
|
||||
require.NoError(t, beaconState.SetEth1DataVotes([]*ethpb.Eth1Data{eth1Data}))
|
||||
|
||||
require.NoError(t, beaconState.SetSlot(beaconState.Slot()+1))
|
||||
epoch := helpers.CurrentEpoch(beaconState)
|
||||
randaoReveal, err := testutil.RandaoReveal(beaconState, epoch, privKeys)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconState.SetSlot(beaconState.Slot()-1))
|
||||
|
||||
nextSlotState, err := core.ProcessSlots(context.Background(), beaconState.Copy(), beaconState.Slot()+1)
|
||||
require.NoError(t, err)
|
||||
parentRoot, err := nextSlotState.LatestBlockHeader().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
proposerIdx, err := helpers.BeaconProposerIndex(nextSlotState)
|
||||
require.NoError(t, err)
|
||||
block := testutil.NewBeaconBlockAltair()
|
||||
block.Block.ProposerIndex = proposerIdx
|
||||
block.Block.Slot = beaconState.Slot() + 1
|
||||
block.Block.ParentRoot = parentRoot[:]
|
||||
block.Block.Body.RandaoReveal = randaoReveal
|
||||
block.Block.Body.Eth1Data = eth1Data
|
||||
|
||||
syncBits := bitfield.NewBitvector512()
|
||||
for i := range syncBits {
|
||||
syncBits[i] = 0xff
|
||||
}
|
||||
indices, err := altair.NextSyncCommitteeIndices(context.Background(), beaconState)
|
||||
require.NoError(t, err)
|
||||
h := copyutil.CopyBeaconBlockHeader(beaconState.LatestBlockHeader())
|
||||
prevStateRoot, err := beaconState.HashTreeRoot(context.Background())
|
||||
require.NoError(t, err)
|
||||
h.StateRoot = prevStateRoot[:]
|
||||
pbr, err := h.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
syncSigs := make([]bls.Signature, len(indices))
|
||||
for i, indice := range indices {
|
||||
b := p2pType.SSZBytes(pbr[:])
|
||||
sb, err := helpers.ComputeDomainAndSign(beaconState, helpers.CurrentEpoch(beaconState), &b, params.BeaconConfig().DomainSyncCommittee, privKeys[indice])
|
||||
require.NoError(t, err)
|
||||
sig, err := bls.SignatureFromBytes(sb)
|
||||
require.NoError(t, err)
|
||||
syncSigs[i] = sig
|
||||
}
|
||||
aggregatedSig := bls.AggregateSignatures(syncSigs).Marshal()
|
||||
syncAggregate := ðpb.SyncAggregate{
|
||||
SyncCommitteeBits: syncBits,
|
||||
SyncCommitteeSignature: aggregatedSig,
|
||||
}
|
||||
block.Block.Body.SyncAggregate = syncAggregate
|
||||
wsb, err := wrapper.WrappedAltairSignedBeaconBlock(block)
|
||||
require.NoError(t, err)
|
||||
stateRoot, err := core.CalculateStateRoot(context.Background(), beaconState, wsb)
|
||||
require.NoError(t, err)
|
||||
block.Block.StateRoot = stateRoot[:]
|
||||
|
||||
c := beaconState.Copy()
|
||||
sig, err := testutil.BlockSignatureAltair(c, block.Block, privKeys)
|
||||
require.NoError(t, err)
|
||||
block.Signature = sig.Marshal()
|
||||
|
||||
wsb, err = wrapper.WrappedAltairSignedBeaconBlock(block)
|
||||
require.NoError(t, err)
|
||||
set, _, err := core.ExecuteStateTransitionNoVerifyAnySig(context.Background(), beaconState, wsb)
|
||||
require.NoError(t, err)
|
||||
verified, err := set.Verify()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, verified, "Could not verify signature set")
|
||||
}
|
||||
|
||||
func TestExecuteAltairStateTransitionNoVerifySignature_CouldNotVerifyStateRoot(t *testing.T) {
|
||||
beaconState, privKeys := testutil.DeterministicGenesisStateAltair(t, 100)
|
||||
|
||||
syncCommittee, err := altair.NextSyncCommittee(context.Background(), beaconState)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconState.SetCurrentSyncCommittee(syncCommittee))
|
||||
|
||||
eth1Data := ðpb.Eth1Data{
|
||||
DepositCount: 100,
|
||||
DepositRoot: bytesutil.PadTo([]byte{2}, 32),
|
||||
BlockHash: make([]byte, 32),
|
||||
}
|
||||
require.NoError(t, beaconState.SetSlot(params.BeaconConfig().SlotsPerEpoch-1))
|
||||
e := beaconState.Eth1Data()
|
||||
e.DepositCount = 100
|
||||
require.NoError(t, beaconState.SetEth1Data(e))
|
||||
bh := beaconState.LatestBlockHeader()
|
||||
bh.Slot = beaconState.Slot()
|
||||
require.NoError(t, beaconState.SetLatestBlockHeader(bh))
|
||||
require.NoError(t, beaconState.SetEth1DataVotes([]*ethpb.Eth1Data{eth1Data}))
|
||||
|
||||
require.NoError(t, beaconState.SetSlot(beaconState.Slot()+1))
|
||||
epoch := helpers.CurrentEpoch(beaconState)
|
||||
randaoReveal, err := testutil.RandaoReveal(beaconState, epoch, privKeys)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, beaconState.SetSlot(beaconState.Slot()-1))
|
||||
|
||||
nextSlotState, err := core.ProcessSlots(context.Background(), beaconState.Copy(), beaconState.Slot()+1)
|
||||
require.NoError(t, err)
|
||||
parentRoot, err := nextSlotState.LatestBlockHeader().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
proposerIdx, err := helpers.BeaconProposerIndex(nextSlotState)
|
||||
require.NoError(t, err)
|
||||
block := testutil.NewBeaconBlockAltair()
|
||||
block.Block.ProposerIndex = proposerIdx
|
||||
block.Block.Slot = beaconState.Slot() + 1
|
||||
block.Block.ParentRoot = parentRoot[:]
|
||||
block.Block.Body.RandaoReveal = randaoReveal
|
||||
block.Block.Body.Eth1Data = eth1Data
|
||||
|
||||
syncBits := bitfield.NewBitvector512()
|
||||
for i := range syncBits {
|
||||
syncBits[i] = 0xff
|
||||
}
|
||||
indices, err := altair.NextSyncCommitteeIndices(context.Background(), beaconState)
|
||||
require.NoError(t, err)
|
||||
h := copyutil.CopyBeaconBlockHeader(beaconState.LatestBlockHeader())
|
||||
prevStateRoot, err := beaconState.HashTreeRoot(context.Background())
|
||||
require.NoError(t, err)
|
||||
h.StateRoot = prevStateRoot[:]
|
||||
pbr, err := h.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
syncSigs := make([]bls.Signature, len(indices))
|
||||
for i, indice := range indices {
|
||||
b := p2pType.SSZBytes(pbr[:])
|
||||
sb, err := helpers.ComputeDomainAndSign(beaconState, helpers.CurrentEpoch(beaconState), &b, params.BeaconConfig().DomainSyncCommittee, privKeys[indice])
|
||||
require.NoError(t, err)
|
||||
sig, err := bls.SignatureFromBytes(sb)
|
||||
require.NoError(t, err)
|
||||
syncSigs[i] = sig
|
||||
}
|
||||
aggregatedSig := bls.AggregateSignatures(syncSigs).Marshal()
|
||||
syncAggregate := ðpb.SyncAggregate{
|
||||
SyncCommitteeBits: syncBits,
|
||||
SyncCommitteeSignature: aggregatedSig,
|
||||
}
|
||||
block.Block.Body.SyncAggregate = syncAggregate
|
||||
|
||||
wsb, err := wrapper.WrappedAltairSignedBeaconBlock(block)
|
||||
require.NoError(t, err)
|
||||
stateRoot, err := core.CalculateStateRoot(context.Background(), beaconState, wsb)
|
||||
require.NoError(t, err)
|
||||
block.Block.StateRoot = stateRoot[:]
|
||||
|
||||
c := beaconState.Copy()
|
||||
sig, err := testutil.BlockSignatureAltair(c, block.Block, privKeys)
|
||||
require.NoError(t, err)
|
||||
block.Signature = sig.Marshal()
|
||||
|
||||
block.Block.StateRoot = bytesutil.PadTo([]byte{'a'}, 32)
|
||||
wsb, err = wrapper.WrappedAltairSignedBeaconBlock(block)
|
||||
require.NoError(t, err)
|
||||
_, _, err = core.ExecuteStateTransitionNoVerifyAnySig(context.Background(), beaconState, wsb)
|
||||
require.ErrorContains(t, "could not validate state root", err)
|
||||
}
|
||||
|
||||
func TestExecuteStateTransitionNoVerifyAnySig_PassesProcessingConditions(t *testing.T) {
|
||||
beaconState, block := createFullAltairBlockWithOperations(t)
|
||||
wsb, err := wrapper.WrappedAltairSignedBeaconBlock(block)
|
||||
require.NoError(t, err)
|
||||
set, _, err := core.ExecuteStateTransitionNoVerifyAnySig(context.Background(), beaconState, wsb)
|
||||
require.NoError(t, err)
|
||||
// Test Signature set verifies.
|
||||
verified, err := set.Verify()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, verified, "Could not verify signature set")
|
||||
}
|
||||
|
||||
func createFullAltairBlockWithOperations(t *testing.T) (state.BeaconStateAltair,
|
||||
*ethpb.SignedBeaconBlockAltair) {
|
||||
beaconState, privKeys := testutil.DeterministicGenesisStateAltair(t, 32)
|
||||
sCom, err := altair.NextSyncCommittee(context.Background(), beaconState)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, beaconState.SetCurrentSyncCommittee(sCom))
|
||||
tState := beaconState.Copy()
|
||||
blk, err := testutil.GenerateFullBlockAltair(tState, privKeys,
|
||||
&testutil.BlockGenConfig{NumAttestations: 1, NumVoluntaryExits: 0, NumDeposits: 0}, 1)
|
||||
require.NoError(t, err)
|
||||
|
||||
return beaconState, blk
|
||||
}
|
||||
@@ -19,7 +19,7 @@ var SkipSlotCache = cache.NewSkipSlotCache()
|
||||
// The key for skip slot cache is mixed between state root and state slot.
|
||||
// state root is in the mix to defend against different forks with same skip slots
|
||||
// to hit the same cache. We don't want beacon states mixed up between different chains.
|
||||
func cacheKey(ctx context.Context, state state.ReadOnlyBeaconState) ([32]byte, error) {
|
||||
func CacheKey(ctx context.Context, state state.ReadOnlyBeaconState) ([32]byte, error) {
|
||||
bh := state.LatestBlockHeader()
|
||||
if bh == nil {
|
||||
return [32]byte{}, errors.New("block head in state can't be nil")
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
||||
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
statepb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
@@ -42,7 +41,7 @@ func TestGenesisBeaconState_OK(t *testing.T) {
|
||||
|
||||
// Misc fields checks.
|
||||
assert.Equal(t, types.Slot(0), newState.Slot(), "Slot was not correctly initialized")
|
||||
if !proto.Equal(newState.Fork(), &statepb.Fork{
|
||||
if !proto.Equal(newState.Fork(), ðpb.Fork{
|
||||
PreviousVersion: genesisForkVersion,
|
||||
CurrentVersion: genesisForkVersion,
|
||||
Epoch: genesisEpoch,
|
||||
@@ -76,10 +75,10 @@ func TestGenesisBeaconState_OK(t *testing.T) {
|
||||
assert.DeepEqual(t, make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector), newState.Slashings(), "Slashings was not correctly initialized")
|
||||
currAtt, err := newState.CurrentEpochAttestations()
|
||||
require.NoError(t, err)
|
||||
assert.DeepSSZEqual(t, []*statepb.PendingAttestation{}, currAtt, "CurrentEpochAttestations was not correctly initialized")
|
||||
assert.DeepSSZEqual(t, []*ethpb.PendingAttestation{}, currAtt, "CurrentEpochAttestations was not correctly initialized")
|
||||
prevAtt, err := newState.CurrentEpochAttestations()
|
||||
require.NoError(t, err)
|
||||
assert.DeepSSZEqual(t, []*statepb.PendingAttestation{}, prevAtt, "PreviousEpochAttestations was not correctly initialized")
|
||||
assert.DeepSSZEqual(t, []*ethpb.PendingAttestation{}, prevAtt, "PreviousEpochAttestations was not correctly initialized")
|
||||
|
||||
zeroHash := params.BeaconConfig().ZeroHash[:]
|
||||
// History root checks.
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/altair"
|
||||
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
e "github.com/prysmaticlabs/prysm/beacon-chain/core/epoch"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch/precompute"
|
||||
@@ -21,6 +22,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/shared/mathutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/traceutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/version"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
@@ -221,7 +223,7 @@ func ProcessSlots(ctx context.Context, state state.BeaconState, slot types.Slot)
|
||||
}
|
||||
|
||||
highestSlot := state.Slot()
|
||||
key, err := cacheKey(ctx, state)
|
||||
key, err := CacheKey(ctx, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -272,16 +274,35 @@ func ProcessSlots(ctx context.Context, state state.BeaconState, slot types.Slot)
|
||||
return nil, errors.Wrap(err, "could not process slot")
|
||||
}
|
||||
if CanProcessEpoch(state) {
|
||||
state, err = ProcessEpochPrecompute(ctx, state)
|
||||
if err != nil {
|
||||
traceutil.AnnotateError(span, err)
|
||||
return nil, errors.Wrap(err, "could not process epoch with optimizations")
|
||||
switch state.Version() {
|
||||
case version.Phase0:
|
||||
state, err = ProcessEpochPrecompute(ctx, state)
|
||||
if err != nil {
|
||||
traceutil.AnnotateError(span, err)
|
||||
return nil, errors.Wrap(err, "could not process epoch with optimizations")
|
||||
}
|
||||
case version.Altair:
|
||||
state, err = altair.ProcessEpoch(ctx, state)
|
||||
if err != nil {
|
||||
traceutil.AnnotateError(span, err)
|
||||
return nil, errors.Wrap(err, "could not process epoch")
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("beacon state should have a version")
|
||||
}
|
||||
}
|
||||
if err := state.SetSlot(state.Slot() + 1); err != nil {
|
||||
traceutil.AnnotateError(span, err)
|
||||
return nil, errors.Wrap(err, "failed to increment state slot")
|
||||
}
|
||||
|
||||
// Transition to Altair state.
|
||||
if helpers.IsEpochStart(state.Slot()) && helpers.SlotToEpoch(state.Slot()) == params.BeaconConfig().AltairForkEpoch {
|
||||
state, err = altair.UpgradeToAltair(ctx, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if highestSlot < state.Slot() {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/altair"
|
||||
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/interop"
|
||||
@@ -15,6 +16,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/traceutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/version"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
@@ -150,6 +152,16 @@ func CalculateStateRoot(
|
||||
if err != nil {
|
||||
return [32]byte{}, errors.Wrap(err, "could not process block")
|
||||
}
|
||||
if signed.Version() == version.Altair {
|
||||
sa, err := signed.Block().Body().SyncAggregate()
|
||||
if err != nil {
|
||||
return [32]byte{}, err
|
||||
}
|
||||
state, err = altair.ProcessSyncAggregate(state, sa)
|
||||
if err != nil {
|
||||
return [32]byte{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return state.HashTreeRoot(ctx)
|
||||
}
|
||||
@@ -182,6 +194,16 @@ func ProcessBlockNoVerifyAnySig(
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if signed.Version() == version.Altair {
|
||||
sa, err := signed.Block().Body().SyncAggregate()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
state, err = altair.ProcessSyncAggregate(state, sa)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
bSet, err := b.BlockSignatureSet(state, blk.ProposerIndex(), signed.Signature(), blk.HashTreeRoot)
|
||||
if err != nil {
|
||||
@@ -240,22 +262,22 @@ func ProcessOperationsNoVerifyAttsSigs(
|
||||
return nil, errors.Wrap(err, "could not verify operation lengths")
|
||||
}
|
||||
|
||||
state, err := b.ProcessProposerSlashings(ctx, state, signedBeaconBlock.Block().Body().ProposerSlashings(), v.SlashValidator)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process block proposer slashings")
|
||||
}
|
||||
state, err = b.ProcessAttesterSlashings(ctx, state, signedBeaconBlock.Block().Body().AttesterSlashings(), v.SlashValidator)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process block attester slashings")
|
||||
}
|
||||
state, err = b.ProcessAttestationsNoVerifySignature(ctx, state, signedBeaconBlock)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process block attestations")
|
||||
}
|
||||
state, err = b.ProcessDeposits(ctx, state, signedBeaconBlock.Block().Body().Deposits())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process block validator deposits")
|
||||
var err error
|
||||
switch signedBeaconBlock.Version() {
|
||||
case version.Phase0:
|
||||
state, err = phase0Operations(ctx, state, signedBeaconBlock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case version.Altair:
|
||||
state, err = altairOperations(ctx, state, signedBeaconBlock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("block does not have correct version")
|
||||
}
|
||||
|
||||
state, err = b.ProcessVoluntaryExits(ctx, state, signedBeaconBlock.Block().Body().VoluntaryExits())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process validator exits")
|
||||
@@ -309,3 +331,43 @@ func ProcessBlockForStateRoot(
|
||||
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// This calls altair specific block operations.
|
||||
func altairOperations(
|
||||
ctx context.Context,
|
||||
state state.BeaconState,
|
||||
signedBeaconBlock block.SignedBeaconBlock) (state.BeaconState, error) {
|
||||
state, err := b.ProcessProposerSlashings(ctx, state, signedBeaconBlock.Block().Body().ProposerSlashings(), altair.SlashValidator)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process block proposer slashings")
|
||||
}
|
||||
state, err = b.ProcessAttesterSlashings(ctx, state, signedBeaconBlock.Block().Body().AttesterSlashings(), altair.SlashValidator)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process block attester slashings")
|
||||
}
|
||||
state, err = altair.ProcessAttestationsNoVerifySignature(ctx, state, signedBeaconBlock)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process block attestations")
|
||||
}
|
||||
return altair.ProcessDeposits(ctx, state, signedBeaconBlock.Block().Body().Deposits())
|
||||
}
|
||||
|
||||
// This calls phase 0 specific block operations.
|
||||
func phase0Operations(
|
||||
ctx context.Context,
|
||||
state state.BeaconStateAltair,
|
||||
signedBeaconBlock block.SignedBeaconBlock) (state.BeaconState, error) {
|
||||
state, err := b.ProcessProposerSlashings(ctx, state, signedBeaconBlock.Block().Body().ProposerSlashings(), v.SlashValidator)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process block proposer slashings")
|
||||
}
|
||||
state, err = b.ProcessAttesterSlashings(ctx, state, signedBeaconBlock.Block().Body().AttesterSlashings(), v.SlashValidator)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process block attester slashings")
|
||||
}
|
||||
state, err = b.ProcessAttestationsNoVerifySignature(ctx, state, signedBeaconBlock)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process block attestations")
|
||||
}
|
||||
return b.ProcessDeposits(ctx, state, signedBeaconBlock.Block().Body().Deposits())
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
statepb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
"github.com/prysmaticlabs/prysm/shared/attestationutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
@@ -33,7 +32,7 @@ func init() {
|
||||
}
|
||||
|
||||
func TestExecuteStateTransition_IncorrectSlot(t *testing.T) {
|
||||
base := &statepb.BeaconState{
|
||||
base := ðpb.BeaconState{
|
||||
Slot: 5,
|
||||
}
|
||||
beaconState, err := v1.InitializeFromProto(base)
|
||||
@@ -243,7 +242,7 @@ func TestProcessBlock_IncorrectProcessExits(t *testing.T) {
|
||||
cp := beaconState.CurrentJustifiedCheckpoint()
|
||||
cp.Root = []byte("hello-world")
|
||||
require.NoError(t, beaconState.SetCurrentJustifiedCheckpoint(cp))
|
||||
require.NoError(t, beaconState.AppendCurrentEpochAttestations(&statepb.PendingAttestation{}))
|
||||
require.NoError(t, beaconState.AppendCurrentEpochAttestations(ðpb.PendingAttestation{}))
|
||||
_, err = core.VerifyOperationLengths(context.Background(), beaconState, wrapper.WrappedPhase0SignedBeaconBlock(block))
|
||||
wanted := "number of voluntary exits (17) in block body exceeds allowed threshold of 16"
|
||||
assert.ErrorContains(t, wanted, err)
|
||||
@@ -269,7 +268,7 @@ func createFullBlockWithOperations(t *testing.T) (state.BeaconState,
|
||||
copy(mockRoot[:], "hello-world")
|
||||
cp.Root = mockRoot[:]
|
||||
require.NoError(t, beaconState.SetCurrentJustifiedCheckpoint(cp))
|
||||
require.NoError(t, beaconState.AppendCurrentEpochAttestations(&statepb.PendingAttestation{}))
|
||||
require.NoError(t, beaconState.AppendCurrentEpochAttestations(ðpb.PendingAttestation{}))
|
||||
|
||||
proposerSlashIdx := types.ValidatorIndex(3)
|
||||
slotsPerEpoch := params.BeaconConfig().SlotsPerEpoch
|
||||
@@ -445,9 +444,9 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) {
|
||||
func TestProcessEpochPrecompute_CanProcess(t *testing.T) {
|
||||
epoch := types.Epoch(1)
|
||||
|
||||
atts := []*statepb.PendingAttestation{{Data: ðpb.AttestationData{Target: ðpb.Checkpoint{Root: make([]byte, 32)}}, InclusionDelay: 1}}
|
||||
atts := []*ethpb.PendingAttestation{{Data: ðpb.AttestationData{Target: ðpb.Checkpoint{Root: make([]byte, 32)}}, InclusionDelay: 1}}
|
||||
slashing := make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector)
|
||||
base := &statepb.BeaconState{
|
||||
base := ðpb.BeaconState{
|
||||
Slot: params.BeaconConfig().SlotsPerEpoch.Mul(uint64(epoch)) + 1,
|
||||
BlockRoots: make([][]byte, 128),
|
||||
Slashings: slashing,
|
||||
@@ -489,7 +488,7 @@ func BenchmarkProcessBlk_65536Validators_FullBlock(b *testing.B) {
|
||||
randaoMixes[i] = params.BeaconConfig().ZeroHash[:]
|
||||
}
|
||||
|
||||
base := &statepb.BeaconState{
|
||||
base := ðpb.BeaconState{
|
||||
Slot: 20,
|
||||
LatestBlockHeader: ðpb.BeaconBlockHeader{},
|
||||
BlockRoots: make([][]byte, 254),
|
||||
@@ -500,7 +499,7 @@ func BenchmarkProcessBlk_65536Validators_FullBlock(b *testing.B) {
|
||||
CurrentJustifiedCheckpoint: ðpb.Checkpoint{
|
||||
Root: []byte("hello-world"),
|
||||
},
|
||||
Fork: &statepb.Fork{
|
||||
Fork: ðpb.Fork{
|
||||
PreviousVersion: []byte{0, 0, 0, 0},
|
||||
CurrentVersion: []byte{0, 0, 0, 0},
|
||||
},
|
||||
@@ -567,7 +566,7 @@ func BenchmarkProcessBlk_65536Validators_FullBlock(b *testing.B) {
|
||||
binary.LittleEndian.PutUint64(buf, 0)
|
||||
domain, err := helpers.Domain(s.Fork(), 0, params.BeaconConfig().DomainRandao, s.GenesisValidatorRoot())
|
||||
require.NoError(b, err)
|
||||
ctr := &statepb.SigningData{
|
||||
ctr := ðpb.SigningData{
|
||||
ObjectRoot: buf,
|
||||
Domain: domain,
|
||||
}
|
||||
@@ -730,7 +729,7 @@ func TestCanProcessEpoch_TrueOnEpochs(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
b := &statepb.BeaconState{Slot: tt.slot}
|
||||
b := ðpb.BeaconState{Slot: tt.slot}
|
||||
s, err := v1.InitializeFromProto(b)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.canProcessEpoch, core.CanProcessEpoch(s), "CanProcessEpoch(%d)", tt.slot)
|
||||
@@ -797,7 +796,7 @@ func TestProcessBlock_OverMaxVoluntaryExits(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProcessBlock_IncorrectDeposits(t *testing.T) {
|
||||
base := &statepb.BeaconState{
|
||||
base := ðpb.BeaconState{
|
||||
Eth1Data: ðpb.Eth1Data{DepositCount: 100},
|
||||
Eth1DepositIndex: 98,
|
||||
}
|
||||
@@ -818,7 +817,7 @@ func TestProcessBlock_IncorrectDeposits(t *testing.T) {
|
||||
|
||||
func TestProcessSlots_SameSlotAsParentState(t *testing.T) {
|
||||
slot := types.Slot(2)
|
||||
parentState, err := v1.InitializeFromProto(&statepb.BeaconState{Slot: slot})
|
||||
parentState, err := v1.InitializeFromProto(ðpb.BeaconState{Slot: slot})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = core.ProcessSlots(context.Background(), parentState, slot)
|
||||
@@ -827,7 +826,7 @@ func TestProcessSlots_SameSlotAsParentState(t *testing.T) {
|
||||
|
||||
func TestProcessSlots_LowerSlotAsParentState(t *testing.T) {
|
||||
slot := types.Slot(2)
|
||||
parentState, err := v1.InitializeFromProto(&statepb.BeaconState{Slot: slot})
|
||||
parentState, err := v1.InitializeFromProto(ðpb.BeaconState{Slot: slot})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = core.ProcessSlots(context.Background(), parentState, slot-1)
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
statepb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
@@ -41,7 +40,7 @@ func TestHasVoted_OK(t *testing.T) {
|
||||
|
||||
func TestInitiateValidatorExit_AlreadyExited(t *testing.T) {
|
||||
exitEpoch := types.Epoch(199)
|
||||
base := &statepb.BeaconState{Validators: []*ethpb.Validator{{
|
||||
base := ðpb.BeaconState{Validators: []*ethpb.Validator{{
|
||||
ExitEpoch: exitEpoch},
|
||||
}}
|
||||
state, err := v1.InitializeFromProto(base)
|
||||
@@ -56,7 +55,7 @@ func TestInitiateValidatorExit_AlreadyExited(t *testing.T) {
|
||||
func TestInitiateValidatorExit_ProperExit(t *testing.T) {
|
||||
exitedEpoch := types.Epoch(100)
|
||||
idx := types.ValidatorIndex(3)
|
||||
base := &statepb.BeaconState{Validators: []*ethpb.Validator{
|
||||
base := ðpb.BeaconState{Validators: []*ethpb.Validator{
|
||||
{ExitEpoch: exitedEpoch},
|
||||
{ExitEpoch: exitedEpoch + 1},
|
||||
{ExitEpoch: exitedEpoch + 2},
|
||||
@@ -74,7 +73,7 @@ func TestInitiateValidatorExit_ProperExit(t *testing.T) {
|
||||
func TestInitiateValidatorExit_ChurnOverflow(t *testing.T) {
|
||||
exitedEpoch := types.Epoch(100)
|
||||
idx := types.ValidatorIndex(4)
|
||||
base := &statepb.BeaconState{Validators: []*ethpb.Validator{
|
||||
base := ðpb.BeaconState{Validators: []*ethpb.Validator{
|
||||
{ExitEpoch: exitedEpoch + 2},
|
||||
{ExitEpoch: exitedEpoch + 2},
|
||||
{ExitEpoch: exitedEpoch + 2},
|
||||
@@ -110,7 +109,7 @@ func TestSlashValidator_OK(t *testing.T) {
|
||||
balances = append(balances, params.BeaconConfig().MaxEffectiveBalance)
|
||||
}
|
||||
|
||||
base := &statepb.BeaconState{
|
||||
base := ðpb.BeaconState{
|
||||
Validators: registry,
|
||||
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
@@ -153,11 +152,11 @@ func TestSlashValidator_OK(t *testing.T) {
|
||||
|
||||
func TestActivatedValidatorIndices(t *testing.T) {
|
||||
tests := []struct {
|
||||
state *statepb.BeaconState
|
||||
state *ethpb.BeaconState
|
||||
wanted []types.ValidatorIndex
|
||||
}{
|
||||
{
|
||||
state: &statepb.BeaconState{
|
||||
state: ðpb.BeaconState{
|
||||
Validators: []*ethpb.Validator{
|
||||
{
|
||||
ActivationEpoch: 0,
|
||||
@@ -179,7 +178,7 @@ func TestActivatedValidatorIndices(t *testing.T) {
|
||||
wanted: []types.ValidatorIndex{0, 1, 3},
|
||||
},
|
||||
{
|
||||
state: &statepb.BeaconState{
|
||||
state: ðpb.BeaconState{
|
||||
Validators: []*ethpb.Validator{
|
||||
{
|
||||
ActivationEpoch: helpers.ActivationExitEpoch(10),
|
||||
@@ -189,7 +188,7 @@ func TestActivatedValidatorIndices(t *testing.T) {
|
||||
wanted: []types.ValidatorIndex{},
|
||||
},
|
||||
{
|
||||
state: &statepb.BeaconState{
|
||||
state: ðpb.BeaconState{
|
||||
Validators: []*ethpb.Validator{
|
||||
{
|
||||
ActivationEpoch: 0,
|
||||
@@ -210,11 +209,11 @@ func TestActivatedValidatorIndices(t *testing.T) {
|
||||
|
||||
func TestSlashedValidatorIndices(t *testing.T) {
|
||||
tests := []struct {
|
||||
state *statepb.BeaconState
|
||||
state *ethpb.BeaconState
|
||||
wanted []types.ValidatorIndex
|
||||
}{
|
||||
{
|
||||
state: &statepb.BeaconState{
|
||||
state: ðpb.BeaconState{
|
||||
Validators: []*ethpb.Validator{
|
||||
{
|
||||
WithdrawableEpoch: params.BeaconConfig().EpochsPerSlashingsVector,
|
||||
@@ -233,7 +232,7 @@ func TestSlashedValidatorIndices(t *testing.T) {
|
||||
wanted: []types.ValidatorIndex{0, 2},
|
||||
},
|
||||
{
|
||||
state: &statepb.BeaconState{
|
||||
state: ðpb.BeaconState{
|
||||
Validators: []*ethpb.Validator{
|
||||
{
|
||||
WithdrawableEpoch: params.BeaconConfig().EpochsPerSlashingsVector,
|
||||
@@ -243,7 +242,7 @@ func TestSlashedValidatorIndices(t *testing.T) {
|
||||
wanted: []types.ValidatorIndex{},
|
||||
},
|
||||
{
|
||||
state: &statepb.BeaconState{
|
||||
state: ðpb.BeaconState{
|
||||
Validators: []*ethpb.Validator{
|
||||
{
|
||||
WithdrawableEpoch: params.BeaconConfig().EpochsPerSlashingsVector,
|
||||
@@ -264,11 +263,11 @@ func TestSlashedValidatorIndices(t *testing.T) {
|
||||
|
||||
func TestExitedValidatorIndices(t *testing.T) {
|
||||
tests := []struct {
|
||||
state *statepb.BeaconState
|
||||
state *ethpb.BeaconState
|
||||
wanted []types.ValidatorIndex
|
||||
}{
|
||||
{
|
||||
state: &statepb.BeaconState{
|
||||
state: ðpb.BeaconState{
|
||||
Validators: []*ethpb.Validator{
|
||||
{
|
||||
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
||||
@@ -290,7 +289,7 @@ func TestExitedValidatorIndices(t *testing.T) {
|
||||
wanted: []types.ValidatorIndex{0, 2},
|
||||
},
|
||||
{
|
||||
state: &statepb.BeaconState{
|
||||
state: ðpb.BeaconState{
|
||||
Validators: []*ethpb.Validator{
|
||||
{
|
||||
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
||||
@@ -302,7 +301,7 @@ func TestExitedValidatorIndices(t *testing.T) {
|
||||
wanted: []types.ValidatorIndex{},
|
||||
},
|
||||
{
|
||||
state: &statepb.BeaconState{
|
||||
state: ðpb.BeaconState{
|
||||
Validators: []*ethpb.Validator{
|
||||
{
|
||||
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
||||
|
||||
@@ -3,6 +3,7 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"altair.go",
|
||||
"archived_point.go",
|
||||
"backup.go",
|
||||
"blocks.go",
|
||||
@@ -40,6 +41,7 @@ go_library(
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/genesis:go_default_library",
|
||||
"//beacon-chain/state/v1:go_default_library",
|
||||
"//beacon-chain/state/v2:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//proto/prysm/v1alpha1/block:go_default_library",
|
||||
"//proto/prysm/v1alpha1/wrapper:go_default_library",
|
||||
@@ -50,6 +52,7 @@ go_library(
|
||||
"//shared/progressutil:go_default_library",
|
||||
"//shared/sliceutil:go_default_library",
|
||||
"//shared/traceutil:go_default_library",
|
||||
"//shared/version:go_default_library",
|
||||
"@com_github_dgraph_io_ristretto//:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ferranbt_fastssz//:go_default_library",
|
||||
@@ -71,6 +74,7 @@ go_test(
|
||||
srcs = [
|
||||
"archived_point_test.go",
|
||||
"backup_test.go",
|
||||
"block_altair_test.go",
|
||||
"blocks_test.go",
|
||||
"checkpoint_test.go",
|
||||
"deposit_contract_test.go",
|
||||
|
||||
11
beacon-chain/db/kv/altair.go
Normal file
11
beacon-chain/db/kv/altair.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package kv
|
||||
|
||||
import "bytes"
|
||||
|
||||
// In order for an encoding to be Altair compatible, it must be prefixed with altair key.
|
||||
func hasAltairKey(enc []byte) bool {
|
||||
if len(altairKey) >= len(enc) {
|
||||
return false
|
||||
}
|
||||
return bytes.Equal(enc[:len(altairKey)], altairKey)
|
||||
}
|
||||
534
beacon-chain/db/kv/block_altair_test.go
Normal file
534
beacon-chain/db/kv/block_altair_test.go
Normal file
@@ -0,0 +1,534 @@
|
||||
package kv
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db/filters"
|
||||
v2 "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func TestStore_SaveAltairBlock_NoDuplicates(t *testing.T) {
|
||||
BlockCacheSize = 1
|
||||
db := setupDB(t)
|
||||
slot := types.Slot(20)
|
||||
ctx := context.Background()
|
||||
// First we save a previous block to ensure the cache max size is reached.
|
||||
prevBlock := testutil.NewBeaconBlockAltair()
|
||||
prevBlock.Block.Slot = slot - 1
|
||||
prevBlock.Block.ParentRoot = bytesutil.PadTo([]byte{1, 2, 3}, 32)
|
||||
wsb, err := wrapper.WrappedAltairSignedBeaconBlock(prevBlock)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveBlock(ctx, wsb))
|
||||
|
||||
block := testutil.NewBeaconBlockAltair()
|
||||
block.Block.Slot = slot
|
||||
block.Block.ParentRoot = bytesutil.PadTo([]byte{1, 2, 3}, 32)
|
||||
// Even with a full cache, saving new blocks should not cause
|
||||
// duplicated blocks in the DB.
|
||||
for i := 0; i < 100; i++ {
|
||||
wsb, err = wrapper.WrappedAltairSignedBeaconBlock(block)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveBlock(ctx, wsb))
|
||||
}
|
||||
f := filters.NewFilter().SetStartSlot(slot).SetEndSlot(slot)
|
||||
retrieved, _, err := db.Blocks(ctx, f)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, len(retrieved))
|
||||
// We reset the block cache size.
|
||||
BlockCacheSize = 256
|
||||
}
|
||||
|
||||
func TestStore_AltairBlocksCRUD(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
block := testutil.NewBeaconBlockAltair()
|
||||
block.Block.Slot = 20
|
||||
block.Block.ParentRoot = bytesutil.PadTo([]byte{1, 2, 3}, 32)
|
||||
|
||||
blockRoot, err := block.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
retrievedBlock, err := db.Block(ctx, blockRoot)
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, nil, retrievedBlock, "Expected nil block")
|
||||
wsb, err := wrapper.WrappedAltairSignedBeaconBlock(block)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveBlock(ctx, wsb))
|
||||
assert.Equal(t, true, db.HasBlock(ctx, blockRoot), "Expected block to exist in the db")
|
||||
retrievedBlock, err = db.Block(ctx, blockRoot)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, proto.Equal(block, retrievedBlock.Proto()), "Wanted: %v, received: %v", block, retrievedBlock)
|
||||
require.NoError(t, db.deleteBlock(ctx, blockRoot))
|
||||
assert.Equal(t, false, db.HasBlock(ctx, blockRoot), "Expected block to have been deleted from the db")
|
||||
}
|
||||
|
||||
func TestStore_AltairBlocksBatchDelete(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
ctx := context.Background()
|
||||
numBlocks := 10
|
||||
totalBlocks := make([]block.SignedBeaconBlock, numBlocks)
|
||||
blockRoots := make([][32]byte, 0)
|
||||
oddBlocks := make([]block.SignedBeaconBlock, 0)
|
||||
for i := 0; i < len(totalBlocks); i++ {
|
||||
b := testutil.NewBeaconBlockAltair()
|
||||
b.Block.Slot = types.Slot(i)
|
||||
b.Block.ParentRoot = bytesutil.PadTo([]byte("parent"), 32)
|
||||
wb, err := wrapper.WrappedAltairSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
totalBlocks[i] = wb
|
||||
if i%2 == 0 {
|
||||
r, err := totalBlocks[i].Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
blockRoots = append(blockRoots, r)
|
||||
} else {
|
||||
oddBlocks = append(oddBlocks, totalBlocks[i])
|
||||
}
|
||||
}
|
||||
require.NoError(t, db.SaveBlocks(ctx, totalBlocks))
|
||||
retrieved, _, err := db.Blocks(ctx, filters.NewFilter().SetParentRoot(bytesutil.PadTo([]byte("parent"), 32)))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, numBlocks, len(retrieved), "Unexpected number of blocks received")
|
||||
// We delete all even indexed blocks.
|
||||
require.NoError(t, db.deleteBlocks(ctx, blockRoots))
|
||||
// When we retrieve the data, only the odd indexed blocks should remain.
|
||||
retrieved, _, err = db.Blocks(ctx, filters.NewFilter().SetParentRoot(bytesutil.PadTo([]byte("parent"), 32)))
|
||||
require.NoError(t, err)
|
||||
sort.Slice(retrieved, func(i, j int) bool {
|
||||
return retrieved[i].Block().Slot() < retrieved[j].Block().Slot()
|
||||
})
|
||||
for i, block := range retrieved {
|
||||
assert.Equal(t, true, proto.Equal(block.Proto(), oddBlocks[i].Proto()), "Wanted: %v, received: %v", block, oddBlocks[i])
|
||||
}
|
||||
}
|
||||
|
||||
func TestStore_AltairBlocksHandleZeroCase(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
ctx := context.Background()
|
||||
numBlocks := 10
|
||||
totalBlocks := make([]block.SignedBeaconBlock, numBlocks)
|
||||
for i := 0; i < len(totalBlocks); i++ {
|
||||
b := testutil.NewBeaconBlockAltair()
|
||||
b.Block.Slot = types.Slot(i)
|
||||
b.Block.ParentRoot = bytesutil.PadTo([]byte("parent"), 32)
|
||||
wb, err := wrapper.WrappedAltairSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
totalBlocks[i] = wb
|
||||
_, err = totalBlocks[i].Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, db.SaveBlocks(ctx, totalBlocks))
|
||||
zeroFilter := filters.NewFilter().SetStartSlot(0).SetEndSlot(0)
|
||||
retrieved, _, err := db.Blocks(ctx, zeroFilter)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, len(retrieved), "Unexpected number of blocks received, expected one")
|
||||
}
|
||||
|
||||
func TestStore_AltairBlocksHandleInvalidEndSlot(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
ctx := context.Background()
|
||||
numBlocks := 10
|
||||
totalBlocks := make([]block.SignedBeaconBlock, numBlocks)
|
||||
// Save blocks from slot 1 onwards.
|
||||
for i := 0; i < len(totalBlocks); i++ {
|
||||
b := testutil.NewBeaconBlockAltair()
|
||||
b.Block.Slot = types.Slot(i) + 1
|
||||
b.Block.ParentRoot = bytesutil.PadTo([]byte("parent"), 32)
|
||||
wb, err := wrapper.WrappedAltairSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
totalBlocks[i] = wb
|
||||
_, err = totalBlocks[i].Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, db.SaveBlocks(ctx, totalBlocks))
|
||||
badFilter := filters.NewFilter().SetStartSlot(5).SetEndSlot(1)
|
||||
_, _, err := db.Blocks(ctx, badFilter)
|
||||
require.ErrorContains(t, errInvalidSlotRange.Error(), err)
|
||||
|
||||
goodFilter := filters.NewFilter().SetStartSlot(0).SetEndSlot(1)
|
||||
requested, _, err := db.Blocks(ctx, goodFilter)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, len(requested), "Unexpected number of blocks received, only expected two")
|
||||
}
|
||||
|
||||
func TestStore_AltairBlocksCRUD_NoCache(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
ctx := context.Background()
|
||||
block := testutil.NewBeaconBlockAltair()
|
||||
block.Block.Slot = 20
|
||||
block.Block.ParentRoot = bytesutil.PadTo([]byte{1, 2, 3}, 32)
|
||||
blockRoot, err := block.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
retrievedBlock, err := db.Block(ctx, blockRoot)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, nil, retrievedBlock, "Expected nil block")
|
||||
wsb, err := wrapper.WrappedAltairSignedBeaconBlock(block)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveBlock(ctx, wsb))
|
||||
db.blockCache.Del(string(blockRoot[:]))
|
||||
assert.Equal(t, true, db.HasBlock(ctx, blockRoot), "Expected block to exist in the db")
|
||||
retrievedBlock, err = db.Block(ctx, blockRoot)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, proto.Equal(block, retrievedBlock.Proto()), "Wanted: %v, received: %v", block, retrievedBlock)
|
||||
require.NoError(t, db.deleteBlock(ctx, blockRoot))
|
||||
assert.Equal(t, false, db.HasBlock(ctx, blockRoot), "Expected block to have been deleted from the db")
|
||||
}
|
||||
|
||||
func TestStore_AltairBlocks_FiltersCorrectly(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
b4 := testutil.NewBeaconBlockAltair()
|
||||
b4.Block.Slot = 4
|
||||
b4.Block.ParentRoot = bytesutil.PadTo([]byte("parent"), 32)
|
||||
b5 := testutil.NewBeaconBlockAltair()
|
||||
b5.Block.Slot = 5
|
||||
b5.Block.ParentRoot = bytesutil.PadTo([]byte("parent2"), 32)
|
||||
b6 := testutil.NewBeaconBlockAltair()
|
||||
b6.Block.Slot = 6
|
||||
b6.Block.ParentRoot = bytesutil.PadTo([]byte("parent2"), 32)
|
||||
b7 := testutil.NewBeaconBlockAltair()
|
||||
b7.Block.Slot = 7
|
||||
b7.Block.ParentRoot = bytesutil.PadTo([]byte("parent3"), 32)
|
||||
b8 := testutil.NewBeaconBlockAltair()
|
||||
b8.Block.Slot = 8
|
||||
b8.Block.ParentRoot = bytesutil.PadTo([]byte("parent4"), 32)
|
||||
blocks := make([]block.SignedBeaconBlock, 0)
|
||||
for _, b := range []*v2.SignedBeaconBlockAltair{b4, b5, b6, b7, b8} {
|
||||
blk, err := wrapper.WrappedAltairSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
blocks = append(blocks, blk)
|
||||
}
|
||||
ctx := context.Background()
|
||||
require.NoError(t, db.SaveBlocks(ctx, blocks))
|
||||
|
||||
tests := []struct {
|
||||
filter *filters.QueryFilter
|
||||
expectedNumBlocks int
|
||||
}{
|
||||
{
|
||||
filter: filters.NewFilter().SetParentRoot(bytesutil.PadTo([]byte("parent2"), 32)),
|
||||
expectedNumBlocks: 2,
|
||||
},
|
||||
{
|
||||
// No block meets the criteria below.
|
||||
filter: filters.NewFilter().SetParentRoot(bytesutil.PadTo([]byte{3, 4, 5}, 32)),
|
||||
expectedNumBlocks: 0,
|
||||
},
|
||||
{
|
||||
// Block slot range filter criteria.
|
||||
filter: filters.NewFilter().SetStartSlot(5).SetEndSlot(7),
|
||||
expectedNumBlocks: 3,
|
||||
},
|
||||
{
|
||||
filter: filters.NewFilter().SetStartSlot(7).SetEndSlot(7),
|
||||
expectedNumBlocks: 1,
|
||||
},
|
||||
{
|
||||
filter: filters.NewFilter().SetStartSlot(4).SetEndSlot(8),
|
||||
expectedNumBlocks: 5,
|
||||
},
|
||||
{
|
||||
filter: filters.NewFilter().SetStartSlot(4).SetEndSlot(5),
|
||||
expectedNumBlocks: 2,
|
||||
},
|
||||
{
|
||||
filter: filters.NewFilter().SetStartSlot(5).SetEndSlot(9),
|
||||
expectedNumBlocks: 4,
|
||||
},
|
||||
{
|
||||
filter: filters.NewFilter().SetEndSlot(7),
|
||||
expectedNumBlocks: 4,
|
||||
},
|
||||
{
|
||||
filter: filters.NewFilter().SetEndSlot(8),
|
||||
expectedNumBlocks: 5,
|
||||
},
|
||||
{
|
||||
filter: filters.NewFilter().SetStartSlot(5).SetEndSlot(10),
|
||||
expectedNumBlocks: 4,
|
||||
},
|
||||
{
|
||||
// Composite filter criteria.
|
||||
filter: filters.NewFilter().
|
||||
SetParentRoot(bytesutil.PadTo([]byte("parent2"), 32)).
|
||||
SetStartSlot(6).
|
||||
SetEndSlot(8),
|
||||
expectedNumBlocks: 1,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
retrievedBlocks, _, err := db.Blocks(ctx, tt.filter)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.expectedNumBlocks, len(retrievedBlocks), "Unexpected number of blocks")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStore_AltairBlocks_VerifyBlockRoots(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
db := setupDB(t)
|
||||
b1 := testutil.NewBeaconBlockAltair()
|
||||
b1.Block.Slot = 1
|
||||
r1, err := b1.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
b2 := testutil.NewBeaconBlockAltair()
|
||||
b2.Block.Slot = 2
|
||||
r2, err := b2.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, b := range []*v2.SignedBeaconBlockAltair{b1, b2} {
|
||||
wsb, err := wrapper.WrappedAltairSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveBlock(ctx, wsb))
|
||||
}
|
||||
|
||||
filter := filters.NewFilter().SetStartSlot(b1.Block.Slot).SetEndSlot(b2.Block.Slot)
|
||||
roots, err := db.BlockRoots(ctx, filter)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.DeepEqual(t, [][32]byte{r1, r2}, roots)
|
||||
}
|
||||
|
||||
func TestStore_AltairBlocks_Retrieve_SlotRange(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
totalBlocks := make([]block.SignedBeaconBlock, 500)
|
||||
for i := 0; i < 500; i++ {
|
||||
b := testutil.NewBeaconBlockAltair()
|
||||
b.Block.Slot = types.Slot(i)
|
||||
b.Block.ParentRoot = bytesutil.PadTo([]byte("parent"), 32)
|
||||
wb, err := wrapper.WrappedAltairSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
totalBlocks[i] = wb
|
||||
}
|
||||
ctx := context.Background()
|
||||
require.NoError(t, db.SaveBlocks(ctx, totalBlocks))
|
||||
retrieved, _, err := db.Blocks(ctx, filters.NewFilter().SetStartSlot(100).SetEndSlot(399))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 300, len(retrieved))
|
||||
}
|
||||
|
||||
func TestStore_AltairBlocks_Retrieve_Epoch(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
slots := params.BeaconConfig().SlotsPerEpoch.Mul(7)
|
||||
totalBlocks := make([]block.SignedBeaconBlock, slots)
|
||||
for i := types.Slot(0); i < slots; i++ {
|
||||
b := testutil.NewBeaconBlockAltair()
|
||||
b.Block.Slot = i
|
||||
b.Block.ParentRoot = bytesutil.PadTo([]byte("parent"), 32)
|
||||
wb, err := wrapper.WrappedAltairSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
totalBlocks[i] = wb
|
||||
}
|
||||
ctx := context.Background()
|
||||
require.NoError(t, db.SaveBlocks(ctx, totalBlocks))
|
||||
retrieved, _, err := db.Blocks(ctx, filters.NewFilter().SetStartEpoch(5).SetEndEpoch(6))
|
||||
require.NoError(t, err)
|
||||
want := params.BeaconConfig().SlotsPerEpoch.Mul(2)
|
||||
assert.Equal(t, uint64(want), uint64(len(retrieved)))
|
||||
retrieved, _, err = db.Blocks(ctx, filters.NewFilter().SetStartEpoch(0).SetEndEpoch(0))
|
||||
require.NoError(t, err)
|
||||
want = params.BeaconConfig().SlotsPerEpoch
|
||||
assert.Equal(t, uint64(want), uint64(len(retrieved)))
|
||||
}
|
||||
|
||||
func TestStore_AltairBlocks_Retrieve_SlotRangeWithStep(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
totalBlocks := make([]block.SignedBeaconBlock, 500)
|
||||
for i := 0; i < 500; i++ {
|
||||
b := testutil.NewBeaconBlockAltair()
|
||||
b.Block.Slot = types.Slot(i)
|
||||
b.Block.ParentRoot = bytesutil.PadTo([]byte("parent"), 32)
|
||||
wb, err := wrapper.WrappedAltairSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
totalBlocks[i] = wb
|
||||
}
|
||||
const step = 2
|
||||
ctx := context.Background()
|
||||
require.NoError(t, db.SaveBlocks(ctx, totalBlocks))
|
||||
retrieved, _, err := db.Blocks(ctx, filters.NewFilter().SetStartSlot(100).SetEndSlot(399).SetSlotStep(step))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 150, len(retrieved))
|
||||
for _, b := range retrieved {
|
||||
assert.Equal(t, types.Slot(0), (b.Block().Slot()-100)%step, "Unexpect block slot %d", b.Block().Slot())
|
||||
}
|
||||
}
|
||||
|
||||
func TestStore_SaveAltairBlock_CanGetHighestAt(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
block1 := testutil.NewBeaconBlockAltair()
|
||||
block1.Block.Slot = 1
|
||||
block2 := testutil.NewBeaconBlockAltair()
|
||||
block2.Block.Slot = 10
|
||||
block3 := testutil.NewBeaconBlockAltair()
|
||||
block3.Block.Slot = 100
|
||||
|
||||
for _, b := range []*v2.SignedBeaconBlockAltair{block1, block2, block3} {
|
||||
wsb, err := wrapper.WrappedAltairSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveBlock(ctx, wsb))
|
||||
}
|
||||
|
||||
highestAt, err := db.HighestSlotBlocksBelow(ctx, 2)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, false, len(highestAt) <= 0, "Got empty highest at slice")
|
||||
assert.Equal(t, true, proto.Equal(block1, highestAt[0].Proto()), "Wanted: %v, received: %v", block1, highestAt[0])
|
||||
highestAt, err = db.HighestSlotBlocksBelow(ctx, 11)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, false, len(highestAt) <= 0, "Got empty highest at slice")
|
||||
assert.Equal(t, true, proto.Equal(block2, highestAt[0].Proto()), "Wanted: %v, received: %v", block2, highestAt[0])
|
||||
highestAt, err = db.HighestSlotBlocksBelow(ctx, 101)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, false, len(highestAt) <= 0, "Got empty highest at slice")
|
||||
assert.Equal(t, true, proto.Equal(block3, highestAt[0].Proto()), "Wanted: %v, received: %v", block3, highestAt[0])
|
||||
|
||||
r3, err := block3.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.deleteBlock(ctx, r3))
|
||||
|
||||
highestAt, err = db.HighestSlotBlocksBelow(ctx, 101)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, proto.Equal(block2, highestAt[0].Proto()), "Wanted: %v, received: %v", block2, highestAt[0])
|
||||
}
|
||||
|
||||
func TestStore_GenesisAltairBlock_CanGetHighestAt(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
genesisBlock := testutil.NewBeaconBlockAltair()
|
||||
genesisRoot, err := genesisBlock.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveGenesisBlockRoot(ctx, genesisRoot))
|
||||
wsb, err := wrapper.WrappedAltairSignedBeaconBlock(genesisBlock)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveBlock(ctx, wsb))
|
||||
block1 := testutil.NewBeaconBlockAltair()
|
||||
block1.Block.Slot = 1
|
||||
wsb, err = wrapper.WrappedAltairSignedBeaconBlock(block1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveBlock(ctx, wsb))
|
||||
|
||||
highestAt, err := db.HighestSlotBlocksBelow(ctx, 2)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, proto.Equal(block1, highestAt[0].Proto()), "Wanted: %v, received: %v", block1, highestAt[0])
|
||||
highestAt, err = db.HighestSlotBlocksBelow(ctx, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, proto.Equal(genesisBlock, highestAt[0].Proto()), "Wanted: %v, received: %v", genesisBlock, highestAt[0])
|
||||
highestAt, err = db.HighestSlotBlocksBelow(ctx, 0)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, proto.Equal(genesisBlock, highestAt[0].Proto()), "Wanted: %v, received: %v", genesisBlock, highestAt[0])
|
||||
}
|
||||
|
||||
func TestStore_SaveAltairBlocks_HasCachedBlocks(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
var err error
|
||||
b := make([]block.SignedBeaconBlock, 500)
|
||||
for i := 0; i < 500; i++ {
|
||||
blk := testutil.NewBeaconBlockAltair()
|
||||
blk.Block.ParentRoot = bytesutil.PadTo([]byte("parent"), 32)
|
||||
blk.Block.Slot = types.Slot(i)
|
||||
b[i], err = wrapper.WrappedAltairSignedBeaconBlock(blk)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
require.NoError(t, db.SaveBlock(ctx, b[0]))
|
||||
require.NoError(t, db.SaveBlocks(ctx, b))
|
||||
f := filters.NewFilter().SetStartSlot(0).SetEndSlot(500)
|
||||
|
||||
blks, _, err := db.Blocks(ctx, f)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 500, len(blks), "Did not get wanted blocks")
|
||||
}
|
||||
|
||||
func TestStore_SaveAltairBlocks_HasRootsMatched(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
var err error
|
||||
b := make([]block.SignedBeaconBlock, 500)
|
||||
for i := 0; i < 500; i++ {
|
||||
blk := testutil.NewBeaconBlockAltair()
|
||||
blk.Block.ParentRoot = bytesutil.PadTo([]byte("parent"), 32)
|
||||
blk.Block.Slot = types.Slot(i)
|
||||
b[i], err = wrapper.WrappedAltairSignedBeaconBlock(blk)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
require.NoError(t, db.SaveBlocks(ctx, b))
|
||||
f := filters.NewFilter().SetStartSlot(0).SetEndSlot(500)
|
||||
|
||||
blks, roots, err := db.Blocks(ctx, f)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 500, len(blks), "Did not get wanted blocks")
|
||||
|
||||
for i, blk := range blks {
|
||||
rt, err := blk.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, roots[i], rt, "mismatch of block roots")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStore_AltairBlocksBySlot_BlockRootsBySlot(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
b1 := testutil.NewBeaconBlockAltair()
|
||||
b1.Block.Slot = 20
|
||||
b2 := testutil.NewBeaconBlockAltair()
|
||||
b2.Block.Slot = 100
|
||||
b2.Block.ParentRoot = bytesutil.PadTo([]byte("parent1"), 32)
|
||||
b3 := testutil.NewBeaconBlockAltair()
|
||||
b3.Block.Slot = 100
|
||||
b3.Block.ParentRoot = bytesutil.PadTo([]byte("parent2"), 32)
|
||||
|
||||
for _, b := range []*v2.SignedBeaconBlockAltair{b1, b2, b3} {
|
||||
wsb, err := wrapper.WrappedAltairSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveBlock(ctx, wsb))
|
||||
}
|
||||
|
||||
r1, err := b1.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
r2, err := b2.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
r3, err := b3.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
hasBlocks, retrievedBlocks, err := db.BlocksBySlot(ctx, 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, len(retrievedBlocks), "Unexpected number of blocks received, expected none")
|
||||
assert.Equal(t, false, hasBlocks, "Expected no blocks")
|
||||
hasBlocks, retrievedBlocks, err = db.BlocksBySlot(ctx, 20)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, proto.Equal(b1, retrievedBlocks[0].Proto()), "Wanted: %v, received: %v", b1, retrievedBlocks[0])
|
||||
assert.Equal(t, true, hasBlocks, "Expected to have blocks")
|
||||
hasBlocks, retrievedBlocks, err = db.BlocksBySlot(ctx, 100)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, proto.Equal(b2, retrievedBlocks[0].Proto()), "Wanted: %v, received: %v", b2, retrievedBlocks[0])
|
||||
assert.Equal(t, true, proto.Equal(b3, retrievedBlocks[1].Proto()), "Wanted: %v, received: %v", b3, retrievedBlocks[1])
|
||||
assert.Equal(t, true, hasBlocks, "Expected to have blocks")
|
||||
|
||||
hasBlockRoots, retrievedBlockRoots, err := db.BlockRootsBySlot(ctx, 1)
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, [][32]byte{}, retrievedBlockRoots)
|
||||
assert.Equal(t, false, hasBlockRoots, "Expected no block roots")
|
||||
hasBlockRoots, retrievedBlockRoots, err = db.BlockRootsBySlot(ctx, 20)
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, [][32]byte{r1}, retrievedBlockRoots)
|
||||
assert.Equal(t, true, hasBlockRoots, "Expected no block roots")
|
||||
hasBlockRoots, retrievedBlockRoots, err = db.BlockRootsBySlot(ctx, 100)
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, [][32]byte{r2, r3}, retrievedBlockRoots)
|
||||
assert.Equal(t, true, hasBlockRoots, "Expected no block roots")
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/snappy"
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
@@ -15,6 +16,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/sliceutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/version"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
@@ -30,24 +32,25 @@ func (s *Store) Block(ctx context.Context, blockRoot [32]byte) (block.SignedBeac
|
||||
if v, ok := s.blockCache.Get(string(blockRoot[:])); v != nil && ok {
|
||||
return v.(block.SignedBeaconBlock), nil
|
||||
}
|
||||
var block *ethpb.SignedBeaconBlock
|
||||
var block block.SignedBeaconBlock
|
||||
err := s.db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(blocksBucket)
|
||||
enc := bkt.Get(blockRoot[:])
|
||||
if enc == nil {
|
||||
return nil
|
||||
}
|
||||
block = ðpb.SignedBeaconBlock{}
|
||||
return decode(ctx, enc, block)
|
||||
var err error
|
||||
block, err = unmarshalBlock(ctx, enc)
|
||||
return err
|
||||
})
|
||||
return wrapper.WrappedPhase0SignedBeaconBlock(block), err
|
||||
return block, err
|
||||
}
|
||||
|
||||
// HeadBlock returns the latest canonical block in the Ethereum Beacon Chain.
|
||||
func (s *Store) HeadBlock(ctx context.Context) (block.SignedBeaconBlock, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.HeadBlock")
|
||||
defer span.End()
|
||||
var headBlock *ethpb.SignedBeaconBlock
|
||||
var headBlock block.SignedBeaconBlock
|
||||
err := s.db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(blocksBucket)
|
||||
headRoot := bkt.Get(headBlockRootKey)
|
||||
@@ -58,10 +61,11 @@ func (s *Store) HeadBlock(ctx context.Context) (block.SignedBeaconBlock, error)
|
||||
if enc == nil {
|
||||
return nil
|
||||
}
|
||||
headBlock = ðpb.SignedBeaconBlock{}
|
||||
return decode(ctx, enc, headBlock)
|
||||
var err error
|
||||
headBlock, err = unmarshalBlock(ctx, enc)
|
||||
return err
|
||||
})
|
||||
return wrapper.WrappedPhase0SignedBeaconBlock(headBlock), err
|
||||
return headBlock, err
|
||||
}
|
||||
|
||||
// Blocks retrieves a list of beacon blocks and its respective roots by filter criteria.
|
||||
@@ -81,11 +85,11 @@ func (s *Store) Blocks(ctx context.Context, f *filters.QueryFilter) ([]block.Sig
|
||||
|
||||
for i := 0; i < len(keys); i++ {
|
||||
encoded := bkt.Get(keys[i])
|
||||
block := ðpb.SignedBeaconBlock{}
|
||||
if err := decode(ctx, encoded, block); err != nil {
|
||||
block, err := unmarshalBlock(ctx, encoded)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
blocks = append(blocks, wrapper.WrappedPhase0SignedBeaconBlock(block))
|
||||
blocks = append(blocks, block)
|
||||
blockRoots = append(blockRoots, bytesutil.ToBytes32(keys[i]))
|
||||
}
|
||||
return nil
|
||||
@@ -153,11 +157,11 @@ func (s *Store) BlocksBySlot(ctx context.Context, slot types.Slot) (bool, []bloc
|
||||
|
||||
for i := 0; i < len(keys); i++ {
|
||||
encoded := bkt.Get(keys[i])
|
||||
block := ðpb.SignedBeaconBlock{}
|
||||
if err := decode(ctx, encoded, block); err != nil {
|
||||
block, err := unmarshalBlock(ctx, encoded)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
blocks = append(blocks, wrapper.WrappedPhase0SignedBeaconBlock(block))
|
||||
blocks = append(blocks, block)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -196,11 +200,11 @@ func (s *Store) deleteBlock(ctx context.Context, blockRoot [32]byte) error {
|
||||
if enc == nil {
|
||||
return nil
|
||||
}
|
||||
block := ðpb.SignedBeaconBlock{}
|
||||
if err := decode(ctx, enc, block); err != nil {
|
||||
block, err := unmarshalBlock(ctx, enc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
indicesByBucket := createBlockIndicesFromBlock(ctx, wrapper.WrappedPhase0BeaconBlock(block.Block))
|
||||
indicesByBucket := createBlockIndicesFromBlock(ctx, block.Block())
|
||||
if err := deleteValueForIndices(ctx, indicesByBucket, blockRoot[:], tx); err != nil {
|
||||
return errors.Wrap(err, "could not delete root for DB indices")
|
||||
}
|
||||
@@ -221,11 +225,11 @@ func (s *Store) deleteBlocks(ctx context.Context, blockRoots [][32]byte) error {
|
||||
if enc == nil {
|
||||
return nil
|
||||
}
|
||||
block := ðpb.SignedBeaconBlock{}
|
||||
if err := decode(ctx, enc, block); err != nil {
|
||||
block, err := unmarshalBlock(ctx, enc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
indicesByBucket := createBlockIndicesFromBlock(ctx, wrapper.WrappedPhase0BeaconBlock(block.Block))
|
||||
indicesByBucket := createBlockIndicesFromBlock(ctx, block.Block())
|
||||
if err := deleteValueForIndices(ctx, indicesByBucket, blockRoot[:], tx); err != nil {
|
||||
return errors.Wrap(err, "could not delete root for DB indices")
|
||||
}
|
||||
@@ -269,7 +273,7 @@ func (s *Store) SaveBlocks(ctx context.Context, blocks []block.SignedBeaconBlock
|
||||
if existingBlock := bkt.Get(blockRoot[:]); existingBlock != nil {
|
||||
continue
|
||||
}
|
||||
enc, err := encode(ctx, block.Proto())
|
||||
enc, err := marshalBlock(ctx, block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -307,7 +311,7 @@ func (s *Store) SaveHeadBlockRoot(ctx context.Context, blockRoot [32]byte) error
|
||||
func (s *Store) GenesisBlock(ctx context.Context) (block.SignedBeaconBlock, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.GenesisBlock")
|
||||
defer span.End()
|
||||
var block *ethpb.SignedBeaconBlock
|
||||
var block block.SignedBeaconBlock
|
||||
err := s.db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(blocksBucket)
|
||||
root := bkt.Get(genesisBlockRootKey)
|
||||
@@ -315,10 +319,11 @@ func (s *Store) GenesisBlock(ctx context.Context) (block.SignedBeaconBlock, erro
|
||||
if enc == nil {
|
||||
return nil
|
||||
}
|
||||
block = ðpb.SignedBeaconBlock{}
|
||||
return decode(ctx, enc, block)
|
||||
var err error
|
||||
block, err = unmarshalBlock(ctx, enc)
|
||||
return err
|
||||
})
|
||||
return wrapper.WrappedPhase0SignedBeaconBlock(block), err
|
||||
return block, err
|
||||
}
|
||||
|
||||
// SaveGenesisBlockRoot to the db.
|
||||
@@ -579,3 +584,46 @@ func createBlockIndicesFromFilters(ctx context.Context, f *filters.QueryFilter)
|
||||
}
|
||||
return indicesByBucket, nil
|
||||
}
|
||||
|
||||
// unmarshal block from marshaled proto beacon block bytes to versioned beacon block struct type.
|
||||
func unmarshalBlock(ctx context.Context, enc []byte) (block.SignedBeaconBlock, error) {
|
||||
var err error
|
||||
enc, err = snappy.Decode(nil, enc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch {
|
||||
case hasAltairKey(enc):
|
||||
// Marshal block bytes to altair beacon block.
|
||||
rawBlock := ðpb.SignedBeaconBlockAltair{}
|
||||
err := rawBlock.UnmarshalSSZ(enc[len(altairKey):])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return wrapper.WrappedAltairSignedBeaconBlock(rawBlock)
|
||||
default:
|
||||
// Marshal block bytes to phase 0 beacon block.
|
||||
rawBlock := ðpb.SignedBeaconBlock{}
|
||||
err = rawBlock.UnmarshalSSZ(enc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return wrapper.WrappedPhase0SignedBeaconBlock(rawBlock), nil
|
||||
}
|
||||
}
|
||||
|
||||
// marshal versioned beacon block from struct type down to bytes.
|
||||
func marshalBlock(ctx context.Context, blk block.SignedBeaconBlock) ([]byte, error) {
|
||||
obj, err := blk.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch blk.Version() {
|
||||
case version.Altair:
|
||||
return snappy.Encode(nil, append(altairKey, obj...)), nil
|
||||
case version.Phase0:
|
||||
return snappy.Encode(nil, obj), nil
|
||||
default:
|
||||
return nil, errors.New("Unknown block version")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db/filters"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
@@ -57,7 +56,7 @@ func TestStore_BlocksCRUD(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
retrievedBlock, err := db.Block(ctx, blockRoot)
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, (*ethpb.SignedBeaconBlock)(nil), retrievedBlock.Proto(), "Expected nil block")
|
||||
assert.DeepEqual(t, nil, retrievedBlock, "Expected nil block")
|
||||
require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(block)))
|
||||
assert.Equal(t, true, db.HasBlock(ctx, blockRoot), "Expected block to exist in the db")
|
||||
retrievedBlock, err = db.Block(ctx, blockRoot)
|
||||
@@ -173,7 +172,7 @@ func TestStore_BlocksCRUD_NoCache(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
retrievedBlock, err := db.Block(ctx, blockRoot)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, (*ethpb.SignedBeaconBlock)(nil), retrievedBlock.Proto(), "Expected nil block")
|
||||
require.DeepEqual(t, nil, retrievedBlock, "Expected nil block")
|
||||
require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(block)))
|
||||
db.blockCache.Del(string(blockRoot[:]))
|
||||
assert.Equal(t, true, db.HasBlock(ctx, blockRoot), "Expected block to exist in the db")
|
||||
|
||||
@@ -43,6 +43,9 @@ var (
|
||||
justifiedCheckpointKey = []byte("justified-checkpoint")
|
||||
finalizedCheckpointKey = []byte("finalized-checkpoint")
|
||||
powchainDataKey = []byte("powchain-data")
|
||||
// Altair key used to identify object is altair compatible.
|
||||
// Objects that are only compatible with altair should be prefixed with such key.
|
||||
altairKey = []byte("altair")
|
||||
|
||||
// Deprecated: This index key was migrated in PR 6461. Do not use, except for migrations.
|
||||
lastArchivedIndexKey = []byte("last-archived")
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state/genesis"
|
||||
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
|
||||
statepb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
v1alpha "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
v2 "github.com/prysmaticlabs/prysm/beacon-chain/state/v2"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
@@ -27,7 +27,6 @@ import (
|
||||
func (s *Store) State(ctx context.Context, blockRoot [32]byte) (state.BeaconState, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.State")
|
||||
defer span.End()
|
||||
var st *statepb.BeaconState
|
||||
enc, err := s.stateBytes(ctx, blockRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -36,18 +35,13 @@ func (s *Store) State(ctx context.Context, blockRoot [32]byte) (state.BeaconStat
|
||||
if len(enc) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// get the validator entries of the state
|
||||
valEntries, valErr := s.validatorEntries(ctx, blockRoot)
|
||||
if valErr != nil {
|
||||
return nil, valErr
|
||||
}
|
||||
|
||||
st, err = s.createState(ctx, enc, valEntries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return v1.InitializeFromProtoUnsafe(st)
|
||||
return s.unmarshalState(ctx, enc, valEntries)
|
||||
}
|
||||
|
||||
// GenesisState returns the genesis state in beacon chain.
|
||||
@@ -65,8 +59,8 @@ func (s *Store) GenesisState(ctx context.Context) (state.BeaconState, error) {
|
||||
return cached, nil
|
||||
}
|
||||
|
||||
var st *statepb.BeaconState
|
||||
if err = s.db.View(func(tx *bolt.Tx) error {
|
||||
var st state.BeaconState
|
||||
err = s.db.View(func(tx *bolt.Tx) error {
|
||||
// Retrieve genesis block's signing root from blocks bucket,
|
||||
// to look up what the genesis state is.
|
||||
bucket := tx.Bucket(blocksBucket)
|
||||
@@ -77,7 +71,6 @@ func (s *Store) GenesisState(ctx context.Context) (state.BeaconState, error) {
|
||||
if enc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// get the validator entries of the genesis state
|
||||
valEntries, valErr := s.validatorEntries(ctx, bytesutil.ToBytes32(genesisBlockRoot))
|
||||
if valErr != nil {
|
||||
@@ -85,15 +78,16 @@ func (s *Store) GenesisState(ctx context.Context) (state.BeaconState, error) {
|
||||
}
|
||||
|
||||
var crtErr error
|
||||
st, crtErr = s.createState(ctx, enc, valEntries)
|
||||
st, err = s.unmarshalState(ctx, enc, valEntries)
|
||||
return crtErr
|
||||
}); err != nil {
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if st == nil {
|
||||
if st == nil || st.IsNil() {
|
||||
return nil, nil
|
||||
}
|
||||
return v1.InitializeFromProtoUnsafe(st)
|
||||
return st, nil
|
||||
}
|
||||
|
||||
// SaveState stores a state to the db using block's signing root which was used to generate the state.
|
||||
@@ -119,14 +113,11 @@ func (s *Store) SaveStates(ctx context.Context, states []state.ReadOnlyBeaconSta
|
||||
}
|
||||
multipleEncs := make([][]byte, len(states))
|
||||
for i, st := range states {
|
||||
pbState, err := v1.ProtobufBeaconState(st.InnerStateUnsafe())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
multipleEncs[i], err = encode(ctx, pbState)
|
||||
stateBytes, err := marshalState(ctx, st)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
multipleEncs[i] = stateBytes
|
||||
}
|
||||
|
||||
return s.db.Update(func(tx *bolt.Tx) error {
|
||||
@@ -151,8 +142,8 @@ func (s *Store) SaveStatesEfficient(ctx context.Context, states []state.ReadOnly
|
||||
if states == nil {
|
||||
return errors.New("nil state")
|
||||
}
|
||||
validatorsEntries := make(map[string]*v1alpha.Validator) // It's a map to make sure that you store only new validator entries.
|
||||
validatorKeys := make([][]byte, len(states)) // For every state, this stores a compressed list of validator keys.
|
||||
validatorsEntries := make(map[string]*ethpb.Validator) // It's a map to make sure that you store only new validator entries.
|
||||
validatorKeys := make([][]byte, len(states)) // For every state, this stores a compressed list of validator keys.
|
||||
for i, st := range states {
|
||||
pbState, err := v1.ProtobufBeaconState(st.InnerStateUnsafe())
|
||||
if err != nil {
|
||||
@@ -195,7 +186,7 @@ func (s *Store) SaveStatesEfficient(ctx context.Context, states []state.ReadOnly
|
||||
return err
|
||||
}
|
||||
valEntries := pbState.Validators
|
||||
pbState.Validators = make([]*v1alpha.Validator, 0)
|
||||
pbState.Validators = make([]*ethpb.Validator, 0)
|
||||
encodedState, err := encode(ctx, pbState)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -270,9 +261,9 @@ func (s *Store) DeleteState(ctx context.Context, blockRoot [32]byte) error {
|
||||
|
||||
bkt = tx.Bucket(checkpointBucket)
|
||||
enc := bkt.Get(finalizedCheckpointKey)
|
||||
checkpoint := &v1alpha.Checkpoint{}
|
||||
checkpoint := ðpb.Checkpoint{}
|
||||
if enc == nil {
|
||||
checkpoint = &v1alpha.Checkpoint{Root: genesisBlockRoot}
|
||||
checkpoint = ðpb.Checkpoint{Root: genesisBlockRoot}
|
||||
} else if err := decode(ctx, enc, checkpoint); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -342,36 +333,86 @@ func (s *Store) DeleteStates(ctx context.Context, blockRoots [][32]byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// creates state from marshaled proto state bytes. Also add the validator entries retrieved
|
||||
// from the validator bucket and complete the state construction.
|
||||
func (s *Store) createState(ctx context.Context, enc []byte, validatorEntries []*v1alpha.Validator) (*statepb.BeaconState, error) {
|
||||
protoState := &statepb.BeaconState{}
|
||||
if err := decode(ctx, enc, protoState); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal encoding")
|
||||
}
|
||||
ok, err := s.isStateValidatorMigrationOver()
|
||||
// unmarshal state from marshaled proto state bytes to versioned state struct type.
|
||||
func (s *Store) unmarshalState(ctx context.Context, enc []byte, validatorEntries []*ethpb.Validator) (state.BeaconState, error) {
|
||||
var err error
|
||||
enc, err = snappy.Decode(nil, enc)
|
||||
if err != nil {
|
||||
return protoState, err
|
||||
return nil, err
|
||||
}
|
||||
if ok {
|
||||
protoState.Validators = validatorEntries
|
||||
|
||||
switch {
|
||||
case hasAltairKey(enc):
|
||||
// Marshal state bytes to altair beacon state.
|
||||
protoState := ðpb.BeaconStateAltair{}
|
||||
if err := protoState.UnmarshalSSZ(enc[len(altairKey):]); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal encoding for altair")
|
||||
}
|
||||
ok, err := s.isStateValidatorMigrationOver()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ok {
|
||||
protoState.Validators = validatorEntries
|
||||
}
|
||||
return v2.InitializeFromProtoUnsafe(protoState)
|
||||
default:
|
||||
// Marshal state bytes to phase 0 beacon state.
|
||||
protoState := ðpb.BeaconState{}
|
||||
if err := protoState.UnmarshalSSZ(enc); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal encoding")
|
||||
}
|
||||
ok, err := s.isStateValidatorMigrationOver()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ok {
|
||||
protoState.Validators = validatorEntries
|
||||
}
|
||||
return v1.InitializeFromProtoUnsafe(protoState)
|
||||
}
|
||||
}
|
||||
|
||||
// marshal versioned state from struct type down to bytes.
|
||||
func marshalState(ctx context.Context, st state.ReadOnlyBeaconState) ([]byte, error) {
|
||||
switch st.InnerStateUnsafe().(type) {
|
||||
case *ethpb.BeaconState:
|
||||
rState, ok := st.InnerStateUnsafe().(*ethpb.BeaconState)
|
||||
if !ok {
|
||||
return nil, errors.New("non valid inner state")
|
||||
}
|
||||
return encode(ctx, rState)
|
||||
case *ethpb.BeaconStateAltair:
|
||||
rState, ok := st.InnerStateUnsafe().(*ethpb.BeaconStateAltair)
|
||||
if !ok {
|
||||
return nil, errors.New("non valid inner state")
|
||||
}
|
||||
if rState == nil {
|
||||
return nil, errors.New("nil state")
|
||||
}
|
||||
rawObj, err := rState.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return snappy.Encode(nil, append(altairKey, rawObj...)), nil
|
||||
default:
|
||||
return nil, errors.New("invalid inner state")
|
||||
}
|
||||
return protoState, nil
|
||||
}
|
||||
|
||||
// Retrieve the validator entries for a given block root. These entries are stored in a
|
||||
// separate bucket to reduce state size.
|
||||
func (s *Store) validatorEntries(ctx context.Context, blockRoot [32]byte) ([]*v1alpha.Validator, error) {
|
||||
func (s *Store) validatorEntries(ctx context.Context, blockRoot [32]byte) ([]*ethpb.Validator, error) {
|
||||
ok, err := s.isStateValidatorMigrationOver()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
return make([]*v1alpha.Validator, 0), nil
|
||||
return make([]*ethpb.Validator, 0), nil
|
||||
}
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.validatorEntries")
|
||||
defer span.End()
|
||||
var validatorEntries []*v1alpha.Validator
|
||||
var validatorEntries []*ethpb.Validator
|
||||
err = s.db.View(func(tx *bolt.Tx) error {
|
||||
// get the validator keys from the index bucket
|
||||
idxBkt := tx.Bucket(blockRootValidatorHashesBucket)
|
||||
@@ -396,7 +437,7 @@ func (s *Store) validatorEntries(ctx context.Context, blockRoot [32]byte) ([]*v1
|
||||
// get the entry bytes from the cache or from the DB.
|
||||
v, ok := s.validatorEntryCache.Get(key)
|
||||
if ok {
|
||||
valEntry, vType := v.(*v1alpha.Validator)
|
||||
valEntry, vType := v.(*ethpb.Validator)
|
||||
if vType {
|
||||
s.validatorEntryCache.Set(key, valEntry, int64(valEntry.SizeSSZ()))
|
||||
validatorEntries = append(validatorEntries, valEntry)
|
||||
@@ -411,7 +452,7 @@ func (s *Store) validatorEntries(ctx context.Context, blockRoot [32]byte) ([]*v1
|
||||
if len(valEntryBytes) == 0 {
|
||||
return errors.New("could not find validator entry")
|
||||
}
|
||||
encValEntry := &v1alpha.Validator{}
|
||||
encValEntry := ðpb.Validator{}
|
||||
decodeErr := decode(ctx, valEntryBytes, encValEntry)
|
||||
if decodeErr != nil {
|
||||
return errors.Wrap(decodeErr, "failed to decode validator entry keys")
|
||||
@@ -425,7 +466,7 @@ func (s *Store) validatorEntries(ctx context.Context, blockRoot [32]byte) ([]*v1
|
||||
return validatorEntries, err
|
||||
}
|
||||
|
||||
/// retrieves and assembles the state information from multiple buckets.
|
||||
// retrieves and assembles the state information from multiple buckets.
|
||||
func (s *Store) stateBytes(ctx context.Context, blockRoot [32]byte) ([]byte, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.stateBytes")
|
||||
defer span.End()
|
||||
@@ -468,16 +509,16 @@ func (s *Store) slotByBlockRoot(ctx context.Context, tx *bolt.Tx, blockRoot []by
|
||||
return 0, errors.New("state enc can't be nil")
|
||||
}
|
||||
// no need to construct the validator entries as it is not used here.
|
||||
s, err := s.createState(ctx, enc, nil)
|
||||
s, err := s.unmarshalState(ctx, enc, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if s == nil {
|
||||
if s == nil || s.IsNil() {
|
||||
return 0, errors.New("state can't be nil")
|
||||
}
|
||||
return s.Slot, nil
|
||||
return s.Slot(), nil
|
||||
}
|
||||
b := &v1alpha.SignedBeaconBlock{}
|
||||
b := ðpb.SignedBeaconBlock{}
|
||||
err := decode(ctx, enc, b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -487,7 +528,7 @@ func (s *Store) slotByBlockRoot(ctx context.Context, tx *bolt.Tx, blockRoot []by
|
||||
}
|
||||
return b.Block.Slot, nil
|
||||
}
|
||||
stateSummary := &statepb.StateSummary{}
|
||||
stateSummary := ðpb.StateSummary{}
|
||||
if err := decode(ctx, enc, stateSummary); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -532,6 +532,48 @@ func TestStore_CleanUpDirtyStates_DontDeleteNonFinalized(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAltairState_CanSaveRetrieve(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
|
||||
r := [32]byte{'A'}
|
||||
|
||||
require.Equal(t, false, db.HasState(context.Background(), r))
|
||||
|
||||
st, _ := testutil.DeterministicGenesisStateAltair(t, 1)
|
||||
require.NoError(t, st.SetSlot(100))
|
||||
|
||||
require.NoError(t, db.SaveState(context.Background(), st, r))
|
||||
require.Equal(t, true, db.HasState(context.Background(), r))
|
||||
|
||||
savedS, err := db.State(context.Background(), r)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.DeepSSZEqual(t, st.InnerStateUnsafe(), savedS.InnerStateUnsafe())
|
||||
|
||||
savedS, err = db.State(context.Background(), [32]byte{'B'})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, state.ReadOnlyBeaconState(nil), savedS, "Unsaved state should've been nil")
|
||||
}
|
||||
|
||||
func TestAltairState_CanDelete(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
|
||||
r := [32]byte{'A'}
|
||||
|
||||
require.Equal(t, false, db.HasState(context.Background(), r))
|
||||
|
||||
st, _ := testutil.DeterministicGenesisStateAltair(t, 1)
|
||||
require.NoError(t, st.SetSlot(100))
|
||||
|
||||
require.NoError(t, db.SaveState(context.Background(), st, r))
|
||||
require.Equal(t, true, db.HasState(context.Background(), r))
|
||||
|
||||
require.NoError(t, db.DeleteState(context.Background(), r))
|
||||
savedS, err := db.State(context.Background(), r)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, state.ReadOnlyBeaconState(nil), savedS, "Unsaved state should've been nil")
|
||||
}
|
||||
|
||||
func validators(limit int) []*ethpb.Validator {
|
||||
var vals []*ethpb.Validator
|
||||
for i := 0; i < limit; i++ {
|
||||
|
||||
@@ -65,7 +65,7 @@ func DefaultConfig(enableDebugRPCEndpoints bool) MuxConfig {
|
||||
)
|
||||
v1Alpha1PbHandler := gateway.PbMux{
|
||||
Registrations: v1Alpha1Registrations,
|
||||
Patterns: []string{"/eth/v1alpha1/"},
|
||||
Patterns: []string{"/prysm/v1alpha1/"},
|
||||
Mux: v1Alpha1Mux,
|
||||
}
|
||||
v1PbHandler := gateway.PbMux{
|
||||
|
||||
@@ -16,7 +16,7 @@ func TestDefaultConfig(t *testing.T) {
|
||||
assert.Equal(t, 4, len(cfg.V1PbMux.Registrations))
|
||||
assert.NotNil(t, cfg.V1Alpha1PbMux.Mux)
|
||||
require.Equal(t, 1, len(cfg.V1Alpha1PbMux.Patterns))
|
||||
assert.Equal(t, "/eth/v1alpha1/", cfg.V1Alpha1PbMux.Patterns[0])
|
||||
assert.Equal(t, "/prysm/v1alpha1/", cfg.V1Alpha1PbMux.Patterns[0])
|
||||
assert.Equal(t, 4, len(cfg.V1Alpha1PbMux.Registrations))
|
||||
})
|
||||
|
||||
@@ -28,7 +28,7 @@ func TestDefaultConfig(t *testing.T) {
|
||||
assert.Equal(t, 5, len(cfg.V1PbMux.Registrations))
|
||||
assert.NotNil(t, cfg.V1Alpha1PbMux.Mux)
|
||||
require.Equal(t, 1, len(cfg.V1Alpha1PbMux.Patterns))
|
||||
assert.Equal(t, "/eth/v1alpha1/", cfg.V1Alpha1PbMux.Patterns[0])
|
||||
assert.Equal(t, "/prysm/v1alpha1/", cfg.V1Alpha1PbMux.Patterns[0])
|
||||
assert.Equal(t, 5, len(cfg.V1Alpha1PbMux.Registrations))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ go_library(
|
||||
"//beacon-chain/node/registration:go_default_library",
|
||||
"//beacon-chain/operations/attestations:go_default_library",
|
||||
"//beacon-chain/operations/slashings:go_default_library",
|
||||
"//beacon-chain/operations/synccommittee:go_default_library",
|
||||
"//beacon-chain/operations/voluntaryexits:go_default_library",
|
||||
"//beacon-chain/p2p:go_default_library",
|
||||
"//beacon-chain/powchain:go_default_library",
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/node/registration"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/operations/synccommittee"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/operations/voluntaryexits"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/powchain"
|
||||
@@ -59,23 +60,24 @@ const testSkipPowFlag = "test-skip-pow"
|
||||
// full PoS node. It handles the lifecycle of the entire system and registers
|
||||
// services to a service registry.
|
||||
type BeaconNode struct {
|
||||
cliCtx *cli.Context
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
services *shared.ServiceRegistry
|
||||
lock sync.RWMutex
|
||||
stop chan struct{} // Channel to wait for termination notifications.
|
||||
db db.Database
|
||||
attestationPool attestations.Pool
|
||||
exitPool voluntaryexits.PoolManager
|
||||
slashingsPool slashings.PoolManager
|
||||
depositCache *depositcache.DepositCache
|
||||
stateFeed *event.Feed
|
||||
blockFeed *event.Feed
|
||||
opFeed *event.Feed
|
||||
forkChoiceStore forkchoice.ForkChoicer
|
||||
stateGen *stategen.State
|
||||
collector *bcnodeCollector
|
||||
cliCtx *cli.Context
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
services *shared.ServiceRegistry
|
||||
lock sync.RWMutex
|
||||
stop chan struct{} // Channel to wait for termination notifications.
|
||||
db db.Database
|
||||
attestationPool attestations.Pool
|
||||
exitPool voluntaryexits.PoolManager
|
||||
slashingsPool slashings.PoolManager
|
||||
syncCommitteePool synccommittee.Pool
|
||||
depositCache *depositcache.DepositCache
|
||||
stateFeed *event.Feed
|
||||
blockFeed *event.Feed
|
||||
opFeed *event.Feed
|
||||
forkChoiceStore forkchoice.ForkChoicer
|
||||
stateGen *stategen.State
|
||||
collector *bcnodeCollector
|
||||
}
|
||||
|
||||
// New creates a new node instance, sets up configuration options, and registers
|
||||
@@ -95,21 +97,25 @@ func New(cliCtx *cli.Context) (*BeaconNode, error) {
|
||||
configureNetwork(cliCtx)
|
||||
configureInteropConfig(cliCtx)
|
||||
|
||||
// Initializes any forks here.
|
||||
params.BeaconConfig().InitializeForkSchedule()
|
||||
|
||||
registry := shared.NewServiceRegistry()
|
||||
|
||||
ctx, cancel := context.WithCancel(cliCtx.Context)
|
||||
beacon := &BeaconNode{
|
||||
cliCtx: cliCtx,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
services: registry,
|
||||
stop: make(chan struct{}),
|
||||
stateFeed: new(event.Feed),
|
||||
blockFeed: new(event.Feed),
|
||||
opFeed: new(event.Feed),
|
||||
attestationPool: attestations.NewPool(),
|
||||
exitPool: voluntaryexits.NewPool(),
|
||||
slashingsPool: slashings.NewPool(),
|
||||
cliCtx: cliCtx,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
services: registry,
|
||||
stop: make(chan struct{}),
|
||||
stateFeed: new(event.Feed),
|
||||
blockFeed: new(event.Feed),
|
||||
opFeed: new(event.Feed),
|
||||
attestationPool: attestations.NewPool(),
|
||||
exitPool: voluntaryexits.NewPool(),
|
||||
slashingsPool: slashings.NewPool(),
|
||||
syncCommitteePool: synccommittee.NewPool(),
|
||||
}
|
||||
|
||||
depositAddress, err := registration.DepositContractAddress()
|
||||
@@ -500,6 +506,7 @@ func (b *BeaconNode) registerSyncService() error {
|
||||
AttPool: b.attestationPool,
|
||||
ExitPool: b.exitPool,
|
||||
SlashingPool: b.slashingsPool,
|
||||
SyncCommsPool: b.syncCommitteePool,
|
||||
StateGen: b.stateGen,
|
||||
})
|
||||
|
||||
@@ -588,6 +595,7 @@ func (b *BeaconNode) registerRPCService() error {
|
||||
AttestationsPool: b.attestationPool,
|
||||
ExitPool: b.exitPool,
|
||||
SlashingsPool: b.slashingsPool,
|
||||
SyncCommitteeObjectPool: b.syncCommitteePool,
|
||||
POWChainService: web3Service,
|
||||
ChainStartFetcher: chainStartFetcher,
|
||||
MockEth1Votes: mockEth1DataVotes,
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
prysmv2 "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/copyutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/queue"
|
||||
)
|
||||
@@ -16,7 +16,7 @@ const syncCommitteeMaxQueueSize = 4
|
||||
|
||||
// SaveSyncCommitteeContribution saves a sync committee contribution in to a priority queue.
|
||||
// The priority queue is capped at syncCommitteeMaxQueueSize contributions.
|
||||
func (s *Store) SaveSyncCommitteeContribution(cont *prysmv2.SyncCommitteeContribution) error {
|
||||
func (s *Store) SaveSyncCommitteeContribution(cont *ethpb.SyncCommitteeContribution) error {
|
||||
if cont == nil {
|
||||
return errNilContribution
|
||||
}
|
||||
@@ -33,7 +33,7 @@ func (s *Store) SaveSyncCommitteeContribution(cont *prysmv2.SyncCommitteeContrib
|
||||
|
||||
// Contributions exist in the queue. Append instead of insert new.
|
||||
if item != nil {
|
||||
contributions, ok := item.Value.([]*prysmv2.SyncCommitteeContribution)
|
||||
contributions, ok := item.Value.([]*ethpb.SyncCommitteeContribution)
|
||||
if !ok {
|
||||
return errors.New("not typed []ethpb.SyncCommitteeContribution")
|
||||
}
|
||||
@@ -50,7 +50,7 @@ func (s *Store) SaveSyncCommitteeContribution(cont *prysmv2.SyncCommitteeContrib
|
||||
// Contribution does not exist. Insert new.
|
||||
if err := s.contributionCache.Push(&queue.Item{
|
||||
Key: syncCommitteeKey(cont.Slot),
|
||||
Value: []*prysmv2.SyncCommitteeContribution{copied},
|
||||
Value: []*ethpb.SyncCommitteeContribution{copied},
|
||||
Priority: int64(cont.Slot),
|
||||
}); err != nil {
|
||||
return err
|
||||
@@ -69,7 +69,7 @@ func (s *Store) SaveSyncCommitteeContribution(cont *prysmv2.SyncCommitteeContrib
|
||||
|
||||
// SyncCommitteeContributions returns sync committee contributions by slot from the priority queue.
|
||||
// Upon retrieval, the contribution is removed from the queue.
|
||||
func (s *Store) SyncCommitteeContributions(slot types.Slot) ([]*prysmv2.SyncCommitteeContribution, error) {
|
||||
func (s *Store) SyncCommitteeContributions(slot types.Slot) ([]*ethpb.SyncCommitteeContribution, error) {
|
||||
s.contributionLock.RLock()
|
||||
defer s.contributionLock.RUnlock()
|
||||
|
||||
@@ -78,9 +78,9 @@ func (s *Store) SyncCommitteeContributions(slot types.Slot) ([]*prysmv2.SyncComm
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
contributions, ok := item.Value.([]*prysmv2.SyncCommitteeContribution)
|
||||
contributions, ok := item.Value.([]*ethpb.SyncCommitteeContribution)
|
||||
if !ok {
|
||||
return nil, errors.New("not typed []prysmv2.SyncCommitteeContribution")
|
||||
return nil, errors.New("not typed []ethpb.SyncCommitteeContribution")
|
||||
}
|
||||
|
||||
return contributions, nil
|
||||
|
||||
@@ -3,7 +3,7 @@ package synccommittee
|
||||
import (
|
||||
"testing"
|
||||
|
||||
prysmv2 "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
|
||||
@@ -15,7 +15,7 @@ func TestSyncCommitteeContributionCache_Nil(t *testing.T) {
|
||||
func TestSyncCommitteeContributionCache_RoundTrip(t *testing.T) {
|
||||
store := NewStore()
|
||||
|
||||
conts := []*prysmv2.SyncCommitteeContribution{
|
||||
conts := []*ethpb.SyncCommitteeContribution{
|
||||
{Slot: 1, SubcommitteeIndex: 0, Signature: []byte{'a'}},
|
||||
{Slot: 1, SubcommitteeIndex: 1, Signature: []byte{'b'}},
|
||||
{Slot: 2, SubcommitteeIndex: 0, Signature: []byte{'c'}},
|
||||
@@ -36,36 +36,36 @@ func TestSyncCommitteeContributionCache_RoundTrip(t *testing.T) {
|
||||
|
||||
conts, err := store.SyncCommitteeContributions(1)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, []*prysmv2.SyncCommitteeContribution(nil), conts)
|
||||
require.DeepSSZEqual(t, []*ethpb.SyncCommitteeContribution(nil), conts)
|
||||
|
||||
conts, err = store.SyncCommitteeContributions(2)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, []*prysmv2.SyncCommitteeContribution(nil), conts)
|
||||
require.DeepSSZEqual(t, []*ethpb.SyncCommitteeContribution(nil), conts)
|
||||
|
||||
conts, err = store.SyncCommitteeContributions(3)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, []*prysmv2.SyncCommitteeContribution{
|
||||
require.DeepSSZEqual(t, []*ethpb.SyncCommitteeContribution{
|
||||
{Slot: 3, SubcommitteeIndex: 0, Signature: []byte{'e'}},
|
||||
{Slot: 3, SubcommitteeIndex: 1, Signature: []byte{'f'}},
|
||||
}, conts)
|
||||
|
||||
conts, err = store.SyncCommitteeContributions(4)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, []*prysmv2.SyncCommitteeContribution{
|
||||
require.DeepSSZEqual(t, []*ethpb.SyncCommitteeContribution{
|
||||
{Slot: 4, SubcommitteeIndex: 0, Signature: []byte{'g'}},
|
||||
{Slot: 4, SubcommitteeIndex: 1, Signature: []byte{'h'}},
|
||||
}, conts)
|
||||
|
||||
conts, err = store.SyncCommitteeContributions(5)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, []*prysmv2.SyncCommitteeContribution{
|
||||
require.DeepSSZEqual(t, []*ethpb.SyncCommitteeContribution{
|
||||
{Slot: 5, SubcommitteeIndex: 0, Signature: []byte{'i'}},
|
||||
{Slot: 5, SubcommitteeIndex: 1, Signature: []byte{'j'}},
|
||||
}, conts)
|
||||
|
||||
conts, err = store.SyncCommitteeContributions(6)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, []*prysmv2.SyncCommitteeContribution{
|
||||
require.DeepSSZEqual(t, []*ethpb.SyncCommitteeContribution{
|
||||
{Slot: 6, SubcommitteeIndex: 0, Signature: []byte{'k'}},
|
||||
{Slot: 6, SubcommitteeIndex: 1, Signature: []byte{'l'}},
|
||||
}, conts)
|
||||
@@ -73,35 +73,35 @@ func TestSyncCommitteeContributionCache_RoundTrip(t *testing.T) {
|
||||
// All the contributions should persist after get.
|
||||
conts, err = store.SyncCommitteeContributions(1)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, []*prysmv2.SyncCommitteeContribution(nil), conts)
|
||||
require.DeepSSZEqual(t, []*ethpb.SyncCommitteeContribution(nil), conts)
|
||||
conts, err = store.SyncCommitteeContributions(2)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, []*prysmv2.SyncCommitteeContribution(nil), conts)
|
||||
require.DeepSSZEqual(t, []*ethpb.SyncCommitteeContribution(nil), conts)
|
||||
|
||||
conts, err = store.SyncCommitteeContributions(3)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, []*prysmv2.SyncCommitteeContribution{
|
||||
require.DeepSSZEqual(t, []*ethpb.SyncCommitteeContribution{
|
||||
{Slot: 3, SubcommitteeIndex: 0, Signature: []byte{'e'}},
|
||||
{Slot: 3, SubcommitteeIndex: 1, Signature: []byte{'f'}},
|
||||
}, conts)
|
||||
|
||||
conts, err = store.SyncCommitteeContributions(4)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, []*prysmv2.SyncCommitteeContribution{
|
||||
require.DeepSSZEqual(t, []*ethpb.SyncCommitteeContribution{
|
||||
{Slot: 4, SubcommitteeIndex: 0, Signature: []byte{'g'}},
|
||||
{Slot: 4, SubcommitteeIndex: 1, Signature: []byte{'h'}},
|
||||
}, conts)
|
||||
|
||||
conts, err = store.SyncCommitteeContributions(5)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, []*prysmv2.SyncCommitteeContribution{
|
||||
require.DeepSSZEqual(t, []*ethpb.SyncCommitteeContribution{
|
||||
{Slot: 5, SubcommitteeIndex: 0, Signature: []byte{'i'}},
|
||||
{Slot: 5, SubcommitteeIndex: 1, Signature: []byte{'j'}},
|
||||
}, conts)
|
||||
|
||||
conts, err = store.SyncCommitteeContributions(6)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, []*prysmv2.SyncCommitteeContribution{
|
||||
require.DeepSSZEqual(t, []*ethpb.SyncCommitteeContribution{
|
||||
{Slot: 6, SubcommitteeIndex: 0, Signature: []byte{'k'}},
|
||||
{Slot: 6, SubcommitteeIndex: 1, Signature: []byte{'l'}},
|
||||
}, conts)
|
||||
|
||||
@@ -3,14 +3,14 @@ package synccommittee
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
prysmv2 "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/copyutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/queue"
|
||||
)
|
||||
|
||||
// SaveSyncCommitteeMessage saves a sync committee message in to a priority queue.
|
||||
// The priority queue capped at syncCommitteeMaxQueueSize contributions.
|
||||
func (s *Store) SaveSyncCommitteeMessage(msg *prysmv2.SyncCommitteeMessage) error {
|
||||
func (s *Store) SaveSyncCommitteeMessage(msg *ethpb.SyncCommitteeMessage) error {
|
||||
if msg == nil {
|
||||
return errNilMessage
|
||||
}
|
||||
@@ -26,7 +26,7 @@ func (s *Store) SaveSyncCommitteeMessage(msg *prysmv2.SyncCommitteeMessage) erro
|
||||
copied := copyutil.CopySyncCommitteeMessage(msg)
|
||||
// Messages exist in the queue. Append instead of insert new.
|
||||
if item != nil {
|
||||
messages, ok := item.Value.([]*prysmv2.SyncCommitteeMessage)
|
||||
messages, ok := item.Value.([]*ethpb.SyncCommitteeMessage)
|
||||
if !ok {
|
||||
return errors.New("not typed []ethpb.SyncCommitteeMessage")
|
||||
}
|
||||
@@ -43,7 +43,7 @@ func (s *Store) SaveSyncCommitteeMessage(msg *prysmv2.SyncCommitteeMessage) erro
|
||||
// Message does not exist. Insert new.
|
||||
if err := s.messageCache.Push(&queue.Item{
|
||||
Key: syncCommitteeKey(msg.Slot),
|
||||
Value: []*prysmv2.SyncCommitteeMessage{copied},
|
||||
Value: []*ethpb.SyncCommitteeMessage{copied},
|
||||
Priority: int64(msg.Slot),
|
||||
}); err != nil {
|
||||
return err
|
||||
@@ -62,7 +62,7 @@ func (s *Store) SaveSyncCommitteeMessage(msg *prysmv2.SyncCommitteeMessage) erro
|
||||
|
||||
// SyncCommitteeMessages returns sync committee messages by slot from the priority queue.
|
||||
// Upon retrieval, the message is removed from the queue.
|
||||
func (s *Store) SyncCommitteeMessages(slot types.Slot) ([]*prysmv2.SyncCommitteeMessage, error) {
|
||||
func (s *Store) SyncCommitteeMessages(slot types.Slot) ([]*ethpb.SyncCommitteeMessage, error) {
|
||||
s.messageLock.RLock()
|
||||
defer s.messageLock.RUnlock()
|
||||
|
||||
@@ -71,9 +71,9 @@ func (s *Store) SyncCommitteeMessages(slot types.Slot) ([]*prysmv2.SyncCommittee
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
messages, ok := item.Value.([]*prysmv2.SyncCommitteeMessage)
|
||||
messages, ok := item.Value.([]*ethpb.SyncCommitteeMessage)
|
||||
if !ok {
|
||||
return nil, errors.New("not typed []prysmv2.SyncCommitteeMessage")
|
||||
return nil, errors.New("not typed []ethpb.SyncCommitteeMessage")
|
||||
}
|
||||
|
||||
return messages, nil
|
||||
|
||||
@@ -3,7 +3,7 @@ package synccommittee
|
||||
import (
|
||||
"testing"
|
||||
|
||||
prysmv2 "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
|
||||
@@ -15,7 +15,7 @@ func TestSyncCommitteeSignatureCache_Nil(t *testing.T) {
|
||||
func TestSyncCommitteeSignatureCache_RoundTrip(t *testing.T) {
|
||||
store := NewStore()
|
||||
|
||||
msgs := []*prysmv2.SyncCommitteeMessage{
|
||||
msgs := []*ethpb.SyncCommitteeMessage{
|
||||
{Slot: 1, ValidatorIndex: 0, Signature: []byte{'a'}},
|
||||
{Slot: 1, ValidatorIndex: 1, Signature: []byte{'b'}},
|
||||
{Slot: 2, ValidatorIndex: 0, Signature: []byte{'c'}},
|
||||
@@ -36,36 +36,36 @@ func TestSyncCommitteeSignatureCache_RoundTrip(t *testing.T) {
|
||||
|
||||
msgs, err := store.SyncCommitteeMessages(1)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, []*prysmv2.SyncCommitteeMessage(nil), msgs)
|
||||
require.DeepSSZEqual(t, []*ethpb.SyncCommitteeMessage(nil), msgs)
|
||||
|
||||
msgs, err = store.SyncCommitteeMessages(2)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, []*prysmv2.SyncCommitteeMessage(nil), msgs)
|
||||
require.DeepSSZEqual(t, []*ethpb.SyncCommitteeMessage(nil), msgs)
|
||||
|
||||
msgs, err = store.SyncCommitteeMessages(3)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, []*prysmv2.SyncCommitteeMessage{
|
||||
require.DeepSSZEqual(t, []*ethpb.SyncCommitteeMessage{
|
||||
{Slot: 3, ValidatorIndex: 0, Signature: []byte{'e'}},
|
||||
{Slot: 3, ValidatorIndex: 1, Signature: []byte{'f'}},
|
||||
}, msgs)
|
||||
|
||||
msgs, err = store.SyncCommitteeMessages(4)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, []*prysmv2.SyncCommitteeMessage{
|
||||
require.DeepSSZEqual(t, []*ethpb.SyncCommitteeMessage{
|
||||
{Slot: 4, ValidatorIndex: 0, Signature: []byte{'g'}},
|
||||
{Slot: 4, ValidatorIndex: 1, Signature: []byte{'h'}},
|
||||
}, msgs)
|
||||
|
||||
msgs, err = store.SyncCommitteeMessages(5)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, []*prysmv2.SyncCommitteeMessage{
|
||||
require.DeepSSZEqual(t, []*ethpb.SyncCommitteeMessage{
|
||||
{Slot: 5, ValidatorIndex: 0, Signature: []byte{'i'}},
|
||||
{Slot: 5, ValidatorIndex: 1, Signature: []byte{'j'}},
|
||||
}, msgs)
|
||||
|
||||
msgs, err = store.SyncCommitteeMessages(6)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, []*prysmv2.SyncCommitteeMessage{
|
||||
require.DeepSSZEqual(t, []*ethpb.SyncCommitteeMessage{
|
||||
{Slot: 6, ValidatorIndex: 0, Signature: []byte{'k'}},
|
||||
{Slot: 6, ValidatorIndex: 1, Signature: []byte{'l'}},
|
||||
}, msgs)
|
||||
@@ -73,36 +73,36 @@ func TestSyncCommitteeSignatureCache_RoundTrip(t *testing.T) {
|
||||
// Messages should persist after retrieval.
|
||||
msgs, err = store.SyncCommitteeMessages(1)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, []*prysmv2.SyncCommitteeMessage(nil), msgs)
|
||||
require.DeepSSZEqual(t, []*ethpb.SyncCommitteeMessage(nil), msgs)
|
||||
|
||||
msgs, err = store.SyncCommitteeMessages(2)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, []*prysmv2.SyncCommitteeMessage(nil), msgs)
|
||||
require.DeepSSZEqual(t, []*ethpb.SyncCommitteeMessage(nil), msgs)
|
||||
|
||||
msgs, err = store.SyncCommitteeMessages(3)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, []*prysmv2.SyncCommitteeMessage{
|
||||
require.DeepSSZEqual(t, []*ethpb.SyncCommitteeMessage{
|
||||
{Slot: 3, ValidatorIndex: 0, Signature: []byte{'e'}},
|
||||
{Slot: 3, ValidatorIndex: 1, Signature: []byte{'f'}},
|
||||
}, msgs)
|
||||
|
||||
msgs, err = store.SyncCommitteeMessages(4)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, []*prysmv2.SyncCommitteeMessage{
|
||||
require.DeepSSZEqual(t, []*ethpb.SyncCommitteeMessage{
|
||||
{Slot: 4, ValidatorIndex: 0, Signature: []byte{'g'}},
|
||||
{Slot: 4, ValidatorIndex: 1, Signature: []byte{'h'}},
|
||||
}, msgs)
|
||||
|
||||
msgs, err = store.SyncCommitteeMessages(5)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, []*prysmv2.SyncCommitteeMessage{
|
||||
require.DeepSSZEqual(t, []*ethpb.SyncCommitteeMessage{
|
||||
{Slot: 5, ValidatorIndex: 0, Signature: []byte{'i'}},
|
||||
{Slot: 5, ValidatorIndex: 1, Signature: []byte{'j'}},
|
||||
}, msgs)
|
||||
|
||||
msgs, err = store.SyncCommitteeMessages(6)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, []*prysmv2.SyncCommitteeMessage{
|
||||
require.DeepSSZEqual(t, []*ethpb.SyncCommitteeMessage{
|
||||
{Slot: 6, ValidatorIndex: 0, Signature: []byte{'k'}},
|
||||
{Slot: 6, ValidatorIndex: 1, Signature: []byte{'l'}},
|
||||
}, msgs)
|
||||
|
||||
@@ -2,7 +2,7 @@ package synccommittee
|
||||
|
||||
import (
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
prysmv2 "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
var _ = Pool(&Store{})
|
||||
@@ -13,12 +13,12 @@ var _ = Pool(&Store{})
|
||||
// sync aggregators.
|
||||
type Pool interface {
|
||||
// Methods for Sync Contributions.
|
||||
SaveSyncCommitteeContribution(contr *prysmv2.SyncCommitteeContribution) error
|
||||
SyncCommitteeContributions(slot types.Slot) ([]*prysmv2.SyncCommitteeContribution, error)
|
||||
SaveSyncCommitteeContribution(contr *ethpb.SyncCommitteeContribution) error
|
||||
SyncCommitteeContributions(slot types.Slot) ([]*ethpb.SyncCommitteeContribution, error)
|
||||
|
||||
// Methods for Sync Committee Messages.
|
||||
SaveSyncCommitteeMessage(sig *prysmv2.SyncCommitteeMessage) error
|
||||
SyncCommitteeMessages(slot types.Slot) ([]*prysmv2.SyncCommitteeMessage, error)
|
||||
SaveSyncCommitteeMessage(sig *ethpb.SyncCommitteeMessage) error
|
||||
SyncCommitteeMessages(slot types.Slot) ([]*ethpb.SyncCommitteeMessage, error)
|
||||
}
|
||||
|
||||
// NewPool returns the sync committee store fulfilling the pool interface.
|
||||
|
||||
@@ -11,6 +11,7 @@ go_library(
|
||||
"discovery.go",
|
||||
"doc.go",
|
||||
"fork.go",
|
||||
"fork_watcher.go",
|
||||
"gossip_scoring_params.go",
|
||||
"gossip_topic_mappings.go",
|
||||
"handshake.go",
|
||||
@@ -39,6 +40,7 @@ go_library(
|
||||
],
|
||||
deps = [
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/core/altair:go_default_library",
|
||||
"//beacon-chain/core/feed:go_default_library",
|
||||
"//beacon-chain/core/feed/state:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
@@ -53,6 +55,7 @@ go_library(
|
||||
"//proto/prysm/v1alpha1/metadata:go_default_library",
|
||||
"//proto/prysm/v1alpha1/wrapper:go_default_library",
|
||||
"//shared:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/fileutil:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
@@ -68,8 +71,10 @@ go_library(
|
||||
"@com_github_ethereum_go_ethereum//p2p/discover:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//p2p/enode:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//p2p/enr:go_default_library",
|
||||
"@com_github_ferranbt_fastssz//:go_default_library",
|
||||
"@com_github_ipfs_go_ipfs_addr//:go_default_library",
|
||||
"@com_github_kevinms_leakybucket_go//:go_default_library",
|
||||
"@com_github_kr_pretty//:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p//:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p//config:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p//p2p/protocol/identify:go_default_library",
|
||||
@@ -147,6 +152,7 @@ go_test(
|
||||
"//shared/testutil/assert:go_default_library",
|
||||
"//shared/testutil/require:go_default_library",
|
||||
"//shared/timeutils:go_default_library",
|
||||
"//shared/version:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//crypto:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//p2p/discover:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//p2p/enode:go_default_library",
|
||||
@@ -164,6 +170,7 @@ go_test(
|
||||
"@com_github_libp2p_go_libp2p_pubsub//pb:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p_swarm//testing:go_default_library",
|
||||
"@com_github_multiformats_go_multiaddr//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
|
||||
@@ -7,8 +7,12 @@ import (
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
ssz "github.com/ferranbt/fastssz"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/altair"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/traceutil"
|
||||
@@ -20,7 +24,8 @@ import (
|
||||
// GossipTypeMapping.
|
||||
var ErrMessageNotMapped = errors.New("message type is not mapped to a PubSub topic")
|
||||
|
||||
// Broadcast a message to the p2p network.
|
||||
// Broadcasts a message to the p2p network, the message is assumed to be
|
||||
// broadcasted to the current fork.
|
||||
func (s *Service) Broadcast(ctx context.Context, msg proto.Message) error {
|
||||
ctx, span := trace.StartSpan(ctx, "p2p.Broadcast")
|
||||
defer span.End()
|
||||
@@ -29,7 +34,7 @@ func (s *Service) Broadcast(ctx context.Context, msg proto.Message) error {
|
||||
ctx, cancel := context.WithTimeout(ctx, twoSlots)
|
||||
defer cancel()
|
||||
|
||||
forkDigest, err := s.forkDigest()
|
||||
forkDigest, err := s.currentForkDigest()
|
||||
if err != nil {
|
||||
err := errors.Wrap(err, "could not retrieve fork digest")
|
||||
traceutil.AnnotateError(span, err)
|
||||
@@ -41,14 +46,19 @@ func (s *Service) Broadcast(ctx context.Context, msg proto.Message) error {
|
||||
traceutil.AnnotateError(span, ErrMessageNotMapped)
|
||||
return ErrMessageNotMapped
|
||||
}
|
||||
return s.broadcastObject(ctx, msg, fmt.Sprintf(topic, forkDigest))
|
||||
castMsg, ok := msg.(ssz.Marshaler)
|
||||
if !ok {
|
||||
return errors.Errorf("message of %T does not support marshaller interface", msg)
|
||||
}
|
||||
return s.broadcastObject(ctx, castMsg, fmt.Sprintf(topic, forkDigest))
|
||||
}
|
||||
|
||||
// BroadcastAttestation broadcasts an attestation to the p2p network.
|
||||
// BroadcastAttestation broadcasts an attestation to the p2p network, the message is assumed to be
|
||||
// broadcasted to the current fork.
|
||||
func (s *Service) BroadcastAttestation(ctx context.Context, subnet uint64, att *eth.Attestation) error {
|
||||
ctx, span := trace.StartSpan(ctx, "p2p.BroadcastAttestation")
|
||||
defer span.End()
|
||||
forkDigest, err := s.forkDigest()
|
||||
forkDigest, err := s.currentForkDigest()
|
||||
if err != nil {
|
||||
err := errors.Wrap(err, "could not retrieve fork digest")
|
||||
traceutil.AnnotateError(span, err)
|
||||
@@ -61,6 +71,24 @@ func (s *Service) BroadcastAttestation(ctx context.Context, subnet uint64, att *
|
||||
return nil
|
||||
}
|
||||
|
||||
// BroadcastAttestation broadcasts an attestation to the p2p network, the message is assumed to be
|
||||
// broadcasted to the current fork.
|
||||
func (s *Service) BroadcastSyncCommitteeMessage(ctx context.Context, subnet uint64, sMsg *ethpb.SyncCommitteeMessage) error {
|
||||
ctx, span := trace.StartSpan(ctx, "p2p.BroadcastSyncCommitteeMessage")
|
||||
defer span.End()
|
||||
forkDigest, err := s.currentForkDigest()
|
||||
if err != nil {
|
||||
err := errors.Wrap(err, "could not retrieve fork digest")
|
||||
traceutil.AnnotateError(span, err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Non-blocking broadcast, with attempts to discover a subnet peer if none available.
|
||||
go s.broadcastSyncCommittee(ctx, subnet, sMsg, forkDigest)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) broadcastAttestation(ctx context.Context, subnet uint64, att *eth.Attestation, forkDigest [4]byte) {
|
||||
ctx, span := trace.StartSpan(ctx, "p2p.broadcastAttestation")
|
||||
defer span.End()
|
||||
@@ -100,6 +128,13 @@ func (s *Service) broadcastAttestation(ctx context.Context, subnet uint64, att *
|
||||
traceutil.AnnotateError(span, err)
|
||||
}
|
||||
}
|
||||
// In the event our attestation is outdated and beyond the
|
||||
// acceptable threshold, we exit early and do not broadcast it.
|
||||
currSlot := helpers.CurrentSlot(uint64(s.genesisTime.Unix()))
|
||||
if att.Data.Slot+params.BeaconConfig().SlotsPerEpoch < currSlot {
|
||||
log.Warnf("Attestation is too old to broadcast, discarding it. Current Slot: %d , Attestation Slot: %d", currSlot, att.Data.Slot)
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.broadcastObject(ctx, att, attestationToTopic(subnet, forkDigest)); err != nil {
|
||||
log.WithError(err).Error("Failed to broadcast attestation")
|
||||
@@ -107,8 +142,63 @@ func (s *Service) broadcastAttestation(ctx context.Context, subnet uint64, att *
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) broadcastSyncCommittee(ctx context.Context, subnet uint64, sMsg *ethpb.SyncCommitteeMessage, forkDigest [4]byte) {
|
||||
ctx, span := trace.StartSpan(ctx, "p2p.broadcastSyncCommittee")
|
||||
defer span.End()
|
||||
ctx = trace.NewContext(context.Background(), span) // clear parent context / deadline.
|
||||
|
||||
oneSlot := time.Duration(1*params.BeaconConfig().SecondsPerSlot) * time.Second
|
||||
ctx, cancel := context.WithTimeout(ctx, oneSlot)
|
||||
defer cancel()
|
||||
|
||||
// Ensure we have peers with this subnet.
|
||||
// This adds in a special value to the subnet
|
||||
// to ensure that we can re-use the same subnet locker.
|
||||
wrappedSubIdx := subnet + syncLockerVal
|
||||
s.subnetLocker(wrappedSubIdx).RLock()
|
||||
hasPeer := s.hasPeerWithSubnet(syncCommitteeToTopic(subnet, forkDigest))
|
||||
s.subnetLocker(wrappedSubIdx).RUnlock()
|
||||
|
||||
span.AddAttributes(
|
||||
trace.BoolAttribute("hasPeer", hasPeer),
|
||||
trace.Int64Attribute("slot", int64(sMsg.Slot)),
|
||||
trace.Int64Attribute("subnet", int64(subnet)),
|
||||
)
|
||||
|
||||
if !hasPeer {
|
||||
syncCommitteeBroadcastAttempts.Inc()
|
||||
if err := func() error {
|
||||
s.subnetLocker(wrappedSubIdx).Lock()
|
||||
defer s.subnetLocker(wrappedSubIdx).Unlock()
|
||||
ok, err := s.FindPeersWithSubnet(ctx, syncCommitteeToTopic(subnet, forkDigest), subnet, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ok {
|
||||
savedSyncCommitteeBroadcasts.Inc()
|
||||
return nil
|
||||
}
|
||||
return errors.New("failed to find peers for subnet")
|
||||
}(); err != nil {
|
||||
log.WithError(err).Error("Failed to find peers")
|
||||
traceutil.AnnotateError(span, err)
|
||||
}
|
||||
}
|
||||
// In the event our sync message is outdated and beyond the
|
||||
// acceptable threshold, we exit early and do not broadcast it.
|
||||
if err := altair.ValidateSyncMessageTime(sMsg.Slot, s.genesisTime, params.BeaconNetworkConfig().MaximumGossipClockDisparity); err != nil {
|
||||
log.Warnf("Sync Committee Message is too old to broadcast, discarding it. %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.broadcastObject(ctx, sMsg, syncCommitteeToTopic(subnet, forkDigest)); err != nil {
|
||||
log.WithError(err).Error("Failed to broadcast sync committee message")
|
||||
traceutil.AnnotateError(span, err)
|
||||
}
|
||||
}
|
||||
|
||||
// method to broadcast messages to other peers in our gossip mesh.
|
||||
func (s *Service) broadcastObject(ctx context.Context, obj interface{}, topic string) error {
|
||||
func (s *Service) broadcastObject(ctx context.Context, obj ssz.Marshaler, topic string) error {
|
||||
_, span := trace.StartSpan(ctx, "p2p.broadcastObject")
|
||||
defer span.End()
|
||||
|
||||
@@ -126,7 +216,6 @@ func (s *Service) broadcastObject(ctx context.Context, obj interface{}, topic st
|
||||
messageLen := int64(buf.Len())
|
||||
span.AddMessageSendEvent(int64(id), messageLen /*uncompressed*/, messageLen /*compressed*/)
|
||||
}
|
||||
|
||||
if err := s.PublishToTopic(ctx, topic+s.Encoding().ProtocolSuffix(), buf.Bytes()); err != nil {
|
||||
err := errors.Wrap(err, "could not publish message")
|
||||
traceutil.AnnotateError(span, err)
|
||||
@@ -138,3 +227,7 @@ func (s *Service) broadcastObject(ctx context.Context, obj interface{}, topic st
|
||||
func attestationToTopic(subnet uint64, forkDigest [4]byte) string {
|
||||
return fmt.Sprintf(AttestationSubnetTopicFormat, forkDigest, subnet)
|
||||
}
|
||||
|
||||
func syncCommitteeToTopic(subnet uint64, forkDigest [4]byte) string {
|
||||
return fmt.Sprintf(SyncCommitteeSubnetTopicFormat, forkDigest, subnet)
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers/scorers"
|
||||
p2ptest "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing"
|
||||
eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
statepb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
testpb "github.com/prysmaticlabs/prysm/proto/testing"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
@@ -46,7 +46,7 @@ func TestService_Broadcast(t *testing.T) {
|
||||
genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32),
|
||||
}
|
||||
|
||||
msg := &statepb.Fork{
|
||||
msg := ðpb.Fork{
|
||||
Epoch: 55,
|
||||
CurrentVersion: []byte("fooo"),
|
||||
PreviousVersion: []byte("barr"),
|
||||
@@ -55,7 +55,7 @@ func TestService_Broadcast(t *testing.T) {
|
||||
topic := "/eth2/%x/testing"
|
||||
// Set a test gossip mapping for testpb.TestSimpleMessage.
|
||||
GossipTypeMapping[reflect.TypeOf(msg)] = topic
|
||||
digest, err := p.forkDigest()
|
||||
digest, err := p.currentForkDigest()
|
||||
require.NoError(t, err)
|
||||
topic = fmt.Sprintf(topic, digest)
|
||||
|
||||
@@ -77,7 +77,7 @@ func TestService_Broadcast(t *testing.T) {
|
||||
incomingMessage, err := sub.Next(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
result := &statepb.Fork{}
|
||||
result := ðpb.Fork{}
|
||||
require.NoError(t, p.Encoding().DecodeGossip(incomingMessage.Data, result))
|
||||
if !proto.Equal(result, msg) {
|
||||
tt.Errorf("Did not receive expected message, got %+v, wanted %+v", result, msg)
|
||||
@@ -169,7 +169,7 @@ func TestService_BroadcastAttestation(t *testing.T) {
|
||||
|
||||
topic := AttestationSubnetTopicFormat
|
||||
GossipTypeMapping[reflect.TypeOf(msg)] = topic
|
||||
digest, err := p.forkDigest()
|
||||
digest, err := p.currentForkDigest()
|
||||
require.NoError(t, err)
|
||||
topic = fmt.Sprintf(topic, digest, subnet)
|
||||
|
||||
@@ -326,7 +326,7 @@ func TestService_BroadcastAttestationWithDiscoveryAttempts(t *testing.T) {
|
||||
msg := testutil.HydrateAttestation(ð.Attestation{AggregationBits: bitfield.NewBitlist(7)})
|
||||
topic := AttestationSubnetTopicFormat
|
||||
GossipTypeMapping[reflect.TypeOf(msg)] = topic
|
||||
digest, err := p.forkDigest()
|
||||
digest, err := p.currentForkDigest()
|
||||
require.NoError(t, err)
|
||||
topic = fmt.Sprintf(topic, digest, subnet)
|
||||
|
||||
@@ -365,3 +365,66 @@ func TestService_BroadcastAttestationWithDiscoveryAttempts(t *testing.T) {
|
||||
t.Error("Failed to receive pubsub within 4s")
|
||||
}
|
||||
}
|
||||
|
||||
func TestService_BroadcastSyncCommittee(t *testing.T) {
|
||||
p1 := p2ptest.NewTestP2P(t)
|
||||
p2 := p2ptest.NewTestP2P(t)
|
||||
p1.Connect(p2)
|
||||
if len(p1.BHost.Network().Peers()) == 0 {
|
||||
t.Fatal("No peers")
|
||||
}
|
||||
|
||||
p := &Service{
|
||||
host: p1.BHost,
|
||||
pubsub: p1.PubSub(),
|
||||
joinedTopics: map[string]*pubsub.Topic{},
|
||||
cfg: &Config{},
|
||||
genesisTime: time.Now(),
|
||||
genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32),
|
||||
subnetsLock: make(map[uint64]*sync.RWMutex),
|
||||
subnetsLockLock: sync.Mutex{},
|
||||
peers: peers.NewStatus(context.Background(), &peers.StatusConfig{
|
||||
ScorerParams: &scorers.Config{},
|
||||
}),
|
||||
}
|
||||
|
||||
msg := testutil.HydrateSyncCommittee(&pb.SyncCommitteeMessage{})
|
||||
subnet := uint64(5)
|
||||
|
||||
topic := SyncCommitteeSubnetTopicFormat
|
||||
GossipTypeMapping[reflect.TypeOf(msg)] = topic
|
||||
digest, err := p.currentForkDigest()
|
||||
require.NoError(t, err)
|
||||
topic = fmt.Sprintf(topic, digest, subnet)
|
||||
|
||||
// External peer subscribes to the topic.
|
||||
topic += p.Encoding().ProtocolSuffix()
|
||||
sub, err := p2.SubscribeToTopic(topic)
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(50 * time.Millisecond) // libp2p fails without this delay...
|
||||
|
||||
// Async listen for the pubsub, must be before the broadcast.
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func(tt *testing.T) {
|
||||
defer wg.Done()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
|
||||
incomingMessage, err := sub.Next(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
result := &pb.SyncCommitteeMessage{}
|
||||
require.NoError(t, p.Encoding().DecodeGossip(incomingMessage.Data, result))
|
||||
if !proto.Equal(result, msg) {
|
||||
tt.Errorf("Did not receive expected message, got %+v, wanted %+v", result, msg)
|
||||
}
|
||||
}(t)
|
||||
|
||||
// Broadcast to peers and wait.
|
||||
require.NoError(t, p.BroadcastSyncCommitteeMessage(context.Background(), subnet, msg))
|
||||
if testutil.WaitTimeout(&wg, 1*time.Second) {
|
||||
t.Error("Failed to receive pubsub within 1s")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/version"
|
||||
)
|
||||
|
||||
// Listener defines the discovery V5 network interface that is used
|
||||
@@ -36,7 +39,7 @@ type Listener interface {
|
||||
// to be dynamically discoverable by others given our tracked committee ids.
|
||||
func (s *Service) RefreshENR() {
|
||||
// return early if discv5 isnt running
|
||||
if s.dv5Listener == nil {
|
||||
if s.dv5Listener == nil || !s.isInitialized() {
|
||||
return
|
||||
}
|
||||
bitV := bitfield.NewBitvector64()
|
||||
@@ -44,16 +47,43 @@ func (s *Service) RefreshENR() {
|
||||
for _, idx := range committees {
|
||||
bitV.SetBitAt(idx, true)
|
||||
}
|
||||
currentBitV, err := bitvector(s.dv5Listener.Self().Record())
|
||||
currentBitV, err := attBitvector(s.dv5Listener.Self().Record())
|
||||
if err != nil {
|
||||
log.Errorf("Could not retrieve bitfield: %v", err)
|
||||
log.Errorf("Could not retrieve att bitfield: %v", err)
|
||||
return
|
||||
}
|
||||
if bytes.Equal(bitV, currentBitV) {
|
||||
// return early if bitfield hasn't changed
|
||||
return
|
||||
// Compare current epoch with our fork epochs
|
||||
currEpoch := helpers.SlotToEpoch(helpers.CurrentSlot(uint64(s.genesisTime.Unix())))
|
||||
altairForkEpoch := params.BeaconConfig().AltairForkEpoch
|
||||
switch {
|
||||
// Altair Behaviour
|
||||
case currEpoch >= altairForkEpoch:
|
||||
// Retrieve sync subnets from application level
|
||||
// cache.
|
||||
bitS := bitfield.Bitvector4{byte(0x00)}
|
||||
committees = cache.SyncSubnetIDs.GetAllSubnets(currEpoch)
|
||||
for _, idx := range committees {
|
||||
bitS.SetBitAt(idx, true)
|
||||
}
|
||||
currentBitS, err := syncBitvector(s.dv5Listener.Self().Record())
|
||||
if err != nil {
|
||||
log.Errorf("Could not retrieve sync bitfield: %v", err)
|
||||
return
|
||||
}
|
||||
if bytes.Equal(bitV, currentBitV) && bytes.Equal(bitS, currentBitS) &&
|
||||
s.Metadata().Version() == version.Altair {
|
||||
// return early if bitfields haven't changed
|
||||
return
|
||||
}
|
||||
s.updateSubnetRecordWithMetadataV2(bitV, bitS)
|
||||
default:
|
||||
// Phase 0 behaviour.
|
||||
if bytes.Equal(bitV, currentBitV) {
|
||||
// return early if bitfield hasn't changed
|
||||
return
|
||||
}
|
||||
s.updateSubnetRecordWithMetadata(bitV)
|
||||
}
|
||||
s.updateSubnetRecordWithMetadata(bitV)
|
||||
// ping all peers to inform them of new metadata
|
||||
s.pingPeers()
|
||||
}
|
||||
@@ -206,7 +236,8 @@ func (s *Service) createLocalNode(
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not add eth2 fork version entry to enr")
|
||||
}
|
||||
return intializeAttSubnets(localNode), nil
|
||||
localNode = initializeAttSubnets(localNode)
|
||||
return initializeSyncCommSubnets(localNode), nil
|
||||
}
|
||||
|
||||
func (s *Service) startDiscoveryV5(
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
|
||||
statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers"
|
||||
@@ -33,8 +34,10 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/iputils"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
"github.com/prysmaticlabs/prysm/shared/version"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
)
|
||||
|
||||
@@ -340,3 +343,163 @@ func addPeer(t *testing.T, p *peers.Status, state peerdata.PeerConnectionState)
|
||||
}))
|
||||
return id
|
||||
}
|
||||
|
||||
func TestRefreshENR_ForkBoundaries(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
// Clean up caches after usage.
|
||||
defer cache.SubnetIDs.EmptyAllCaches()
|
||||
defer cache.SubnetIDs.EmptyAllCaches()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
svcBuilder func(t *testing.T) *Service
|
||||
postValidation func(t *testing.T, s *Service)
|
||||
}{
|
||||
{
|
||||
name: "metadata no change",
|
||||
svcBuilder: func(t *testing.T) *Service {
|
||||
port := 2000
|
||||
ipAddr, pkey := createAddrAndPrivKey(t)
|
||||
s := &Service{
|
||||
genesisTime: time.Now(),
|
||||
genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32),
|
||||
cfg: &Config{UDPPort: uint(port)},
|
||||
}
|
||||
listener, err := s.createListener(ipAddr, pkey)
|
||||
assert.NoError(t, err)
|
||||
s.dv5Listener = listener
|
||||
s.metaData = wrapper.WrappedMetadataV0(new(pb.MetaDataV0))
|
||||
s.updateSubnetRecordWithMetadata([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
||||
return s
|
||||
},
|
||||
postValidation: func(t *testing.T, s *Service) {
|
||||
assert.DeepEqual(t, bitfield.NewBitvector64(), s.metaData.AttnetsBitfield())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "metadata updated",
|
||||
svcBuilder: func(t *testing.T) *Service {
|
||||
port := 2000
|
||||
ipAddr, pkey := createAddrAndPrivKey(t)
|
||||
s := &Service{
|
||||
genesisTime: time.Now(),
|
||||
genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32),
|
||||
cfg: &Config{UDPPort: uint(port)},
|
||||
}
|
||||
listener, err := s.createListener(ipAddr, pkey)
|
||||
assert.NoError(t, err)
|
||||
s.dv5Listener = listener
|
||||
s.metaData = wrapper.WrappedMetadataV0(new(pb.MetaDataV0))
|
||||
s.updateSubnetRecordWithMetadata([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01})
|
||||
cache.SubnetIDs.AddPersistentCommittee([]byte{'A'}, []uint64{1, 2, 3, 23}, 0)
|
||||
return s
|
||||
},
|
||||
postValidation: func(t *testing.T, s *Service) {
|
||||
assert.DeepEqual(t, bitfield.Bitvector64{0xe, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0}, s.metaData.AttnetsBitfield())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "metadata updated at fork epoch",
|
||||
svcBuilder: func(t *testing.T) *Service {
|
||||
port := 2000
|
||||
ipAddr, pkey := createAddrAndPrivKey(t)
|
||||
s := &Service{
|
||||
genesisTime: time.Now().Add(-5 * oneEpochDuration()),
|
||||
genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32),
|
||||
cfg: &Config{UDPPort: uint(port)},
|
||||
}
|
||||
listener, err := s.createListener(ipAddr, pkey)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Update params
|
||||
cfg := params.BeaconConfig()
|
||||
cfg.AltairForkEpoch = 5
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
params.BeaconConfig().InitializeForkSchedule()
|
||||
|
||||
s.dv5Listener = listener
|
||||
s.metaData = wrapper.WrappedMetadataV0(new(pb.MetaDataV0))
|
||||
s.updateSubnetRecordWithMetadata([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01})
|
||||
cache.SubnetIDs.AddPersistentCommittee([]byte{'A'}, []uint64{1, 2, 3, 23}, 0)
|
||||
return s
|
||||
},
|
||||
postValidation: func(t *testing.T, s *Service) {
|
||||
assert.Equal(t, version.Altair, s.metaData.Version())
|
||||
assert.DeepEqual(t, bitfield.Bitvector4{0x00}, s.metaData.MetadataObjV1().Syncnets)
|
||||
assert.DeepEqual(t, bitfield.Bitvector64{0xe, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0}, s.metaData.AttnetsBitfield())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "metadata updated at fork epoch with no bitfield",
|
||||
svcBuilder: func(t *testing.T) *Service {
|
||||
port := 2000
|
||||
ipAddr, pkey := createAddrAndPrivKey(t)
|
||||
s := &Service{
|
||||
genesisTime: time.Now().Add(-5 * oneEpochDuration()),
|
||||
genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32),
|
||||
cfg: &Config{UDPPort: uint(port)},
|
||||
}
|
||||
listener, err := s.createListener(ipAddr, pkey)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Update params
|
||||
cfg := params.BeaconConfig()
|
||||
cfg.AltairForkEpoch = 5
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
params.BeaconConfig().InitializeForkSchedule()
|
||||
|
||||
s.dv5Listener = listener
|
||||
s.metaData = wrapper.WrappedMetadataV0(new(pb.MetaDataV0))
|
||||
s.updateSubnetRecordWithMetadata([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
||||
return s
|
||||
},
|
||||
postValidation: func(t *testing.T, s *Service) {
|
||||
assert.Equal(t, version.Altair, s.metaData.Version())
|
||||
assert.DeepEqual(t, bitfield.Bitvector4{0x00}, s.metaData.MetadataObjV1().Syncnets)
|
||||
assert.DeepEqual(t, bitfield.Bitvector64{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, s.metaData.AttnetsBitfield())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "metadata updated past fork epoch with bitfields",
|
||||
svcBuilder: func(t *testing.T) *Service {
|
||||
port := 2000
|
||||
ipAddr, pkey := createAddrAndPrivKey(t)
|
||||
s := &Service{
|
||||
genesisTime: time.Now().Add(-6 * oneEpochDuration()),
|
||||
genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32),
|
||||
cfg: &Config{UDPPort: uint(port)},
|
||||
}
|
||||
listener, err := s.createListener(ipAddr, pkey)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Update params
|
||||
cfg := params.BeaconConfig()
|
||||
cfg.AltairForkEpoch = 5
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
params.BeaconConfig().InitializeForkSchedule()
|
||||
|
||||
s.dv5Listener = listener
|
||||
s.metaData = wrapper.WrappedMetadataV0(new(pb.MetaDataV0))
|
||||
s.updateSubnetRecordWithMetadata([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
||||
cache.SubnetIDs.AddPersistentCommittee([]byte{'A'}, []uint64{1, 2, 3, 23}, 0)
|
||||
cache.SyncSubnetIDs.AddSyncCommitteeSubnets([]byte{'A'}, 0, []uint64{0, 1}, 0)
|
||||
return s
|
||||
},
|
||||
postValidation: func(t *testing.T, s *Service) {
|
||||
assert.Equal(t, version.Altair, s.metaData.Version())
|
||||
assert.DeepEqual(t, bitfield.Bitvector4{0x03}, s.metaData.MetadataObjV1().Syncnets)
|
||||
assert.DeepEqual(t, bitfield.Bitvector64{0xe, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0}, s.metaData.AttnetsBitfield())
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := tt.svcBuilder(t)
|
||||
s.RefreshENR()
|
||||
tt.postValidation(t, s)
|
||||
s.dv5Listener.Close()
|
||||
cache.SubnetIDs.EmptyAllCaches()
|
||||
cache.SyncSubnetIDs.EmptyAllCaches()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,20 +2,22 @@ package encoder
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
ssz "github.com/ferranbt/fastssz"
|
||||
)
|
||||
|
||||
// NetworkEncoding represents an encoder compatible with Ethereum consensus p2p.
|
||||
type NetworkEncoding interface {
|
||||
// DecodeGossip to the provided gossip message. The interface must be a pointer to the decoding destination.
|
||||
DecodeGossip([]byte, interface{}) error
|
||||
DecodeGossip([]byte, ssz.Unmarshaler) error
|
||||
// DecodeWithMaxLength a bytes from a reader with a varint length prefix. The interface must be a pointer to the
|
||||
// decoding destination. The length of the message should not be more than the provided limit.
|
||||
DecodeWithMaxLength(io.Reader, interface{}) error
|
||||
DecodeWithMaxLength(io.Reader, ssz.Unmarshaler) error
|
||||
// EncodeGossip an arbitrary gossip message to the provided writer. The interface must be a pointer object to encode.
|
||||
EncodeGossip(io.Writer, interface{}) (int, error)
|
||||
EncodeGossip(io.Writer, ssz.Marshaler) (int, error)
|
||||
// EncodeWithMaxLength an arbitrary message to the provided writer with a varint length prefix. The interface must be
|
||||
// a pointer object to encode. The encoded message should not be bigger than the provided limit.
|
||||
EncodeWithMaxLength(io.Writer, interface{}) (int, error)
|
||||
EncodeWithMaxLength(io.Writer, ssz.Marshaler) (int, error)
|
||||
// ProtocolSuffix returns the last part of the protocol ID to indicate the encoding scheme.
|
||||
ProtocolSuffix() string
|
||||
}
|
||||
|
||||
@@ -33,15 +33,12 @@ type SszNetworkEncoder struct{}
|
||||
// ProtocolSuffixSSZSnappy is the last part of the topic string to identify the encoding protocol.
|
||||
const ProtocolSuffixSSZSnappy = "ssz_snappy"
|
||||
|
||||
func (e SszNetworkEncoder) doEncode(msg interface{}) ([]byte, error) {
|
||||
if v, ok := msg.(fastssz.Marshaler); ok {
|
||||
return v.MarshalSSZ()
|
||||
}
|
||||
return nil, errors.Errorf("non-supported type: %T", msg)
|
||||
func (e SszNetworkEncoder) doEncode(msg fastssz.Marshaler) ([]byte, error) {
|
||||
return msg.MarshalSSZ()
|
||||
}
|
||||
|
||||
// EncodeGossip the proto gossip message to the io.Writer.
|
||||
func (e SszNetworkEncoder) EncodeGossip(w io.Writer, msg interface{}) (int, error) {
|
||||
func (e SszNetworkEncoder) EncodeGossip(w io.Writer, msg fastssz.Marshaler) (int, error) {
|
||||
if msg == nil {
|
||||
return 0, nil
|
||||
}
|
||||
@@ -58,7 +55,7 @@ func (e SszNetworkEncoder) EncodeGossip(w io.Writer, msg interface{}) (int, erro
|
||||
|
||||
// EncodeWithMaxLength the proto message to the io.Writer. This encoding prefixes the byte slice with a protobuf varint
|
||||
// to indicate the size of the message. This checks that the encoded message isn't larger than the provided max limit.
|
||||
func (e SszNetworkEncoder) EncodeWithMaxLength(w io.Writer, msg interface{}) (int, error) {
|
||||
func (e SszNetworkEncoder) EncodeWithMaxLength(w io.Writer, msg fastssz.Marshaler) (int, error) {
|
||||
if msg == nil {
|
||||
return 0, nil
|
||||
}
|
||||
@@ -81,15 +78,12 @@ func (e SszNetworkEncoder) EncodeWithMaxLength(w io.Writer, msg interface{}) (in
|
||||
return writeSnappyBuffer(w, b)
|
||||
}
|
||||
|
||||
func (e SszNetworkEncoder) doDecode(b []byte, to interface{}) error {
|
||||
if v, ok := to.(fastssz.Unmarshaler); ok {
|
||||
return v.UnmarshalSSZ(b)
|
||||
}
|
||||
return errors.Errorf("non-supported type: %T", to)
|
||||
func (e SszNetworkEncoder) doDecode(b []byte, to fastssz.Unmarshaler) error {
|
||||
return to.UnmarshalSSZ(b)
|
||||
}
|
||||
|
||||
// DecodeGossip decodes the bytes to the protobuf gossip message provided.
|
||||
func (e SszNetworkEncoder) DecodeGossip(b []byte, to interface{}) error {
|
||||
func (e SszNetworkEncoder) DecodeGossip(b []byte, to fastssz.Unmarshaler) error {
|
||||
b, err := DecodeSnappy(b, MaxGossipSize)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -115,7 +109,7 @@ func DecodeSnappy(msg []byte, maxSize uint64) ([]byte, error) {
|
||||
|
||||
// DecodeWithMaxLength the bytes from io.Reader to the protobuf message provided.
|
||||
// This checks that the decoded message isn't larger than the provided max limit.
|
||||
func (e SszNetworkEncoder) DecodeWithMaxLength(r io.Reader, to interface{}) error {
|
||||
func (e SszNetworkEncoder) DecodeWithMaxLength(r io.Reader, to fastssz.Unmarshaler) error {
|
||||
msgLen, err := readVarint(r)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
gogo "github.com/gogo/protobuf/proto"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/encoder"
|
||||
statepb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
@@ -26,7 +26,7 @@ func TestSszNetworkEncoder_RoundTrip(t *testing.T) {
|
||||
|
||||
func TestSszNetworkEncoder_FailsSnappyLength(t *testing.T) {
|
||||
e := &encoder.SszNetworkEncoder{}
|
||||
att := &statepb.Fork{}
|
||||
att := ðpb.Fork{}
|
||||
data := make([]byte, 32)
|
||||
binary.PutUvarint(data, encoder.MaxGossipSize+32)
|
||||
err := e.DecodeGossip(data, att)
|
||||
@@ -35,14 +35,14 @@ func TestSszNetworkEncoder_FailsSnappyLength(t *testing.T) {
|
||||
|
||||
func testRoundTripWithLength(t *testing.T, e *encoder.SszNetworkEncoder) {
|
||||
buf := new(bytes.Buffer)
|
||||
msg := &statepb.Fork{
|
||||
msg := ðpb.Fork{
|
||||
PreviousVersion: []byte("fooo"),
|
||||
CurrentVersion: []byte("barr"),
|
||||
Epoch: 9001,
|
||||
}
|
||||
_, err := e.EncodeWithMaxLength(buf, msg)
|
||||
require.NoError(t, err)
|
||||
decoded := &statepb.Fork{}
|
||||
decoded := ðpb.Fork{}
|
||||
require.NoError(t, e.DecodeWithMaxLength(buf, decoded))
|
||||
if !proto.Equal(decoded, msg) {
|
||||
t.Logf("decoded=%+v\n", decoded)
|
||||
@@ -52,14 +52,14 @@ func testRoundTripWithLength(t *testing.T, e *encoder.SszNetworkEncoder) {
|
||||
|
||||
func testRoundTripWithGossip(t *testing.T, e *encoder.SszNetworkEncoder) {
|
||||
buf := new(bytes.Buffer)
|
||||
msg := &statepb.Fork{
|
||||
msg := ðpb.Fork{
|
||||
PreviousVersion: []byte("fooo"),
|
||||
CurrentVersion: []byte("barr"),
|
||||
Epoch: 9001,
|
||||
}
|
||||
_, err := e.EncodeGossip(buf, msg)
|
||||
require.NoError(t, err)
|
||||
decoded := &statepb.Fork{}
|
||||
decoded := ðpb.Fork{}
|
||||
require.NoError(t, e.DecodeGossip(buf.Bytes(), decoded))
|
||||
if !proto.Equal(decoded, msg) {
|
||||
t.Logf("decoded=%+v\n", decoded)
|
||||
@@ -69,7 +69,7 @@ func testRoundTripWithGossip(t *testing.T, e *encoder.SszNetworkEncoder) {
|
||||
|
||||
func TestSszNetworkEncoder_EncodeWithMaxLength(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
msg := &statepb.Fork{
|
||||
msg := ðpb.Fork{
|
||||
PreviousVersion: []byte("fooo"),
|
||||
CurrentVersion: []byte("barr"),
|
||||
Epoch: 9001,
|
||||
@@ -86,7 +86,7 @@ func TestSszNetworkEncoder_EncodeWithMaxLength(t *testing.T) {
|
||||
|
||||
func TestSszNetworkEncoder_DecodeWithMaxLength(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
msg := &statepb.Fork{
|
||||
msg := ðpb.Fork{
|
||||
PreviousVersion: []byte("fooo"),
|
||||
CurrentVersion: []byte("barr"),
|
||||
Epoch: 4242,
|
||||
@@ -99,7 +99,7 @@ func TestSszNetworkEncoder_DecodeWithMaxLength(t *testing.T) {
|
||||
params.OverrideBeaconNetworkConfig(c)
|
||||
_, err := e.EncodeGossip(buf, msg)
|
||||
require.NoError(t, err)
|
||||
decoded := &statepb.Fork{}
|
||||
decoded := ðpb.Fork{}
|
||||
err = e.DecodeWithMaxLength(buf, decoded)
|
||||
wanted := fmt.Sprintf("goes over the provided max limit of %d", maxChunkSize)
|
||||
assert.ErrorContains(t, wanted, err)
|
||||
@@ -115,13 +115,13 @@ func TestSszNetworkEncoder_DecodeWithMultipleFrames(t *testing.T) {
|
||||
maxChunkSize := uint64(1 << 22)
|
||||
c.MaxChunkSize = maxChunkSize
|
||||
params.OverrideBeaconNetworkConfig(c)
|
||||
_, err := e.EncodeWithMaxLength(buf, st.InnerStateUnsafe())
|
||||
_, err := e.EncodeWithMaxLength(buf, st.InnerStateUnsafe().(*ethpb.BeaconState))
|
||||
require.NoError(t, err)
|
||||
// Max snappy block size
|
||||
if buf.Len() <= 76490 {
|
||||
t.Errorf("buffer smaller than expected, wanted > %d but got %d", 76490, buf.Len())
|
||||
}
|
||||
decoded := new(statepb.BeaconState)
|
||||
decoded := new(ethpb.BeaconState)
|
||||
err = e.DecodeWithMaxLength(buf, decoded)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@@ -144,7 +144,7 @@ func TestSszNetworkEncoder_MaxInt64(t *testing.T) {
|
||||
func TestSszNetworkEncoder_DecodeWithBadSnappyStream(t *testing.T) {
|
||||
st := newBadSnappyStream()
|
||||
e := &encoder.SszNetworkEncoder{}
|
||||
decoded := new(statepb.Fork)
|
||||
decoded := new(ethpb.Fork)
|
||||
err := e.DecodeWithMaxLength(st, decoded)
|
||||
assert.ErrorContains(t, io.EOF.Error(), err)
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@ package p2p
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/enr"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/p2putils"
|
||||
@@ -20,16 +20,12 @@ import (
|
||||
var eth2ENRKey = params.BeaconNetworkConfig().ETH2Key
|
||||
|
||||
// ForkDigest returns the current fork digest of
|
||||
// the node.
|
||||
func (s *Service) forkDigest() ([4]byte, error) {
|
||||
if s.currentForkDigest != [4]byte{} {
|
||||
return s.currentForkDigest, nil
|
||||
// the node according to the local clock.
|
||||
func (s *Service) currentForkDigest() ([4]byte, error) {
|
||||
if !s.isInitialized() {
|
||||
return [4]byte{}, errors.New("state is not initialized")
|
||||
}
|
||||
fd, err := p2putils.CreateForkDigest(s.genesisTime, s.genesisValidatorsRoot)
|
||||
if err != nil {
|
||||
s.currentForkDigest = fd
|
||||
}
|
||||
return fd, err
|
||||
return p2putils.CreateForkDigest(s.genesisTime, s.genesisValidatorsRoot)
|
||||
}
|
||||
|
||||
// Compares fork ENRs between an incoming peer's record and our node's
|
||||
@@ -97,20 +93,10 @@ func addForkEntry(
|
||||
if timeutils.Now().Before(genesisTime) {
|
||||
currentEpoch = 0
|
||||
}
|
||||
fork, err := p2putils.Fork(currentEpoch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nextForkEpoch := params.BeaconConfig().NextForkEpoch
|
||||
nextForkVersion := params.BeaconConfig().NextForkVersion
|
||||
// Set to the current fork version if our next fork is not planned.
|
||||
if nextForkEpoch == math.MaxUint64 {
|
||||
nextForkVersion = fork.CurrentVersion
|
||||
}
|
||||
nextForkVersion, nextForkEpoch := p2putils.NextForkData(currentEpoch)
|
||||
enrForkID := &pb.ENRForkID{
|
||||
CurrentForkDigest: digest[:],
|
||||
NextForkVersion: nextForkVersion,
|
||||
NextForkVersion: nextForkVersion[:],
|
||||
NextForkEpoch: nextForkEpoch,
|
||||
}
|
||||
enc, err := enrForkID.MarshalSSZ()
|
||||
|
||||
@@ -144,7 +144,7 @@ func TestStartDiscv5_SameForkDigests_DifferentNextForkData(t *testing.T) {
|
||||
|
||||
c := params.BeaconConfig()
|
||||
nextForkEpoch := types.Epoch(i)
|
||||
c.NextForkEpoch = nextForkEpoch
|
||||
c.ForkVersionSchedule[[4]byte{'A', 'B', 'C', 'D'}] = nextForkEpoch
|
||||
params.OverrideBeaconConfig(c)
|
||||
|
||||
// We give every peer a different genesis validators root, which
|
||||
@@ -209,14 +209,12 @@ func TestStartDiscv5_SameForkDigests_DifferentNextForkData(t *testing.T) {
|
||||
func TestDiscv5_AddRetrieveForkEntryENR(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
c := params.BeaconConfig()
|
||||
c.ForkVersionSchedule = map[types.Epoch][]byte{
|
||||
0: params.BeaconConfig().GenesisForkVersion,
|
||||
1: {0, 0, 0, 1},
|
||||
c.ForkVersionSchedule = map[[4]byte]types.Epoch{
|
||||
bytesutil.ToBytes4(params.BeaconConfig().GenesisForkVersion): 0,
|
||||
{0, 0, 0, 1}: 1,
|
||||
}
|
||||
nextForkEpoch := types.Epoch(1)
|
||||
nextForkVersion := []byte{0, 0, 0, 1}
|
||||
c.NextForkEpoch = nextForkEpoch
|
||||
c.NextForkVersion = nextForkVersion
|
||||
params.OverrideBeaconConfig(c)
|
||||
|
||||
genesisTime := time.Now()
|
||||
@@ -255,6 +253,7 @@ func TestDiscv5_AddRetrieveForkEntryENR(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAddForkEntry_Genesis(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
temp := t.TempDir()
|
||||
randNum := rand.Int()
|
||||
tempPath := path.Join(temp, strconv.Itoa(randNum))
|
||||
@@ -264,6 +263,11 @@ func TestAddForkEntry_Genesis(t *testing.T) {
|
||||
db, err := enode.OpenDB("")
|
||||
require.NoError(t, err)
|
||||
|
||||
bCfg := params.BeaconConfig()
|
||||
bCfg.ForkVersionSchedule = map[[4]byte]types.Epoch{}
|
||||
bCfg.ForkVersionSchedule[bytesutil.ToBytes4(params.BeaconConfig().GenesisForkVersion)] = bCfg.GenesisEpoch
|
||||
params.OverrideBeaconConfig(bCfg)
|
||||
|
||||
localNode := enode.NewLocalNode(db, pkey)
|
||||
localNode, err = addForkEntry(localNode, time.Now().Add(10*time.Second), bytesutil.PadTo([]byte{'A', 'B', 'C', 'D'}, 32))
|
||||
require.NoError(t, err)
|
||||
|
||||
30
beacon-chain/p2p/fork_watcher.go
Normal file
30
beacon-chain/p2p/fork_watcher.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package p2p
|
||||
|
||||
import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/slotutil"
|
||||
)
|
||||
|
||||
// A background routine which listens for new and upcoming forks and
|
||||
// updates the node's discovery service to reflect any new fork version
|
||||
// changes.
|
||||
func (s *Service) forkWatcher() {
|
||||
slotTicker := slotutil.NewSlotTicker(s.genesisTime, params.BeaconConfig().SecondsPerSlot)
|
||||
for {
|
||||
select {
|
||||
case currSlot := <-slotTicker.C():
|
||||
currEpoch := helpers.SlotToEpoch(currSlot)
|
||||
if currEpoch == params.BeaconConfig().AltairForkEpoch {
|
||||
_, err := addForkEntry(s.dv5Listener.LocalNode(), s.genesisTime, s.genesisValidatorsRoot)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not add fork entry")
|
||||
}
|
||||
}
|
||||
case <-s.ctx.Done():
|
||||
log.Debug("Context closed, exiting goroutine")
|
||||
slotTicker.Done()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,9 +21,15 @@ const (
|
||||
// aggregateWeight specifies the scoring weight that we apply to
|
||||
// our aggregate topic.
|
||||
aggregateWeight = 0.5
|
||||
// syncContributionWeight specifies the scoring weight that we apply to
|
||||
// our sync contribution topic.
|
||||
syncContributionWeight = 0.2
|
||||
// attestationTotalWeight specifies the scoring weight that we apply to
|
||||
// our attestation subnet topic.
|
||||
attestationTotalWeight = 1
|
||||
// syncCommitteesTotalWeight specifies the scoring weight that we apply to
|
||||
// our sync subnet topic.
|
||||
syncCommitteesTotalWeight = 0.4
|
||||
// attesterSlashingWeight specifies the scoring weight that we apply to
|
||||
// our attester slashing topic.
|
||||
attesterSlashingWeight = 0.05
|
||||
@@ -92,6 +98,10 @@ func (s *Service) topicScoreParams(topic string) (*pubsub.TopicScoreParams, erro
|
||||
return defaultAggregateTopicParams(activeValidators)
|
||||
case strings.Contains(topic, "beacon_attestation"):
|
||||
return defaultAggregateSubnetTopicParams(activeValidators)
|
||||
case strings.Contains(topic, GossipSyncCommitteeMessage):
|
||||
return defaultSyncSubnetTopicParams(activeValidators)
|
||||
case strings.Contains(topic, "sync_committee_contribution_and_proof"):
|
||||
return defaultSyncContributionTopicParams()
|
||||
case strings.Contains(topic, "voluntary_exit"):
|
||||
return defaultVoluntaryExitTopicParams(), nil
|
||||
case strings.Contains(topic, "proposer_slashing"):
|
||||
@@ -215,6 +225,48 @@ func defaultAggregateTopicParams(activeValidators uint64) (*pubsub.TopicScorePar
|
||||
}, nil
|
||||
}
|
||||
|
||||
func defaultSyncContributionTopicParams() (*pubsub.TopicScoreParams, error) {
|
||||
// Determine the expected message rate for the particular gossip topic.
|
||||
aggPerSlot := params.BeaconConfig().SyncCommitteeSubnetCount * params.BeaconConfig().TargetAggregatorsPerSyncSubcommittee
|
||||
firstMessageCap, err := decayLimit(scoreDecay(1*oneEpochDuration()), float64(aggPerSlot*2/gossipSubD))
|
||||
if err != nil {
|
||||
log.Warnf("skipping initializing topic scoring: %v", err)
|
||||
return nil, nil
|
||||
}
|
||||
firstMessageWeight := maxFirstDeliveryScore / firstMessageCap
|
||||
meshThreshold, err := decayThreshold(scoreDecay(1*oneEpochDuration()), float64(aggPerSlot)/dampeningFactor)
|
||||
if err != nil {
|
||||
log.Warnf("skipping initializing topic scoring: %v", err)
|
||||
return nil, nil
|
||||
}
|
||||
meshWeight := -scoreByWeight(syncContributionWeight, meshThreshold)
|
||||
meshCap := 4 * meshThreshold
|
||||
if !meshDeliveryIsScored {
|
||||
// Set the mesh weight as zero as a temporary measure, so as to prevent
|
||||
// the average nodes from being penalised.
|
||||
meshWeight = 0
|
||||
}
|
||||
return &pubsub.TopicScoreParams{
|
||||
TopicWeight: syncContributionWeight,
|
||||
TimeInMeshWeight: maxInMeshScore / inMeshCap(),
|
||||
TimeInMeshQuantum: inMeshTime(),
|
||||
TimeInMeshCap: inMeshCap(),
|
||||
FirstMessageDeliveriesWeight: firstMessageWeight,
|
||||
FirstMessageDeliveriesDecay: scoreDecay(1 * oneEpochDuration()),
|
||||
FirstMessageDeliveriesCap: firstMessageCap,
|
||||
MeshMessageDeliveriesWeight: meshWeight,
|
||||
MeshMessageDeliveriesDecay: scoreDecay(1 * oneEpochDuration()),
|
||||
MeshMessageDeliveriesCap: meshCap,
|
||||
MeshMessageDeliveriesThreshold: meshThreshold,
|
||||
MeshMessageDeliveriesWindow: 2 * time.Second,
|
||||
MeshMessageDeliveriesActivation: 1 * oneEpochDuration(),
|
||||
MeshFailurePenaltyWeight: meshWeight,
|
||||
MeshFailurePenaltyDecay: scoreDecay(1 * oneEpochDuration()),
|
||||
InvalidMessageDeliveriesWeight: -maxScore() / syncContributionWeight,
|
||||
InvalidMessageDeliveriesDecay: scoreDecay(50 * oneEpochDuration()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func defaultAggregateSubnetTopicParams(activeValidators uint64) (*pubsub.TopicScoreParams, error) {
|
||||
subnetCount := params.BeaconNetworkConfig().AttestationSubnetCount
|
||||
// Get weight for each specific subnet.
|
||||
@@ -238,8 +290,13 @@ func defaultAggregateSubnetTopicParams(activeValidators uint64) (*pubsub.TopicSc
|
||||
firstDecay = 4
|
||||
meshDecay = 16
|
||||
}
|
||||
rate := numPerSlot * 2 / gossipSubD
|
||||
if rate == 0 {
|
||||
log.Warn("rate is 0, skipping initializing topic scoring")
|
||||
return nil, nil
|
||||
}
|
||||
// Determine expected first deliveries based on the message rate.
|
||||
firstMessageCap, err := decayLimit(scoreDecay(firstDecay*oneEpochDuration()), float64(numPerSlot*2/gossipSubD))
|
||||
firstMessageCap, err := decayLimit(scoreDecay(firstDecay*oneEpochDuration()), float64(rate))
|
||||
if err != nil {
|
||||
log.Warnf("skipping initializing topic scoring: %v", err)
|
||||
return nil, nil
|
||||
@@ -279,6 +336,69 @@ func defaultAggregateSubnetTopicParams(activeValidators uint64) (*pubsub.TopicSc
|
||||
}, nil
|
||||
}
|
||||
|
||||
func defaultSyncSubnetTopicParams(activeValidators uint64) (*pubsub.TopicScoreParams, error) {
|
||||
subnetCount := params.BeaconConfig().SyncCommitteeSubnetCount
|
||||
// Get weight for each specific subnet.
|
||||
topicWeight := syncCommitteesTotalWeight / float64(subnetCount)
|
||||
syncComSize := params.BeaconConfig().SyncCommitteeSize
|
||||
// Set the max as the sync committee size
|
||||
if activeValidators > syncComSize {
|
||||
activeValidators = syncComSize
|
||||
}
|
||||
subnetWeight := activeValidators / subnetCount
|
||||
if subnetWeight == 0 {
|
||||
log.Warn("Subnet weight is 0, skipping initializing topic scoring")
|
||||
return nil, nil
|
||||
}
|
||||
firstDecay := time.Duration(1)
|
||||
meshDecay := time.Duration(4)
|
||||
|
||||
rate := subnetWeight * 2 / gossipSubD
|
||||
if rate == 0 {
|
||||
log.Warn("rate is 0, skipping initializing topic scoring")
|
||||
return nil, nil
|
||||
}
|
||||
// Determine expected first deliveries based on the message rate.
|
||||
firstMessageCap, err := decayLimit(scoreDecay(firstDecay*oneEpochDuration()), float64(rate))
|
||||
if err != nil {
|
||||
log.Warnf("skipping initializing topic scoring: %v", err)
|
||||
return nil, nil
|
||||
}
|
||||
firstMessageWeight := maxFirstDeliveryScore / firstMessageCap
|
||||
// Determine expected mesh deliveries based on message rate applied with a dampening factor.
|
||||
meshThreshold, err := decayThreshold(scoreDecay(meshDecay*oneEpochDuration()), float64(subnetWeight)/dampeningFactor)
|
||||
if err != nil {
|
||||
log.Warnf("skipping initializing topic scoring: %v", err)
|
||||
return nil, nil
|
||||
}
|
||||
meshWeight := -scoreByWeight(topicWeight, meshThreshold)
|
||||
meshCap := 4 * meshThreshold
|
||||
if !meshDeliveryIsScored {
|
||||
// Set the mesh weight as zero as a temporary measure, so as to prevent
|
||||
// the average nodes from being penalised.
|
||||
meshWeight = 0
|
||||
}
|
||||
return &pubsub.TopicScoreParams{
|
||||
TopicWeight: topicWeight,
|
||||
TimeInMeshWeight: maxInMeshScore / inMeshCap(),
|
||||
TimeInMeshQuantum: inMeshTime(),
|
||||
TimeInMeshCap: inMeshCap(),
|
||||
FirstMessageDeliveriesWeight: firstMessageWeight,
|
||||
FirstMessageDeliveriesDecay: scoreDecay(firstDecay * oneEpochDuration()),
|
||||
FirstMessageDeliveriesCap: firstMessageCap,
|
||||
MeshMessageDeliveriesWeight: meshWeight,
|
||||
MeshMessageDeliveriesDecay: scoreDecay(meshDecay * oneEpochDuration()),
|
||||
MeshMessageDeliveriesCap: meshCap,
|
||||
MeshMessageDeliveriesThreshold: meshThreshold,
|
||||
MeshMessageDeliveriesWindow: 2 * time.Second,
|
||||
MeshMessageDeliveriesActivation: 1 * oneEpochDuration(),
|
||||
MeshFailurePenaltyWeight: meshWeight,
|
||||
MeshFailurePenaltyDecay: scoreDecay(meshDecay * oneEpochDuration()),
|
||||
InvalidMessageDeliveriesWeight: -maxScore() / topicWeight,
|
||||
InvalidMessageDeliveriesDecay: scoreDecay(50 * oneEpochDuration()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func defaultAttesterSlashingTopicParams() *pubsub.TopicScoreParams {
|
||||
return &pubsub.TopicScoreParams{
|
||||
TopicWeight: attesterSlashingWeight,
|
||||
@@ -401,8 +521,9 @@ func scoreByWeight(weight, threshold float64) float64 {
|
||||
|
||||
// maxScore attainable by a peer.
|
||||
func maxScore() float64 {
|
||||
totalWeight := beaconBlockWeight + aggregateWeight + attestationTotalWeight +
|
||||
attesterSlashingWeight + proposerSlashingWeight + voluntaryExitWeight
|
||||
totalWeight := beaconBlockWeight + aggregateWeight + +syncContributionWeight +
|
||||
attestationTotalWeight + syncCommitteesTotalWeight + attesterSlashingWeight +
|
||||
proposerSlashingWeight + voluntaryExitWeight
|
||||
return (maxInMeshScore + maxFirstDeliveryScore) * totalWeight
|
||||
}
|
||||
|
||||
|
||||
@@ -3,27 +3,53 @@ package p2p
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// GossipTopicMappings represent the protocol ID to protobuf message type map for easy
|
||||
// gossipTopicMappings represent the protocol ID to protobuf message type map for easy
|
||||
// lookup.
|
||||
var GossipTopicMappings = map[string]proto.Message{
|
||||
BlockSubnetTopicFormat: &pb.SignedBeaconBlock{},
|
||||
AttestationSubnetTopicFormat: &pb.Attestation{},
|
||||
ExitSubnetTopicFormat: &pb.SignedVoluntaryExit{},
|
||||
ProposerSlashingSubnetTopicFormat: &pb.ProposerSlashing{},
|
||||
AttesterSlashingSubnetTopicFormat: &pb.AttesterSlashing{},
|
||||
AggregateAndProofSubnetTopicFormat: &pb.SignedAggregateAttestationAndProof{},
|
||||
var gossipTopicMappings = map[string]proto.Message{
|
||||
BlockSubnetTopicFormat: &pb.SignedBeaconBlock{},
|
||||
AttestationSubnetTopicFormat: &pb.Attestation{},
|
||||
ExitSubnetTopicFormat: &pb.SignedVoluntaryExit{},
|
||||
ProposerSlashingSubnetTopicFormat: &pb.ProposerSlashing{},
|
||||
AttesterSlashingSubnetTopicFormat: &pb.AttesterSlashing{},
|
||||
AggregateAndProofSubnetTopicFormat: &pb.SignedAggregateAttestationAndProof{},
|
||||
SyncContributionAndProofSubnetTopicFormat: ðpb.SignedContributionAndProof{},
|
||||
SyncCommitteeSubnetTopicFormat: ðpb.SyncCommitteeMessage{},
|
||||
}
|
||||
|
||||
// GossipTopicMappings is a function to return the assigned data type
|
||||
// versioned by epoch.
|
||||
func GossipTopicMappings(topic string, epoch types.Epoch) proto.Message {
|
||||
if topic == BlockSubnetTopicFormat && epoch >= params.BeaconConfig().AltairForkEpoch {
|
||||
return ðpb.SignedBeaconBlockAltair{}
|
||||
}
|
||||
return gossipTopicMappings[topic]
|
||||
}
|
||||
|
||||
// AllTopics returns all topics stored in our
|
||||
// gossip mapping.
|
||||
func AllTopics() []string {
|
||||
topics := []string{}
|
||||
for k := range gossipTopicMappings {
|
||||
topics = append(topics, k)
|
||||
}
|
||||
return topics
|
||||
}
|
||||
|
||||
// GossipTypeMapping is the inverse of GossipTopicMappings so that an arbitrary protobuf message
|
||||
// can be mapped to a protocol ID string.
|
||||
var GossipTypeMapping = make(map[reflect.Type]string, len(GossipTopicMappings))
|
||||
var GossipTypeMapping = make(map[reflect.Type]string, len(gossipTopicMappings))
|
||||
|
||||
func init() {
|
||||
for k, v := range GossipTopicMappings {
|
||||
for k, v := range gossipTopicMappings {
|
||||
GossipTypeMapping[reflect.TypeOf(v)] = k
|
||||
}
|
||||
// Specially handle Altair Objects.
|
||||
GossipTypeMapping[reflect.TypeOf(ðpb.SignedBeaconBlockAltair{})] = BlockSubnetTopicFormat
|
||||
}
|
||||
|
||||
@@ -3,14 +3,39 @@ package p2p
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
eth2types "github.com/prysmaticlabs/eth2-types"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
)
|
||||
|
||||
func TestMappingHasNoDuplicates(t *testing.T) {
|
||||
m := make(map[reflect.Type]bool)
|
||||
for _, v := range GossipTopicMappings {
|
||||
for _, v := range gossipTopicMappings {
|
||||
if _, ok := m[reflect.TypeOf(v)]; ok {
|
||||
t.Errorf("%T is duplicated in the topic mapping", v)
|
||||
}
|
||||
m[reflect.TypeOf(v)] = true
|
||||
}
|
||||
}
|
||||
|
||||
func TestGossipTopicMappings_CorrectBlockType(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
bCfg := params.BeaconConfig()
|
||||
forkEpoch := eth2types.Epoch(100)
|
||||
bCfg.AltairForkEpoch = forkEpoch
|
||||
bCfg.ForkVersionSchedule[bytesutil.ToBytes4(bCfg.AltairForkVersion)] = eth2types.Epoch(100)
|
||||
params.OverrideBeaconConfig(bCfg)
|
||||
|
||||
// Before Fork
|
||||
pMessage := GossipTopicMappings(BlockSubnetTopicFormat, 0)
|
||||
_, ok := pMessage.(*ethpb.SignedBeaconBlock)
|
||||
assert.Equal(t, true, ok)
|
||||
|
||||
// After Fork
|
||||
pMessage = GossipTopicMappings(BlockSubnetTopicFormat, forkEpoch)
|
||||
_, ok = pMessage.(*ethpb.SignedBeaconBlockAltair)
|
||||
assert.Equal(t, true, ok)
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ type P2P interface {
|
||||
type Broadcaster interface {
|
||||
Broadcast(context.Context, proto.Message) error
|
||||
BroadcastAttestation(ctx context.Context, subnet uint64, att *ethpb.Attestation) error
|
||||
BroadcastSyncCommitteeMessage(ctx context.Context, subnet uint64, sMsg *ethpb.SyncCommitteeMessage) error
|
||||
}
|
||||
|
||||
// SetStreamHandler configures p2p to handle streams of a certain topic ID.
|
||||
|
||||
@@ -25,6 +25,16 @@ var (
|
||||
Name: "p2p_attestation_subnet_attempted_broadcasts",
|
||||
Help: "The number of attestations that were attempted to be broadcast.",
|
||||
})
|
||||
savedSyncCommitteeBroadcasts = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "p2p_sync_committee_subnet_recovered_broadcasts",
|
||||
Help: "The number of sync committee messages that were attempted to be broadcast with no peers on " +
|
||||
"the subnet. The beacon node increments this counter when the broadcast is blocked " +
|
||||
"until a subnet peer can be found.",
|
||||
})
|
||||
syncCommitteeBroadcastAttempts = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "p2p_sync_committee_subnet_attempted_broadcasts",
|
||||
Help: "The number of sync committee that were attempted to be broadcast.",
|
||||
})
|
||||
)
|
||||
|
||||
func (s *Service) updateMetrics() {
|
||||
|
||||
@@ -235,7 +235,7 @@ func (p *Status) SetMetadata(pid peer.ID, metaData metadata.Metadata) {
|
||||
defer p.store.Unlock()
|
||||
|
||||
peerData := p.store.PeerDataGetOrCreate(pid)
|
||||
peerData.MetaData = metaData
|
||||
peerData.MetaData = metaData.Copy()
|
||||
}
|
||||
|
||||
// Metadata returns a copy of the metadata corresponding to the provided
|
||||
|
||||
@@ -2,16 +2,21 @@ package p2p
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
pubsub_pb "github.com/libp2p/go-libp2p-pubsub/pb"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/encoder"
|
||||
"github.com/prysmaticlabs/prysm/cmd/beacon-chain/flags"
|
||||
pbrpc "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/p2putils"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
@@ -36,6 +41,11 @@ const (
|
||||
randomSubD = 6 // random gossip target
|
||||
)
|
||||
|
||||
var errInvalidTopic = errors.New("invalid topic format")
|
||||
|
||||
// Specifies the fixed size context length.
|
||||
const digestLength = 4
|
||||
|
||||
// JoinTopic will join PubSub topic, if not already joined.
|
||||
func (s *Service) JoinTopic(topic string, opts ...pubsub.TopicOpt) (*pubsub.Topic, error) {
|
||||
s.joinedTopicsLock.Lock()
|
||||
@@ -132,7 +142,26 @@ func (s *Service) peerInspector(peerMap map[peer.ID]*pubsub.PeerScoreSnapshot) {
|
||||
// Otherwise, set `message-id` to the first 20 bytes of the `SHA256` hash of
|
||||
// the concatenation of `MESSAGE_DOMAIN_INVALID_SNAPPY` with the raw message data,
|
||||
// i.e. `SHA256(MESSAGE_DOMAIN_INVALID_SNAPPY + message.data)[:20]`.
|
||||
func msgIDFunction(pmsg *pubsub_pb.Message) string {
|
||||
func (s *Service) msgIDFunction(pmsg *pubsub_pb.Message) string {
|
||||
digest, err := ExtractGossipDigest(*pmsg.Topic)
|
||||
if err != nil {
|
||||
// Impossible condition that should
|
||||
// never be hit.
|
||||
msg := make([]byte, 20)
|
||||
copy(msg, "invalid")
|
||||
return string(msg)
|
||||
}
|
||||
_, fEpoch, err := p2putils.RetrieveForkDataFromDigest(digest, s.genesisValidatorsRoot)
|
||||
if err != nil {
|
||||
// Impossible condition that should
|
||||
// never be hit.
|
||||
msg := make([]byte, 20)
|
||||
copy(msg, "invalid")
|
||||
return string(msg)
|
||||
}
|
||||
if fEpoch >= params.BeaconConfig().AltairForkEpoch {
|
||||
return s.altairMsgID(pmsg)
|
||||
}
|
||||
decodedData, err := encoder.DecodeSnappy(pmsg.Data, params.BeaconNetworkConfig().GossipMaxSize)
|
||||
if err != nil {
|
||||
combinedData := append(params.BeaconNetworkConfig().MessageDomainInvalidSnappy[:], pmsg.Data...)
|
||||
@@ -144,6 +173,43 @@ func msgIDFunction(pmsg *pubsub_pb.Message) string {
|
||||
return string(h[:20])
|
||||
}
|
||||
|
||||
// Spec:
|
||||
// The derivation of the message-id has changed starting with Altair to incorporate the message topic along with the message data.
|
||||
// These are fields of the Message Protobuf, and interpreted as empty byte strings if missing. The message-id MUST be the following
|
||||
// 20 byte value computed from the message:
|
||||
//
|
||||
// If message.data has a valid snappy decompression, set message-id to the first 20 bytes of the SHA256 hash of the concatenation of
|
||||
// the following data: MESSAGE_DOMAIN_VALID_SNAPPY, the length of the topic byte string (encoded as little-endian uint64), the topic
|
||||
// byte string, and the snappy decompressed message data: i.e. SHA256(MESSAGE_DOMAIN_VALID_SNAPPY + uint_to_bytes(uint64(len(message.topic)))
|
||||
// + message.topic + snappy_decompress(message.data))[:20]. Otherwise, set message-id to the first 20 bytes of the SHA256 hash of the concatenation
|
||||
// of the following data: MESSAGE_DOMAIN_INVALID_SNAPPY, the length of the topic byte string (encoded as little-endian uint64),
|
||||
// the topic byte string, and the raw message data: i.e. SHA256(MESSAGE_DOMAIN_INVALID_SNAPPY + uint_to_bytes(uint64(len(message.topic))) + message.topic + message.data)[:20].
|
||||
func (s *Service) altairMsgID(pmsg *pubsub_pb.Message) string {
|
||||
topic := *pmsg.Topic
|
||||
topicLen := uint64(len(topic))
|
||||
topicLenBytes := bytesutil.Uint64ToBytesLittleEndian(topicLen)
|
||||
|
||||
decodedData, err := encoder.DecodeSnappy(pmsg.Data, params.BeaconNetworkConfig().GossipMaxSize)
|
||||
if err != nil {
|
||||
totalLength := len(params.BeaconNetworkConfig().MessageDomainInvalidSnappy) + len(topicLenBytes) + int(topicLen) + len(pmsg.Data)
|
||||
combinedData := make([]byte, 0, totalLength)
|
||||
combinedData = append(combinedData, params.BeaconNetworkConfig().MessageDomainInvalidSnappy[:]...)
|
||||
combinedData = append(combinedData, topicLenBytes...)
|
||||
combinedData = append(combinedData, topic...)
|
||||
combinedData = append(combinedData, pmsg.Data...)
|
||||
h := hashutil.Hash(combinedData)
|
||||
return string(h[:20])
|
||||
}
|
||||
totalLength := len(params.BeaconNetworkConfig().MessageDomainValidSnappy) + len(topicLenBytes) + int(topicLen) + len(decodedData)
|
||||
combinedData := make([]byte, 0, totalLength)
|
||||
combinedData = append(combinedData, params.BeaconNetworkConfig().MessageDomainValidSnappy[:]...)
|
||||
combinedData = append(combinedData, topicLenBytes...)
|
||||
combinedData = append(combinedData, topic...)
|
||||
combinedData = append(combinedData, decodedData...)
|
||||
h := hashutil.Hash(combinedData)
|
||||
return string(h[:20])
|
||||
}
|
||||
|
||||
// creates a custom gossipsub parameter set.
|
||||
func pubsubGossipParam() pubsub.GossipSubParams {
|
||||
gParams := pubsub.DefaultGossipSubParams()
|
||||
@@ -184,3 +250,27 @@ func convertTopicScores(topicMap map[string]*pubsub.TopicScoreSnapshot) map[stri
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
|
||||
// Extracts the relevant fork digest from the gossip topic.
|
||||
func ExtractGossipDigest(topic string) ([4]byte, error) {
|
||||
splitParts := strings.Split(topic, "/")
|
||||
parts := []string{}
|
||||
for _, p := range splitParts {
|
||||
if p == "" {
|
||||
continue
|
||||
}
|
||||
parts = append(parts, p)
|
||||
}
|
||||
if len(parts) < 2 {
|
||||
return [4]byte{}, errors.Wrapf(errInvalidTopic, "it only has %d parts: %v", len(parts), parts)
|
||||
}
|
||||
strDigest := parts[1]
|
||||
digest, err := hex.DecodeString(strDigest)
|
||||
if err != nil {
|
||||
return [4]byte{}, err
|
||||
}
|
||||
if len(digest) != digestLength {
|
||||
return [4]byte{}, errors.Errorf("invalid digest length wanted %d but got %d", digestLength, len(digest))
|
||||
}
|
||||
return bytesutil.ToBytes4(digest), nil
|
||||
}
|
||||
|
||||
@@ -8,11 +8,18 @@ import (
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
pubsubpb "github.com/libp2p/go-libp2p-pubsub/pb"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/encoder"
|
||||
"github.com/prysmaticlabs/prysm/shared/p2putils"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
var _ pubsub.SubscriptionFilter = (*Service)(nil)
|
||||
|
||||
const pubsubSubscriptionRequestLimit = 100
|
||||
// It is set at this limit to handle the possibility
|
||||
// of double topic subscriptions at fork boundaries.
|
||||
// -> 64 Attestation Subnets * 2.
|
||||
// -> 4 Sync Committee Subnets * 2.
|
||||
// -> Block,Aggregate,ProposerSlashing,AttesterSlashing,Exits,SyncContribution * 2.
|
||||
const pubsubSubscriptionRequestLimit = 200
|
||||
|
||||
// CanSubscribe returns true if the topic is of interest and we could subscribe to it.
|
||||
func (s *Service) CanSubscribe(topic string) bool {
|
||||
@@ -30,12 +37,17 @@ func (s *Service) CanSubscribe(topic string) bool {
|
||||
if parts[1] != "eth2" {
|
||||
return false
|
||||
}
|
||||
fd, err := s.forkDigest()
|
||||
fd, err := s.currentForkDigest()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not determine fork digest")
|
||||
return false
|
||||
}
|
||||
if parts[2] != fmt.Sprintf("%x", fd) {
|
||||
digest, err := p2putils.ForkDigestFromEpoch(params.BeaconConfig().AltairForkEpoch, s.genesisValidatorsRoot)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not determine next fork digest")
|
||||
return false
|
||||
}
|
||||
if parts[2] != fmt.Sprintf("%x", fd) && parts[2] != fmt.Sprintf("%x", digest) {
|
||||
return false
|
||||
}
|
||||
if parts[4] != encoder.ProtocolSuffixSSZSnappy {
|
||||
@@ -43,7 +55,7 @@ func (s *Service) CanSubscribe(topic string) bool {
|
||||
}
|
||||
|
||||
// Check the incoming topic matches any topic mapping. This includes a check for part[3].
|
||||
for gt := range GossipTopicMappings {
|
||||
for gt := range gossipTopicMappings {
|
||||
if _, err := scanfcheck(strings.Join(parts[0:4], "/"), gt); err == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ import (
|
||||
statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/encoder"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/p2putils"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/timeutils"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -22,6 +23,10 @@ import (
|
||||
func TestService_CanSubscribe(t *testing.T) {
|
||||
currentFork := [4]byte{0x01, 0x02, 0x03, 0x04}
|
||||
validProtocolSuffix := "/" + encoder.ProtocolSuffixSSZSnappy
|
||||
genesisTime := time.Now()
|
||||
valRoot := [32]byte{}
|
||||
digest, err := p2putils.CreateForkDigest(genesisTime, valRoot[:])
|
||||
assert.NoError(t, err)
|
||||
type test struct {
|
||||
name string
|
||||
topic string
|
||||
@@ -30,7 +35,7 @@ func TestService_CanSubscribe(t *testing.T) {
|
||||
tests := []test{
|
||||
{
|
||||
name: "block topic on current fork",
|
||||
topic: fmt.Sprintf(BlockSubnetTopicFormat, currentFork) + validProtocolSuffix,
|
||||
topic: fmt.Sprintf(BlockSubnetTopicFormat, digest) + validProtocolSuffix,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
@@ -60,17 +65,17 @@ func TestService_CanSubscribe(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "bad prefix",
|
||||
topic: fmt.Sprintf("/eth3/%x/foobar", currentFork) + validProtocolSuffix,
|
||||
topic: fmt.Sprintf("/eth3/%x/foobar", digest) + validProtocolSuffix,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "topic not in gossip mapping",
|
||||
topic: fmt.Sprintf("/eth2/%x/foobar", currentFork) + validProtocolSuffix,
|
||||
topic: fmt.Sprintf("/eth2/%x/foobar", digest) + validProtocolSuffix,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "att subnet topic on current fork",
|
||||
topic: fmt.Sprintf(AttestationSubnetTopicFormat, currentFork, 55 /*subnet*/) + validProtocolSuffix,
|
||||
topic: fmt.Sprintf(AttestationSubnetTopicFormat, digest, 55 /*subnet*/) + validProtocolSuffix,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
@@ -81,11 +86,11 @@ func TestService_CanSubscribe(t *testing.T) {
|
||||
}
|
||||
|
||||
// Ensure all gossip topic mappings pass validation.
|
||||
for topic := range GossipTopicMappings {
|
||||
formatting := []interface{}{currentFork}
|
||||
for _, topic := range AllTopics() {
|
||||
formatting := []interface{}{digest}
|
||||
|
||||
// Special case for attestation subnets which have a second formatting placeholder.
|
||||
if topic == AttestationSubnetTopicFormat {
|
||||
if topic == AttestationSubnetTopicFormat || topic == SyncCommitteeSubnetTopicFormat {
|
||||
formatting = append(formatting, 0 /* some subnet ID */)
|
||||
}
|
||||
|
||||
@@ -99,9 +104,8 @@ func TestService_CanSubscribe(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := &Service{
|
||||
currentForkDigest: currentFork,
|
||||
genesisValidatorsRoot: make([]byte, 32),
|
||||
genesisTime: time.Now(),
|
||||
genesisValidatorsRoot: valRoot[:],
|
||||
genesisTime: genesisTime,
|
||||
}
|
||||
if got := s.CanSubscribe(tt.topic); got != tt.want {
|
||||
t.Errorf("CanSubscribe(%s) = %v, want %v", tt.topic, got, tt.want)
|
||||
@@ -189,7 +193,7 @@ func Test_scanfcheck(t *testing.T) {
|
||||
func TestGossipTopicMapping_scanfcheck_GossipTopicFormattingSanityCheck(t *testing.T) {
|
||||
// scanfcheck only supports integer based substitutions at the moment. Any others will
|
||||
// inaccurately fail validation.
|
||||
for topic := range GossipTopicMappings {
|
||||
for _, topic := range AllTopics() {
|
||||
t.Run(topic, func(t *testing.T) {
|
||||
for i, c := range topic {
|
||||
if string(c) == "%" {
|
||||
@@ -204,8 +208,11 @@ func TestGossipTopicMapping_scanfcheck_GossipTopicFormattingSanityCheck(t *testi
|
||||
}
|
||||
|
||||
func TestService_FilterIncomingSubscriptions(t *testing.T) {
|
||||
currentFork := [4]byte{0x01, 0x02, 0x03, 0x04}
|
||||
validProtocolSuffix := "/" + encoder.ProtocolSuffixSSZSnappy
|
||||
genesisTime := time.Now()
|
||||
valRoot := [32]byte{}
|
||||
digest, err := p2putils.CreateForkDigest(genesisTime, valRoot[:])
|
||||
assert.NoError(t, err)
|
||||
type args struct {
|
||||
id peer.ID
|
||||
subs []*pubsubpb.RPC_SubOpts
|
||||
@@ -241,7 +248,7 @@ func TestService_FilterIncomingSubscriptions(t *testing.T) {
|
||||
return &b
|
||||
}(),
|
||||
Topicid: func() *string {
|
||||
s := fmt.Sprintf(BlockSubnetTopicFormat, currentFork) + validProtocolSuffix
|
||||
s := fmt.Sprintf(BlockSubnetTopicFormat, digest) + validProtocolSuffix
|
||||
return &s
|
||||
}(),
|
||||
},
|
||||
@@ -255,7 +262,7 @@ func TestService_FilterIncomingSubscriptions(t *testing.T) {
|
||||
return &b
|
||||
}(),
|
||||
Topicid: func() *string {
|
||||
s := fmt.Sprintf(BlockSubnetTopicFormat, currentFork) + validProtocolSuffix
|
||||
s := fmt.Sprintf(BlockSubnetTopicFormat, digest) + validProtocolSuffix
|
||||
return &s
|
||||
}(),
|
||||
},
|
||||
@@ -271,7 +278,7 @@ func TestService_FilterIncomingSubscriptions(t *testing.T) {
|
||||
return &b
|
||||
}(),
|
||||
Topicid: func() *string {
|
||||
s := fmt.Sprintf(BlockSubnetTopicFormat, currentFork) + validProtocolSuffix
|
||||
s := fmt.Sprintf(BlockSubnetTopicFormat, digest) + validProtocolSuffix
|
||||
return &s
|
||||
}(),
|
||||
},
|
||||
@@ -281,7 +288,7 @@ func TestService_FilterIncomingSubscriptions(t *testing.T) {
|
||||
return &b
|
||||
}(),
|
||||
Topicid: func() *string {
|
||||
s := fmt.Sprintf(BlockSubnetTopicFormat, currentFork) + validProtocolSuffix
|
||||
s := fmt.Sprintf(BlockSubnetTopicFormat, digest) + validProtocolSuffix
|
||||
return &s
|
||||
}(),
|
||||
},
|
||||
@@ -295,7 +302,7 @@ func TestService_FilterIncomingSubscriptions(t *testing.T) {
|
||||
return &b
|
||||
}(),
|
||||
Topicid: func() *string {
|
||||
s := fmt.Sprintf(BlockSubnetTopicFormat, currentFork) + validProtocolSuffix
|
||||
s := fmt.Sprintf(BlockSubnetTopicFormat, digest) + validProtocolSuffix
|
||||
return &s
|
||||
}(),
|
||||
},
|
||||
@@ -305,9 +312,8 @@ func TestService_FilterIncomingSubscriptions(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := &Service{
|
||||
currentForkDigest: currentFork,
|
||||
genesisValidatorsRoot: make([]byte, 32),
|
||||
genesisTime: time.Now(),
|
||||
genesisValidatorsRoot: valRoot[:],
|
||||
genesisTime: genesisTime,
|
||||
}
|
||||
got, err := s.FilterIncomingSubscriptions(tt.args.id, tt.args.subs)
|
||||
if (err != nil) != tt.wantErr {
|
||||
@@ -350,14 +356,4 @@ func TestService_MonitorsStateForkUpdates(t *testing.T) {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
require.True(t, s.isInitialized())
|
||||
require.NotEmpty(t, s.currentForkDigest)
|
||||
}
|
||||
|
||||
func TestService_doesntSupportForksYet(t *testing.T) {
|
||||
// Part of phase 1 will include a state transition which updates the state's fork. In phase 0,
|
||||
// there are no forks or fork schedule planned. As such, we'll work on supporting fork upgrades
|
||||
// in phase 1 changes.
|
||||
if len(params.BeaconConfig().ForkVersionSchedule) > 0 {
|
||||
t.Fatal("pubsub subscription filters do not support fork schedule (yet)")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,12 @@ import (
|
||||
|
||||
"github.com/golang/snappy"
|
||||
pubsubpb "github.com/libp2p/go-libp2p-pubsub/pb"
|
||||
"github.com/pkg/errors"
|
||||
mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/encoder"
|
||||
testp2p "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
@@ -58,16 +61,112 @@ func TestService_PublishToTopicConcurrentMapWrite(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMessageIDFunction_HashesCorrectly(t *testing.T) {
|
||||
s := &Service{
|
||||
cfg: &Config{
|
||||
TCPPort: 0,
|
||||
UDPPort: 0,
|
||||
},
|
||||
genesisTime: time.Now(),
|
||||
genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32),
|
||||
}
|
||||
d, err := s.currentForkDigest()
|
||||
assert.NoError(t, err)
|
||||
tpc := fmt.Sprintf(BlockSubnetTopicFormat, d)
|
||||
invalidSnappy := [32]byte{'J', 'U', 'N', 'K'}
|
||||
pMsg := &pubsubpb.Message{Data: invalidSnappy[:]}
|
||||
pMsg := &pubsubpb.Message{Data: invalidSnappy[:], Topic: &tpc}
|
||||
hashedData := hashutil.Hash(append(params.BeaconNetworkConfig().MessageDomainInvalidSnappy[:], pMsg.Data...))
|
||||
msgID := string(hashedData[:20])
|
||||
assert.Equal(t, msgID, msgIDFunction(pMsg), "Got incorrect msg id")
|
||||
assert.Equal(t, msgID, s.msgIDFunction(pMsg), "Got incorrect msg id")
|
||||
|
||||
validObj := [32]byte{'v', 'a', 'l', 'i', 'd'}
|
||||
enc := snappy.Encode(nil, validObj[:])
|
||||
nMsg := &pubsubpb.Message{Data: enc}
|
||||
nMsg := &pubsubpb.Message{Data: enc, Topic: &tpc}
|
||||
hashedData = hashutil.Hash(append(params.BeaconNetworkConfig().MessageDomainValidSnappy[:], validObj[:]...))
|
||||
msgID = string(hashedData[:20])
|
||||
assert.Equal(t, msgID, msgIDFunction(nMsg), "Got incorrect msg id")
|
||||
assert.Equal(t, msgID, s.msgIDFunction(nMsg), "Got incorrect msg id")
|
||||
}
|
||||
|
||||
func TestMessageIDFunction_HashesCorrectlyAltair(t *testing.T) {
|
||||
s := &Service{
|
||||
cfg: &Config{
|
||||
TCPPort: 0,
|
||||
UDPPort: 0,
|
||||
},
|
||||
genesisTime: time.Now(),
|
||||
genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32),
|
||||
}
|
||||
d, err := helpers.ComputeForkDigest(params.BeaconConfig().AltairForkVersion, s.genesisValidatorsRoot)
|
||||
assert.NoError(t, err)
|
||||
tpc := fmt.Sprintf(BlockSubnetTopicFormat, d)
|
||||
topicLen := uint64(len(tpc))
|
||||
topicLenBytes := bytesutil.Uint64ToBytesLittleEndian(topicLen)
|
||||
invalidSnappy := [32]byte{'J', 'U', 'N', 'K'}
|
||||
pMsg := &pubsubpb.Message{Data: invalidSnappy[:], Topic: &tpc}
|
||||
// Create object to hash
|
||||
combinedObj := append(params.BeaconNetworkConfig().MessageDomainInvalidSnappy[:], topicLenBytes...)
|
||||
combinedObj = append(combinedObj, tpc...)
|
||||
combinedObj = append(combinedObj, pMsg.Data...)
|
||||
hashedData := hashutil.Hash(combinedObj)
|
||||
msgID := string(hashedData[:20])
|
||||
assert.Equal(t, msgID, s.msgIDFunction(pMsg), "Got incorrect msg id")
|
||||
|
||||
validObj := [32]byte{'v', 'a', 'l', 'i', 'd'}
|
||||
enc := snappy.Encode(nil, validObj[:])
|
||||
nMsg := &pubsubpb.Message{Data: enc, Topic: &tpc}
|
||||
// Create object to hash
|
||||
combinedObj = append(params.BeaconNetworkConfig().MessageDomainValidSnappy[:], topicLenBytes...)
|
||||
combinedObj = append(combinedObj, tpc...)
|
||||
combinedObj = append(combinedObj, validObj[:]...)
|
||||
hashedData = hashutil.Hash(combinedObj)
|
||||
msgID = string(hashedData[:20])
|
||||
assert.Equal(t, msgID, s.msgIDFunction(nMsg), "Got incorrect msg id")
|
||||
}
|
||||
|
||||
func TestExtractGossipDigest(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
topic string
|
||||
want [4]byte
|
||||
wantErr bool
|
||||
error error
|
||||
}{
|
||||
{
|
||||
name: "too short topic",
|
||||
topic: "/eth2/",
|
||||
want: [4]byte{},
|
||||
wantErr: true,
|
||||
error: errors.New("invalid topic format"),
|
||||
},
|
||||
{
|
||||
name: "invalid digest in topic",
|
||||
topic: "/eth2/zzxxyyaa/beacon_block" + "/" + encoder.ProtocolSuffixSSZSnappy,
|
||||
want: [4]byte{},
|
||||
wantErr: true,
|
||||
error: errors.New("encoding/hex: invalid byte"),
|
||||
},
|
||||
{
|
||||
name: "short digest",
|
||||
topic: fmt.Sprintf(BlockSubnetTopicFormat, []byte{0xb5, 0x30, 0x3f}) + "/" + encoder.ProtocolSuffixSSZSnappy,
|
||||
want: [4]byte{},
|
||||
wantErr: true,
|
||||
error: errors.New("invalid digest length wanted"),
|
||||
},
|
||||
{
|
||||
name: "valid topic",
|
||||
topic: fmt.Sprintf(BlockSubnetTopicFormat, []byte{0xb5, 0x30, 0x3f, 0x2a}) + "/" + encoder.ProtocolSuffixSSZSnappy,
|
||||
want: [4]byte{0xb5, 0x30, 0x3f, 0x2a},
|
||||
wantErr: false,
|
||||
error: nil,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ExtractGossipDigest(tt.topic)
|
||||
assert.Equal(t, err != nil, tt.wantErr)
|
||||
if tt.wantErr {
|
||||
assert.ErrorContains(t, tt.error.Error(), err)
|
||||
}
|
||||
assert.DeepEqual(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,56 +7,82 @@ import (
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
p2ptypes "github.com/prysmaticlabs/prysm/beacon-chain/p2p/types"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
// SchemaVersionV1 specifies the schema version for our rpc protocol ID.
|
||||
const SchemaVersionV1 = "/1"
|
||||
|
||||
// SchemaVersionV2 specifies the next schema version for our rpc protocol ID.
|
||||
const SchemaVersionV2 = "/2"
|
||||
|
||||
// Specifies the protocol prefix for all our Req/Resp topics.
|
||||
const protocolPrefix = "/eth2/beacon_chain/req"
|
||||
|
||||
// Specifies the name for the status message topic.
|
||||
const statusMessageName = "/status"
|
||||
// StatusMessageName specifies the name for the status message topic.
|
||||
const StatusMessageName = "/status"
|
||||
|
||||
// Specifies the name for the goodbye message topic.
|
||||
const goodbyeMessageName = "/goodbye"
|
||||
// GoodbyeMessageName specifies the name for the goodbye message topic.
|
||||
const GoodbyeMessageName = "/goodbye"
|
||||
|
||||
// Specifies the name for the beacon blocks by range message topic.
|
||||
const beaconBlocksByRangeMessageName = "/beacon_blocks_by_range"
|
||||
// BeaconBlocksByRangeMessageName specifies the name for the beacon blocks by range message topic.
|
||||
const BeaconBlocksByRangeMessageName = "/beacon_blocks_by_range"
|
||||
|
||||
// Specifies the name for the beacon blocks by root message topic.
|
||||
const beaconBlocksByRootsMessageName = "/beacon_blocks_by_root"
|
||||
// BeaconBlocksByRootsMessageName specifies the name for the beacon blocks by root message topic.
|
||||
const BeaconBlocksByRootsMessageName = "/beacon_blocks_by_root"
|
||||
|
||||
// Specifies the name for the ping message topic.
|
||||
const pingMessageName = "/ping"
|
||||
// PingMessageName Specifies the name for the ping message topic.
|
||||
const PingMessageName = "/ping"
|
||||
|
||||
// Specifies the name for the metadata message topic.
|
||||
const metadataMessageName = "/metadata"
|
||||
// MetadataMessageName specifies the name for the metadata message topic.
|
||||
const MetadataMessageName = "/metadata"
|
||||
|
||||
const (
|
||||
// V1 RPC Topics
|
||||
// RPCStatusTopicV1 defines the v1 topic for the status rpc method.
|
||||
RPCStatusTopicV1 = protocolPrefix + statusMessageName + SchemaVersionV1
|
||||
RPCStatusTopicV1 = protocolPrefix + StatusMessageName + SchemaVersionV1
|
||||
// RPCGoodByeTopicV1 defines the v1 topic for the goodbye rpc method.
|
||||
RPCGoodByeTopicV1 = protocolPrefix + goodbyeMessageName + SchemaVersionV1
|
||||
RPCGoodByeTopicV1 = protocolPrefix + GoodbyeMessageName + SchemaVersionV1
|
||||
// RPCBlocksByRangeTopicV1 defines v1 the topic for the blocks by range rpc method.
|
||||
RPCBlocksByRangeTopicV1 = protocolPrefix + beaconBlocksByRangeMessageName + SchemaVersionV1
|
||||
RPCBlocksByRangeTopicV1 = protocolPrefix + BeaconBlocksByRangeMessageName + SchemaVersionV1
|
||||
// RPCBlocksByRootTopicV1 defines the v1 topic for the blocks by root rpc method.
|
||||
RPCBlocksByRootTopicV1 = protocolPrefix + beaconBlocksByRootsMessageName + SchemaVersionV1
|
||||
RPCBlocksByRootTopicV1 = protocolPrefix + BeaconBlocksByRootsMessageName + SchemaVersionV1
|
||||
// RPCPingTopicV1 defines the v1 topic for the ping rpc method.
|
||||
RPCPingTopicV1 = protocolPrefix + pingMessageName + SchemaVersionV1
|
||||
RPCPingTopicV1 = protocolPrefix + PingMessageName + SchemaVersionV1
|
||||
// RPCMetaDataTopicV1 defines the v1 topic for the metadata rpc method.
|
||||
RPCMetaDataTopicV1 = protocolPrefix + metadataMessageName + SchemaVersionV1
|
||||
RPCMetaDataTopicV1 = protocolPrefix + MetadataMessageName + SchemaVersionV1
|
||||
|
||||
// V2 RPC Topics
|
||||
// RPCBlocksByRangeTopicV2 defines v2 the topic for the blocks by range rpc method.
|
||||
RPCBlocksByRangeTopicV2 = protocolPrefix + BeaconBlocksByRangeMessageName + SchemaVersionV2
|
||||
// RPCBlocksByRootTopicV2 defines the v2 topic for the blocks by root rpc method.
|
||||
RPCBlocksByRootTopicV2 = protocolPrefix + BeaconBlocksByRootsMessageName + SchemaVersionV2
|
||||
// RPCMetaDataTopicV2 defines the v2 topic for the metadata rpc method.
|
||||
RPCMetaDataTopicV2 = protocolPrefix + MetadataMessageName + SchemaVersionV2
|
||||
)
|
||||
|
||||
// RPC errors for topic parsing.
|
||||
const (
|
||||
invalidRPCMessageType = "provided message type doesn't have a registered mapping"
|
||||
)
|
||||
|
||||
// RPCTopicMappings map the base message type to the rpc request.
|
||||
var RPCTopicMappings = map[string]interface{}{
|
||||
RPCStatusTopicV1: new(pb.Status),
|
||||
RPCGoodByeTopicV1: new(types.SSZUint64),
|
||||
// RPC Status Message
|
||||
RPCStatusTopicV1: new(pb.Status),
|
||||
// RPC Goodbye Message
|
||||
RPCGoodByeTopicV1: new(types.SSZUint64),
|
||||
// RPC Block By Range Message
|
||||
RPCBlocksByRangeTopicV1: new(pb.BeaconBlocksByRangeRequest),
|
||||
RPCBlocksByRootTopicV1: new(p2ptypes.BeaconBlockByRootsReq),
|
||||
RPCPingTopicV1: new(types.SSZUint64),
|
||||
RPCMetaDataTopicV1: new(interface{}),
|
||||
RPCBlocksByRangeTopicV2: new(pb.BeaconBlocksByRangeRequest),
|
||||
// RPC Block By Root Message
|
||||
RPCBlocksByRootTopicV1: new(p2ptypes.BeaconBlockByRootsReq),
|
||||
RPCBlocksByRootTopicV2: new(p2ptypes.BeaconBlockByRootsReq),
|
||||
// RPC Ping Message
|
||||
RPCPingTopicV1: new(types.SSZUint64),
|
||||
// RPC Metadata Message
|
||||
RPCMetaDataTopicV1: new(interface{}),
|
||||
RPCMetaDataTopicV2: new(interface{}),
|
||||
}
|
||||
|
||||
// Maps all registered protocol prefixes.
|
||||
@@ -67,16 +93,24 @@ var protocolMapping = map[string]bool{
|
||||
// Maps all the protocol message names for the different rpc
|
||||
// topics.
|
||||
var messageMapping = map[string]bool{
|
||||
statusMessageName: true,
|
||||
goodbyeMessageName: true,
|
||||
beaconBlocksByRangeMessageName: true,
|
||||
beaconBlocksByRootsMessageName: true,
|
||||
pingMessageName: true,
|
||||
metadataMessageName: true,
|
||||
StatusMessageName: true,
|
||||
GoodbyeMessageName: true,
|
||||
BeaconBlocksByRangeMessageName: true,
|
||||
BeaconBlocksByRootsMessageName: true,
|
||||
PingMessageName: true,
|
||||
MetadataMessageName: true,
|
||||
}
|
||||
|
||||
// Maps all the RPC messages which are to updated in altair.
|
||||
var altairMapping = map[string]bool{
|
||||
BeaconBlocksByRangeMessageName: true,
|
||||
BeaconBlocksByRootsMessageName: true,
|
||||
MetadataMessageName: true,
|
||||
}
|
||||
|
||||
var versionMapping = map[string]bool{
|
||||
SchemaVersionV1: true,
|
||||
SchemaVersionV2: true,
|
||||
}
|
||||
|
||||
// VerifyTopicMapping verifies that the topic and its accompanying
|
||||
@@ -187,3 +221,17 @@ func (r RPCTopic) Version() string {
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
||||
// TopicFromMessage constructs the rpc topic from the provided message
|
||||
// type and epoch.
|
||||
func TopicFromMessage(msg string, epoch types.Epoch) (string, error) {
|
||||
if !messageMapping[msg] {
|
||||
return "", errors.Errorf("%s: %s", invalidRPCMessageType, msg)
|
||||
}
|
||||
version := SchemaVersionV1
|
||||
isAltair := epoch >= params.BeaconConfig().AltairForkEpoch
|
||||
if isAltair && altairMapping[msg] {
|
||||
version = SchemaVersionV2
|
||||
}
|
||||
return protocolPrefix + msg + version, nil
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package p2p
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
eth2types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/types"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
@@ -34,9 +39,9 @@ func TestTopicDeconstructor(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "valid status topic",
|
||||
topic: protocolPrefix + statusMessageName + SchemaVersionV1,
|
||||
topic: protocolPrefix + StatusMessageName + SchemaVersionV1,
|
||||
expectedError: "",
|
||||
output: []string{protocolPrefix, statusMessageName, SchemaVersionV1},
|
||||
output: []string{protocolPrefix, StatusMessageName, SchemaVersionV1},
|
||||
},
|
||||
{
|
||||
name: "malformed status topic",
|
||||
@@ -46,13 +51,13 @@ func TestTopicDeconstructor(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "valid beacon block by range topic",
|
||||
topic: protocolPrefix + beaconBlocksByRangeMessageName + SchemaVersionV1 + "/ssz_snappy",
|
||||
topic: protocolPrefix + BeaconBlocksByRangeMessageName + SchemaVersionV1 + "/ssz_snappy",
|
||||
expectedError: "",
|
||||
output: []string{protocolPrefix, beaconBlocksByRangeMessageName, SchemaVersionV1},
|
||||
output: []string{protocolPrefix, BeaconBlocksByRangeMessageName, SchemaVersionV1},
|
||||
},
|
||||
{
|
||||
name: "beacon block by range topic with malformed version",
|
||||
topic: protocolPrefix + beaconBlocksByRangeMessageName + "/v" + "/ssz_snappy",
|
||||
topic: protocolPrefix + BeaconBlocksByRangeMessageName + "/v" + "/ssz_snappy",
|
||||
expectedError: "unable to find a valid schema version for /eth2/beacon_chain/req/beacon_blocks_by_range/v/ssz_snappy",
|
||||
output: []string{""},
|
||||
},
|
||||
@@ -73,3 +78,45 @@ func TestTopicDeconstructor(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTopicFromMessage_CorrectType(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
bCfg := params.BeaconConfig()
|
||||
forkEpoch := eth2types.Epoch(100)
|
||||
bCfg.AltairForkEpoch = forkEpoch
|
||||
bCfg.ForkVersionSchedule[bytesutil.ToBytes4(bCfg.AltairForkVersion)] = eth2types.Epoch(100)
|
||||
params.OverrideBeaconConfig(bCfg)
|
||||
|
||||
// Garbage Message
|
||||
badMsg := "wljdjska"
|
||||
_, err := TopicFromMessage(badMsg, 0)
|
||||
assert.ErrorContains(t, fmt.Sprintf("%s: %s", invalidRPCMessageType, badMsg), err)
|
||||
// Before Fork
|
||||
for m := range messageMapping {
|
||||
topic, err := TopicFromMessage(m, 0)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, true, strings.Contains(topic, SchemaVersionV1))
|
||||
_, _, version, err := TopicDeconstructor(topic)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, SchemaVersionV1, version)
|
||||
}
|
||||
|
||||
// After Fork
|
||||
for m := range messageMapping {
|
||||
topic, err := TopicFromMessage(m, forkEpoch)
|
||||
assert.NoError(t, err)
|
||||
|
||||
if altairMapping[m] {
|
||||
assert.Equal(t, true, strings.Contains(topic, SchemaVersionV2))
|
||||
_, _, version, err := TopicDeconstructor(topic)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, SchemaVersionV2, version)
|
||||
} else {
|
||||
assert.Equal(t, true, strings.Contains(topic, SchemaVersionV1))
|
||||
_, _, version, err := TopicDeconstructor(topic)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, SchemaVersionV1, version)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user