mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 21:38:05 -05:00
Compare commits
92 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d27d18b192 | ||
|
|
0e88085661 | ||
|
|
3f6435ac80 | ||
|
|
64b69d9216 | ||
|
|
13207a9de5 | ||
|
|
ab756ec094 | ||
|
|
499f05f34b | ||
|
|
0077654fb5 | ||
|
|
f8cac0fb41 | ||
|
|
607f086de9 | ||
|
|
3b18aee181 | ||
|
|
f43a7c67f2 | ||
|
|
199ddc6cdb | ||
|
|
023dfebc73 | ||
|
|
53c4a26184 | ||
|
|
5acc362f7e | ||
|
|
68edad13bc | ||
|
|
bb2f329562 | ||
|
|
5169209360 | ||
|
|
c4ca8a47b3 | ||
|
|
904898e405 | ||
|
|
7f96fcc51b | ||
|
|
24583864b4 | ||
|
|
db9153e8e4 | ||
|
|
cd6e3e8a09 | ||
|
|
fc7c530696 | ||
|
|
8f05f14b36 | ||
|
|
3b8701296b | ||
|
|
48f69c0762 | ||
|
|
75521fffbd | ||
|
|
8ba6c84d6b | ||
|
|
6ae829a555 | ||
|
|
89f4053c33 | ||
|
|
3332abbb5a | ||
|
|
76e9111833 | ||
|
|
81c53c26fb | ||
|
|
62aaec1e20 | ||
|
|
cc18b2f4d3 | ||
|
|
a938c305b4 | ||
|
|
b87d0abc6c | ||
|
|
768c2bd812 | ||
|
|
c8b8c6165d | ||
|
|
db866e6580 | ||
|
|
b50f1583f3 | ||
|
|
0be4e6fed8 | ||
|
|
e9bd530221 | ||
|
|
67cf86ad5e | ||
|
|
5a789fca4a | ||
|
|
295b3a74e9 | ||
|
|
d35c5db260 | ||
|
|
9c1e3c260a | ||
|
|
371f808aa4 | ||
|
|
8df65c1bcc | ||
|
|
6b6273fec1 | ||
|
|
6e90931837 | ||
|
|
bf49fa3c26 | ||
|
|
d4e7e15e50 | ||
|
|
7d1633230d | ||
|
|
db26c0d012 | ||
|
|
2b0acffe7f | ||
|
|
485fc538c3 | ||
|
|
be1b90d511 | ||
|
|
8977e5088e | ||
|
|
cc16a10a33 | ||
|
|
103bdfc688 | ||
|
|
ab92326dfb | ||
|
|
da6c270d46 | ||
|
|
86cd873e67 | ||
|
|
49e0ddf861 | ||
|
|
9ca95530fa | ||
|
|
a29032c2bf | ||
|
|
82de66bb90 | ||
|
|
b2b48c2a4d | ||
|
|
5f79abd828 | ||
|
|
1b1e994a80 | ||
|
|
fbc31dc99b | ||
|
|
2785a6d5ee | ||
|
|
2b444ea954 | ||
|
|
ae89cce593 | ||
|
|
749f4b776b | ||
|
|
0481eb4872 | ||
|
|
f1627c0b67 | ||
|
|
3d1b69e945 | ||
|
|
f8c870aa91 | ||
|
|
094f1974be | ||
|
|
c4d47faae5 | ||
|
|
582c382771 | ||
|
|
c5b0b3c326 | ||
|
|
57036d16f9 | ||
|
|
1c4b4c8393 | ||
|
|
0a3825e79e | ||
|
|
acf11262de |
@@ -14,8 +14,7 @@ build:remote-cache --strategy=Genrule=standalone
|
||||
build:remote-cache --bes_results_url="https://source.cloud.google.com/results/invocations/"
|
||||
build:remote-cache --bes_backend=buildeventservice.googleapis.com
|
||||
build:remote-cache --bes_timeout=60s
|
||||
build:remote-cache --project_id=prysmaticlabs
|
||||
build:remote-cache --bes_upload_mode=fully_async
|
||||
build:remote-cache --project_id=prysmaticlabs
|
||||
|
||||
# Prysm specific remote-cache properties.
|
||||
build:remote-cache --disk_cache=
|
||||
|
||||
126
README.md
126
README.md
@@ -1,28 +1,33 @@
|
||||
# Prysm: Ethereum 'Serenity' 2.0 Go Implementation
|
||||
# Prysm: An Ethereum 2.0 Client Written in Go
|
||||
|
||||
[](https://buildkite.com/prysmatic-labs/prysm)
|
||||
[](https://github.com/ethereum/eth2.0-specs/commit/452ecf8e27c7852c7854597f2b1bb4a62b80c7ec)
|
||||
[](https://discord.gg/KSA7rPr)
|
||||
[](https://gitter.im/prysmaticlabs/geth-sharding?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
|
||||
This is the Core repository for Prysm, [Prysmatic Labs](https://prysmaticlabs.com)' [Go](https://golang.org/) implementation of the Ethereum protocol 2.0 (Serenity).
|
||||
This is the core repository for Prysm, a [Golang](https://golang.org/) implementation of the Ethereum 2.0 client specifications developed by [Prysmatic Labs](https://prysmaticlabs.com).
|
||||
|
||||
### Need assistance?
|
||||
A more detailed set of installation and usage instructions as well as explanations of each component are available on our [official documentation portal](https://prysmaticlabs.gitbook.io/prysm/). If you still have questions, feel free to stop by either our [Discord](https://discord.gg/KSA7rPr) or [Gitter](https://gitter.im/prysmaticlabs/geth-sharding?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) and a member of the team or our community will be happy to assist you.
|
||||
|
||||
**Interested in what's next?** Be sure to read our [Roadmap Reference Implementation](https://github.com/prysmaticlabs/prysm/blob/master/docs/ROADMAP.md) document. This page outlines the basics of sharding as well as the various short-term milestones that we hope to achieve over the coming year.
|
||||
A more detailed set of installation and usage instructions as well as breakdowns of each individual component are available in the [official documentation portal](https://prysmaticlabs.gitbook.io/prysm/). If you still have questions, feel free to stop by either our [Discord](https://discord.gg/KSA7rPr) or [Gitter](https://gitter.im/prysmaticlabs/geth-sharding?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) and a member of the team or our community will be happy to assist you.
|
||||
|
||||
### Come join the testnet!
|
||||
Participation is now open to the public in our testnet release for Ethereum 2.0 phase 0. Visit [prylabs.net](https://prylabs.net) for more information on the project itself or to sign up as a validator on the network.
|
||||
Participation is now open to the public for our Ethereum 2.0 phase 0 testnet release. Visit [prylabs.net](https://prylabs.net) for more information on the project or to sign up as a validator on the network.
|
||||
|
||||
# Table of Contents
|
||||
|
||||
- [Dependencies](#dependencies)
|
||||
- [Installation](#installation)
|
||||
- [Build Via Docker](#build-via-docker)
|
||||
- [Build Via Bazel](#build-via-bazel)
|
||||
- [Running an Ethereum 2.0 Beacon Node](#running-an-ethereum-20-beacon-node)
|
||||
- [Staking ETH: Running a Validator Client](#staking-eth-running-a-validator-client)
|
||||
- [Build via Docker](#build-via-docker)
|
||||
- [Build via Bazel](#build-via-bazel)
|
||||
- [Connecting to the public testnet: running a beacon node](#connecting-to-the-testnet-running-a-beacon-node)
|
||||
- [Running via Docker](#build-via-docker)
|
||||
- [Running via Bazel](#build-via-bazel)
|
||||
- [Staking ETH: running a validator client](#staking-eth-running-a-validator-client)
|
||||
- [Activating your validator: depositing 3.2 Goerli ETH](#activating-your-validator-depositing-32-goerli-eth)
|
||||
- [Starting the validator with Bazel](#starting-the-validator-with-bazel)
|
||||
- [Setting up a local ETH2 development chain](#setting-up-a-local-eth2-development-chain)
|
||||
- [Installation and dependencies](#installation-and-dependencies)
|
||||
- [Running a local beacon node and validator client](#running-a-local-beacon-node-and-validator-client)
|
||||
- [Testing Prysm](#testing-prysm)
|
||||
- [Contributing](#contributing)
|
||||
- [License](#license)
|
||||
@@ -69,14 +74,14 @@ bazel build //validator:validator
|
||||
```
|
||||
Bazel will automatically pull and install any dependencies as well, including Go and necessary compilers.
|
||||
|
||||
Note that to build with the appropriate configuration for the Prysm testnet you should run:
|
||||
4. Build the configuration for the Prysm testnet by issuing the commands:
|
||||
|
||||
```
|
||||
bazel build --define ssz=minimal //beacon-chain:beacon-chain
|
||||
bazel build --define ssz=minimal //validator:validator
|
||||
```
|
||||
|
||||
The binaries will be created in an architecture-dependent subdirectory of `bazel-bin` and this information is supplied as part of bazel's build process. For example:
|
||||
The binaries will be built in an architecture-dependent subdirectory of `bazel-bin`, and are supplied as part of Bazel's build process. To fetch the location, issue the command:
|
||||
|
||||
```
|
||||
$ bazel build --define ssz=minimal //beacon-chain:beacon-chain
|
||||
@@ -86,53 +91,65 @@ Target //beacon-chain:beacon-chain up-to-date:
|
||||
...
|
||||
```
|
||||
|
||||
Here it can be seen the beacon chain binary has been created at `bazel-bin/beacon-chain/linux_amd64_stripped/beacon-chain`
|
||||
In the example above, the beacon chain binary has been created in `bazel-bin/beacon-chain/linux_amd64_stripped/beacon-chain`.
|
||||
|
||||
## Running an Ethereum 2.0 Beacon Node
|
||||
To understand the role that both the beacon node and validator play in Prysm, see [this section of our documentation](https://prysmaticlabs.gitbook.io/prysm/how-prysm-works/overview-technical).
|
||||
## Connecting to the testnet: running a beacon node
|
||||
|
||||
This section contains instructions for initialising a beacon node and connecting to the public testnet. To further understand the role that both the beacon node and validator play in Prysm, see [this section of our documentation](https://prysmaticlabs.gitbook.io/prysm/how-prysm-works/overview-technical).
|
||||
|
||||
### Running via Docker
|
||||
|
||||
**Docker on Linux/Mac:**
|
||||
#### Docker on Linux/Mac
|
||||
|
||||
To start your beacon node, issue the following command:
|
||||
|
||||
```
|
||||
docker run -v $HOME/prysm-data:/data -p 4000:4000 \
|
||||
gcr.io/prysmaticlabs/prysm/beacon-chain:latest \
|
||||
--name beacon-node \
|
||||
gcr.io/prysmaticlabs/prysm/beacon-chain:latest \
|
||||
--no-genesis-delay \
|
||||
--datadir=/data
|
||||
```
|
||||
|
||||
You can stop the beacon node using `Ctrl+c` or with the following command:
|
||||
(Optional) If you want to enable gRPC, then run this command instead of the one above:
|
||||
|
||||
```
|
||||
docker run -v $HOME/prysm-data:/data -p 4000:4000 -p 7000:7000 \
|
||||
--name beacon-node \
|
||||
gcr.io/prysmaticlabs/prysm/beacon-chain:latest \
|
||||
--datadir=/data \
|
||||
--no-genesis-delay \
|
||||
--grpc-gateway-port=7000
|
||||
```
|
||||
|
||||
You can halt the beacon node using `Ctrl+c` or with the following command:
|
||||
|
||||
```
|
||||
docker stop beacon-node
|
||||
```
|
||||
|
||||
Then it can be restarted again with
|
||||
To restart the beacon node, issue the command:
|
||||
|
||||
```
|
||||
docker start -ai beacon-node
|
||||
```
|
||||
|
||||
If you run into issues you can always delete the container like this:
|
||||
To delete a corrupted container, issue the command:
|
||||
|
||||
```
|
||||
docker rm beacon-node
|
||||
```
|
||||
|
||||
and re-create it again and even reset the chain database adding the parameter `--clear-db` as specified here:
|
||||
To recreate a deleted container and refresh the chain database, issue the start command with an additional `--force-clear-db` parameter:
|
||||
|
||||
```
|
||||
docker run -it -v $HOME/prysm-data:/data -p 4000:4000 \
|
||||
docker run -it -v $HOME/prysm-data:/data -p 4000:4000 --name beacon-node \
|
||||
gcr.io/prysmaticlabs/prysm/beacon-chain:latest \
|
||||
--name beacon-node \
|
||||
--datadir=/data \
|
||||
--clear-db
|
||||
--force-clear-db
|
||||
```
|
||||
|
||||
**Docker on Windows:**
|
||||
#### Docker on Windows
|
||||
|
||||
1) You will need to share the local drive you wish to mount to to container (e.g. C:).
|
||||
1. Enter Docker settings (right click the tray icon)
|
||||
@@ -142,30 +159,31 @@ docker run -it -v $HOME/prysm-data:/data -p 4000:4000 \
|
||||
|
||||
2) You will next need to create a directory named ```/tmp/prysm-data/``` within your selected shared Drive. This folder will be used as a local data directory for Beacon Node chain data as well as account and keystore information required by the validator. Docker will **not** create this directory if it does not exist already. For the purposes of these instructions, it is assumed that ```C:``` is your prior-selected shared Drive.
|
||||
|
||||
4) To run the beacon node, issue the following command:
|
||||
4) To run the beacon node, issue the command:
|
||||
```
|
||||
docker run -it -v c:/tmp/prysm-data:/data -p 4000:4000 gcr.io/prysmaticlabs/prysm/beacon-chain:latest --datadir=/data --clear-db
|
||||
docker run -it -v c:/tmp/prysm-data:/data -p 4000:4000 gcr.io/prysmaticlabs/prysm/beacon-chain:latest --datadir=/data
|
||||
```
|
||||
|
||||
### Running via Bazel
|
||||
|
||||
1) To start your Beacon Node with Bazel, issue the following command:
|
||||
1) To start your Beacon Node with Bazel, issue the command:
|
||||
```
|
||||
bazel run //beacon-chain -- --clear-db --datadir=/tmp/prysm-data
|
||||
bazel run //beacon-chain -- --datadir=/tmp/prysm-data
|
||||
```
|
||||
|
||||
This will sync up the Beacon Node with the latest head block in the network.
|
||||
This will sync up the Beacon Node with the latest head block in the network. Note that the beacon node must be **completely synced** before attempting to initialise a validator client, otherwise the validator will not be able to complete the deposit and funds will be lost.
|
||||
|
||||
|
||||
## Staking ETH: Running a Validator Client
|
||||
## Staking ETH: running a validator client
|
||||
|
||||
Once your beacon node is up and **completely synced** (otherwise you will lose validator funds since the validator will not be able to operate), the chain will be waiting for you to deposit 3.2 Goerli ETH into the Validator Deposit Contract to activate your validator (discussed in the section below). First though, you will need to create a *validator client* to connect to this node in order to stake and participate. Each validator represents 3.2 Goerli ETH being staked in the system, and it is possible to spin up as many as you desire in order to have more stake in the network.
|
||||
Once your beacon node is up, the chain will be waiting for you to deposit 3.2 Goerli ETH into the Validator Deposit Contract to activate your validator (discussed in the section below). First though, you will need to create a validator client to connect to this node in order to stake and participate. Each validator represents 3.2 Goerli ETH being staked in the system, and it is possible to spin up as many as you desire in order to have more stake in the network.
|
||||
|
||||
### Activating Your Validator: Depositing 3.2 Goerli ETH
|
||||
For more information on the functionality of validator clients, see [this section](https://prysmaticlabs.gitbook.io/prysm/how-prysm-works/validator-clients) of our official documentation.
|
||||
|
||||
### Activating your validator: depositing 3.2 Goerli ETH
|
||||
|
||||
Using your validator deposit data from the previous step, follow the instructions found on https://prylabs.net/participate to make a deposit.
|
||||
|
||||
It will take a while for the nodes in the network to process your deposit, but once your node is active, the validator will begin doing its responsibility. In your validator client, you will be able to frequently see your validator balance as it goes up over time. Note that, should your node ever go offline for a long period, you'll start gradually losing your deposit until you are removed from the system.
|
||||
It will take a while for the nodes in the network to process your deposit, but once your node is active, the validator will begin doing its responsibility. In your validator client, you will be able to frequently see your validator balance as it goes up over time. Note that, should your node ever go offline for a long period, you'll start gradually losing your deposit until you are removed from the system.
|
||||
|
||||
### Starting the validator with Bazel
|
||||
|
||||
@@ -176,20 +194,52 @@ bazel run //validator
|
||||
```
|
||||
**Congratulations, you are now running Ethereum 2.0 Phase 0!**
|
||||
|
||||
## Setting up a local ETH2 development chain
|
||||
|
||||
This section outlines the process of setting up Prysm for local interop testing with other Ethereum 2.0 client implementations. See the [INTEROP.md](https://github.com/prysmaticlabs/prysm/blob/master/INTEROP.md) file for advanced configuration options. For more background information on interoperability development, see [this blog post](https://blog.ethereum.org/2019/09/19/eth2-interop-in-review/).
|
||||
|
||||
### Installation and dependencies
|
||||
|
||||
To begin setting up a local ETH2 development chain, follow the **Bazel** instructions found in the [dependencies](#dependencies) and [installation](#installation) sections respectively.
|
||||
|
||||
### Running a local beacon node and validator client
|
||||
|
||||
The example below will deterministically generate a beacon genesis state, initiate Prysm with 64 validators and set the genesis time to your local machines current UNIX time.
|
||||
|
||||
1. Open up two terminal windows. In the first, issue the command:
|
||||
|
||||
```
|
||||
bazel run //beacon-chain -- \
|
||||
--no-genesis-delay \
|
||||
--bootstrap-node= \
|
||||
--deposit-contract 0xD775140349E6A5D12524C6ccc3d6A1d4519D4029 \
|
||||
--clear-db \
|
||||
--interop-num-validators 64 \
|
||||
--interop-eth1data-votes
|
||||
```
|
||||
|
||||
2. Wait a moment for the beacon chain to start. In the other terminal, issue the command:
|
||||
|
||||
```
|
||||
bazel run //validator -- --interop-num-validators 64
|
||||
```
|
||||
|
||||
This command will kickstart the system with your 64 validators performing their duties accordingly.
|
||||
|
||||
## Testing Prysm
|
||||
|
||||
**To run the unit tests of our system**, issue the command:
|
||||
To run the unit tests of our system, issue the command:
|
||||
```
|
||||
bazel test //...
|
||||
```
|
||||
|
||||
**To run our linter**, make sure you have [golangci-lint](https://github.com/golangci/golangci-lint) installed and then issue the command:
|
||||
To run the linter, ensure you have [golangci-lint](https://github.com/golangci/golangci-lint) installed, then issue the command:
|
||||
```
|
||||
golangci-lint run
|
||||
```
|
||||
|
||||
## Contributing
|
||||
We have put all of our contribution guidelines into [CONTRIBUTING.md](https://github.com/prysmaticlabs/prysm/blob/master/CONTRIBUTING.md)! Check it out to get started.
|
||||
Want to get involved? Check out our [Contribution Guide](https://prysmaticlabs.gitbook.io/prysm/getting-involved/contribution-guidelines) to learn more!
|
||||
|
||||
## License
|
||||
[GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html)
|
||||
|
||||
134
WORKSPACE
134
WORKSPACE
@@ -8,15 +8,6 @@ http_archive(
|
||||
url = "https://github.com/bazelbuild/bazel-skylib/archive/0.8.0.tar.gz",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "io_bazel_rules_go",
|
||||
sha256 = "513c12397db1bc9aa46dd62f02dd94b49a9b5d17444d49b5a04c5a89f3053c1c",
|
||||
urls = [
|
||||
"https://storage.googleapis.com/bazel-mirror/github.com/bazelbuild/rules_go/releases/download/v0.19.5/rules_go-v0.19.5.tar.gz",
|
||||
"https://github.com/bazelbuild/rules_go/releases/download/v0.19.5/rules_go-v0.19.5.tar.gz",
|
||||
],
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "bazel_gazelle",
|
||||
sha256 = "7fc87f4170011201b1690326e8c16c5d802836e3a0d617d8f75c3af2b23180c4",
|
||||
@@ -35,9 +26,16 @@ http_archive(
|
||||
|
||||
http_archive(
|
||||
name = "io_bazel_rules_docker",
|
||||
sha256 = "9ff889216e28c918811b77999257d4ac001c26c1f7c7fb17a79bc28abf74182e",
|
||||
strip_prefix = "rules_docker-0.10.1",
|
||||
url = "https://github.com/bazelbuild/rules_docker/archive/v0.10.1.tar.gz",
|
||||
# sha256 = "9ff889216e28c918811b77999257d4ac001c26c1f7c7fb17a79bc28abf74182e",
|
||||
strip_prefix = "rules_docker-0.12.1",
|
||||
url = "https://github.com/bazelbuild/rules_docker/archive/v0.12.1.tar.gz",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "io_bazel_rules_go",
|
||||
sha256 = "886db2f8d620fcb5791c8e2a402a575bc70728e17ec116841d78f3837a09f69e",
|
||||
strip_prefix = "rules_go-9bb1562710f7077cd109b66cd4b45900e6d7ae73",
|
||||
urls = ["https://github.com/bazelbuild/rules_go/archive/9bb1562710f7077cd109b66cd4b45900e6d7ae73.tar.gz"],
|
||||
)
|
||||
|
||||
http_archive(
|
||||
@@ -128,9 +126,8 @@ filegroup(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
sha256 = "386335fc3b055fad37088bd821929ff684bc00bb1a74e044e4b377ebd6e88fce",
|
||||
# File names are normally too long, see: https://github.com/ethereum/eth2.0-spec-tests/issues/15
|
||||
url = "https://prysmaticlabs.com/uploads/v0.8.3_general_spectests.tar.gz",
|
||||
sha256 = "5c5b65a961b5e7251435efc9548648b45142a07993ad3e100850c240cb76e9af",
|
||||
url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v0.9.0/general.tar.gz",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
@@ -145,8 +142,8 @@ filegroup(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
sha256 = "7ab89a364796e3f8a9af84750c241e9c9e2170a34c1a4e160fdfa2cee5b03fb7",
|
||||
url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v0.8.3/minimal.tar.gz",
|
||||
sha256 = "3b5f0168af4331d09da52bebc26609def9d11be3e6c784ce7c3df3596617808d",
|
||||
url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v0.9.0/minimal.tar.gz",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
@@ -161,8 +158,8 @@ filegroup(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
sha256 = "6274e3b77f393faf7b17cef10e93244c16316d3b7ae9c6b844501b12f432a7c3",
|
||||
url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v0.8.3/mainnet.tar.gz",
|
||||
sha256 = "f3ff68508dfe9696f23506daf0ca895cda955e30398741e00cffa33a01b0565c",
|
||||
url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v0.9.0/mainnet.tar.gz",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
@@ -184,7 +181,7 @@ go_repository(
|
||||
|
||||
git_repository(
|
||||
name = "com_google_protobuf",
|
||||
commit = "09745575a923640154bcf307fba8aedff47f240a",
|
||||
commit = "d09d649aea36f02c03f8396ba39a8d4db8a607e4",
|
||||
remote = "https://github.com/protocolbuffers/protobuf",
|
||||
shallow_since = "1558721209 -0700",
|
||||
)
|
||||
@@ -210,7 +207,7 @@ go_repository(
|
||||
|
||||
go_repository(
|
||||
name = "com_github_prysmaticlabs_go_ssz",
|
||||
commit = "7e767fb53d02ea220428a6cc0850ee6e17d71bb1",
|
||||
commit = "58b2f86b0f02f06e634db06dee0c838ad41849f8",
|
||||
importpath = "github.com/prysmaticlabs/go-ssz",
|
||||
)
|
||||
|
||||
@@ -1014,7 +1011,7 @@ go_repository(
|
||||
|
||||
go_repository(
|
||||
name = "grpc_ecosystem_grpc_gateway",
|
||||
commit = "e652ba06e9067ef41c199af59b9c6c67724850d4",
|
||||
commit = "da7a886035e25b2f274f89b6f3c64bf70a9f6780",
|
||||
importpath = "github.com/grpc-ecosystem/grpc-gateway",
|
||||
)
|
||||
|
||||
@@ -1202,7 +1199,7 @@ go_repository(
|
||||
|
||||
go_repository(
|
||||
name = "com_github_prysmaticlabs_ethereumapis",
|
||||
commit = "b4ca5785e074dd8fed39f18a61aae0318b57b4b0",
|
||||
commit = "4220be00b4eeb50d10d9d4535accb5568bd94f3a",
|
||||
importpath = "github.com/prysmaticlabs/ethereumapis",
|
||||
)
|
||||
|
||||
@@ -1259,3 +1256,94 @@ go_repository(
|
||||
sum = "h1:n9HxLrNxWWtEb1cA950nuEEj3QnKbtsCJ6KjcgisNUs=",
|
||||
version = "v0.0.0-20191002040644-a1355ae1e2c3",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_naoina_toml",
|
||||
importpath = "github.com/naoina/toml",
|
||||
sum = "h1:PT/lllxVVN0gzzSqSlHEmP8MJB4MY2U7STGxiouV4X8=",
|
||||
version = "v0.1.1",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_elastic_gosigar",
|
||||
importpath = "github.com/elastic/gosigar",
|
||||
sum = "h1:GzPQ+78RaAb4J63unidA/JavQRKrB6s8IOzN6Ib59jo=",
|
||||
version = "v0.10.5",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "in_gopkg_urfave_cli_v1",
|
||||
importpath = "gopkg.in/urfave/cli.v1",
|
||||
sum = "h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=",
|
||||
version = "v1.20.0",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_naoina_go_stringutil",
|
||||
importpath = "github.com/naoina/go-stringutil",
|
||||
sum = "h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks=",
|
||||
version = "v0.1.0",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_influxdata_influxdb",
|
||||
importpath = "github.com/influxdata/influxdb",
|
||||
sum = "h1:uSeBTNO4rBkbp1Be5FKRsAmglM9nlx25TzVQRQt1An4=",
|
||||
version = "v1.7.9",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_robertkrimen_otto",
|
||||
importpath = "github.com/robertkrimen/otto",
|
||||
sum = "h1:1VUlQbCfkoSGv7qP7Y+ro3ap1P1pPZxgdGVqiTVy5C4=",
|
||||
version = "v0.0.0-20180617131154-15f95af6e78d",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_peterh_liner",
|
||||
importpath = "github.com/peterh/liner",
|
||||
sum = "h1:f+aAedNJA6uk7+6rXsYBnhdo4Xux7ESLe+kcuVUF5os=",
|
||||
version = "v1.1.0",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_graph_gophers_graphql_go",
|
||||
importpath = "github.com/graph-gophers/graphql-go",
|
||||
sum = "h1:HwRCZlPXN00r58jaIPE11HXn7EvhheQrE+Cxw0vkrH0=",
|
||||
version = "v0.0.0-20191031232829-adde0d0f76a3",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_rjeczalik_notify",
|
||||
importpath = "github.com/rjeczalik/notify",
|
||||
sum = "h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8=",
|
||||
version = "v0.9.2",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_mohae_deepcopy",
|
||||
importpath = "github.com/mohae/deepcopy",
|
||||
sum = "h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=",
|
||||
version = "v0.0.0-20170929034955-c48cc78d4826",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "in_gopkg_olebedev_go_duktape_v3",
|
||||
importpath = "gopkg.in/olebedev/go-duktape.v3",
|
||||
sum = "h1:uuol9OUzSvZntY1v963NAbVd7A+PHLMz1FlCe3Lorcs=",
|
||||
version = "v3.0.0-20190709231704-1e4459ed25ff",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "in_gopkg_sourcemap_v1",
|
||||
importpath = "gopkg.in/sourcemap.v1",
|
||||
sum = "h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=",
|
||||
version = "v1.0.5",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_fatih_color",
|
||||
importpath = "github.com/fatih/color",
|
||||
sum = "h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=",
|
||||
version = "v1.7.0",
|
||||
)
|
||||
|
||||
@@ -79,7 +79,10 @@ docker_push(
|
||||
go_binary(
|
||||
name = "beacon-chain",
|
||||
embed = [":go_default_library"],
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//endtoend:__pkg__",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
|
||||
@@ -13,6 +13,7 @@ go_library(
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
],
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -68,27 +69,18 @@ func (s *Service) Status() error {
|
||||
// We archive committee information pertaining to the head state's epoch.
|
||||
func (s *Service) archiveCommitteeInfo(ctx context.Context, headState *pb.BeaconState) error {
|
||||
currentEpoch := helpers.SlotToEpoch(headState.Slot)
|
||||
committeeCount, err := helpers.CommitteeCount(headState, currentEpoch)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get committee count")
|
||||
}
|
||||
seed, err := helpers.Seed(headState, currentEpoch)
|
||||
proposerSeed, err := helpers.Seed(headState, currentEpoch, params.BeaconConfig().DomainBeaconProposer)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not generate seed")
|
||||
}
|
||||
startShard, err := helpers.StartShard(headState, currentEpoch)
|
||||
attesterSeed, err := helpers.Seed(headState, currentEpoch, params.BeaconConfig().DomainBeaconAttester)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get start shard")
|
||||
}
|
||||
proposerIndex, err := helpers.BeaconProposerIndex(headState)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get beacon proposer index")
|
||||
return errors.Wrap(err, "could not generate seed")
|
||||
}
|
||||
|
||||
info := ðpb.ArchivedCommitteeInfo{
|
||||
Seed: seed[:],
|
||||
StartShard: startShard,
|
||||
CommitteeCount: committeeCount,
|
||||
ProposerIndex: proposerIndex,
|
||||
ProposerSeed: proposerSeed[:],
|
||||
AttesterSeed: attesterSeed[:],
|
||||
}
|
||||
if err := s.beaconDB.SaveArchivedCommitteeInfo(ctx, currentEpoch, info); err != nil {
|
||||
return errors.Wrap(err, "could not archive committee info")
|
||||
@@ -96,11 +88,15 @@ func (s *Service) archiveCommitteeInfo(ctx context.Context, headState *pb.Beacon
|
||||
return nil
|
||||
}
|
||||
|
||||
// We archive active validator set changes that happened during the epoch.
|
||||
// We archive active validator set changes that happened during the previous epoch.
|
||||
func (s *Service) archiveActiveSetChanges(ctx context.Context, headState *pb.BeaconState) error {
|
||||
activations := validators.ActivatedValidatorIndices(headState)
|
||||
slashings := validators.SlashedValidatorIndices(headState)
|
||||
exited, err := validators.ExitedValidatorIndices(headState)
|
||||
activations := validators.ActivatedValidatorIndices(helpers.PrevEpoch(headState), headState.Validators)
|
||||
slashings := validators.SlashedValidatorIndices(helpers.PrevEpoch(headState), headState.Validators)
|
||||
activeValidatorCount, err := helpers.ActiveValidatorCount(headState, helpers.PrevEpoch(headState))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get active validator count")
|
||||
}
|
||||
exited, err := validators.ExitedValidatorIndices(headState.Validators, activeValidatorCount)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not determine exited validator indices")
|
||||
}
|
||||
@@ -109,7 +105,7 @@ func (s *Service) archiveActiveSetChanges(ctx context.Context, headState *pb.Bea
|
||||
Exited: exited,
|
||||
Slashed: slashings,
|
||||
}
|
||||
if err := s.beaconDB.SaveArchivedActiveValidatorChanges(ctx, helpers.CurrentEpoch(headState), activeSetChanges); err != nil {
|
||||
if err := s.beaconDB.SaveArchivedActiveValidatorChanges(ctx, helpers.PrevEpoch(headState), activeSetChanges); err != nil {
|
||||
return errors.Wrap(err, "could not archive active validator set changes")
|
||||
}
|
||||
return nil
|
||||
@@ -118,7 +114,7 @@ func (s *Service) archiveActiveSetChanges(ctx context.Context, headState *pb.Bea
|
||||
// We compute participation metrics by first retrieving the head state and
|
||||
// matching validator attestations during the epoch.
|
||||
func (s *Service) archiveParticipation(ctx context.Context, headState *pb.BeaconState) error {
|
||||
participation, err := epoch.ComputeValidatorParticipation(headState)
|
||||
participation, err := epoch.ComputeValidatorParticipation(headState, helpers.SlotToEpoch(headState.Slot))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not compute participation")
|
||||
}
|
||||
|
||||
@@ -128,27 +128,17 @@ func TestArchiverService_SavesCommitteeInfo(t *testing.T) {
|
||||
triggerNewHeadEvent(t, svc, [32]byte{})
|
||||
|
||||
currentEpoch := helpers.CurrentEpoch(headState)
|
||||
startShard, err := helpers.StartShard(headState, currentEpoch)
|
||||
proposerSeed, err := helpers.Seed(headState, currentEpoch, params.BeaconConfig().DomainBeaconProposer)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
committeeCount, err := helpers.CommitteeCount(headState, currentEpoch)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
seed, err := helpers.Seed(headState, currentEpoch)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
propIdx, err := helpers.BeaconProposerIndex(headState)
|
||||
attesterSeed, err := helpers.Seed(headState, currentEpoch, params.BeaconConfig().DomainBeaconAttester)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wanted := ðpb.ArchivedCommitteeInfo{
|
||||
Seed: seed[:],
|
||||
StartShard: startShard,
|
||||
CommitteeCount: committeeCount,
|
||||
ProposerIndex: propIdx,
|
||||
ProposerSeed: proposerSeed[:],
|
||||
AttesterSeed: attesterSeed[:],
|
||||
}
|
||||
|
||||
retrieved, err := svc.beaconDB.ArchivedCommitteeInfo(svc.ctx, helpers.CurrentEpoch(headState))
|
||||
@@ -175,13 +165,13 @@ func TestArchiverService_SavesActivatedValidatorChanges(t *testing.T) {
|
||||
svc.headFetcher = &mock.ChainService{
|
||||
State: headState,
|
||||
}
|
||||
currentEpoch := helpers.CurrentEpoch(headState)
|
||||
delayedActEpoch := helpers.DelayedActivationExitEpoch(currentEpoch)
|
||||
prevEpoch := helpers.PrevEpoch(headState)
|
||||
delayedActEpoch := helpers.DelayedActivationExitEpoch(prevEpoch)
|
||||
headState.Validators[4].ActivationEpoch = delayedActEpoch
|
||||
headState.Validators[5].ActivationEpoch = delayedActEpoch
|
||||
triggerNewHeadEvent(t, svc, [32]byte{})
|
||||
|
||||
retrieved, err := beaconDB.ArchivedActiveValidatorChanges(svc.ctx, currentEpoch)
|
||||
retrieved, err := beaconDB.ArchivedActiveValidatorChanges(svc.ctx, prevEpoch)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -200,12 +190,12 @@ func TestArchiverService_SavesSlashedValidatorChanges(t *testing.T) {
|
||||
svc.headFetcher = &mock.ChainService{
|
||||
State: headState,
|
||||
}
|
||||
currentEpoch := helpers.CurrentEpoch(headState)
|
||||
prevEpoch := helpers.PrevEpoch(headState)
|
||||
headState.Validators[95].Slashed = true
|
||||
headState.Validators[96].Slashed = true
|
||||
triggerNewHeadEvent(t, svc, [32]byte{})
|
||||
|
||||
retrieved, err := beaconDB.ArchivedActiveValidatorChanges(svc.ctx, currentEpoch)
|
||||
retrieved, err := beaconDB.ArchivedActiveValidatorChanges(svc.ctx, prevEpoch)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -224,12 +214,12 @@ func TestArchiverService_SavesExitedValidatorChanges(t *testing.T) {
|
||||
svc.headFetcher = &mock.ChainService{
|
||||
State: headState,
|
||||
}
|
||||
currentEpoch := helpers.CurrentEpoch(headState)
|
||||
headState.Validators[95].ExitEpoch = currentEpoch + 1
|
||||
headState.Validators[95].WithdrawableEpoch = currentEpoch + 1 + params.BeaconConfig().MinValidatorWithdrawabilityDelay
|
||||
prevEpoch := helpers.PrevEpoch(headState)
|
||||
headState.Validators[95].ExitEpoch = prevEpoch + 1
|
||||
headState.Validators[95].WithdrawableEpoch = prevEpoch + 1 + params.BeaconConfig().MinValidatorWithdrawabilityDelay
|
||||
triggerNewHeadEvent(t, svc, [32]byte{})
|
||||
|
||||
retrieved, err := beaconDB.ArchivedActiveValidatorChanges(svc.ctx, currentEpoch)
|
||||
retrieved, err := beaconDB.ArchivedActiveValidatorChanges(svc.ctx, prevEpoch)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -251,14 +241,7 @@ func setupState(t *testing.T, validatorCount uint64) *pb.BeaconState {
|
||||
balances[i] = params.BeaconConfig().MaxEffectiveBalance
|
||||
}
|
||||
|
||||
atts := []*pb.PendingAttestation{{Data: ðpb.AttestationData{Crosslink: ðpb.Crosslink{Shard: 0}, Target: ðpb.Checkpoint{}}}}
|
||||
var crosslinks []*ethpb.Crosslink
|
||||
for i := uint64(0); i < params.BeaconConfig().ShardCount; i++ {
|
||||
crosslinks = append(crosslinks, ðpb.Crosslink{
|
||||
StartEpoch: 0,
|
||||
DataRoot: []byte{'A'},
|
||||
})
|
||||
}
|
||||
atts := []*pb.PendingAttestation{{Data: ðpb.AttestationData{Target: ðpb.Checkpoint{}}}}
|
||||
|
||||
// We initialize a head state that has attestations from participated
|
||||
// validators in a simulated fashion.
|
||||
@@ -269,9 +252,6 @@ func setupState(t *testing.T, validatorCount uint64) *pb.BeaconState {
|
||||
BlockRoots: make([][]byte, 128),
|
||||
Slashings: []uint64{0, 1e9, 1e9},
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
CompactCommitteesRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
CurrentCrosslinks: crosslinks,
|
||||
CurrentEpochAttestations: atts,
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{},
|
||||
JustificationBits: bitfield.Bitvector4{0x00},
|
||||
|
||||
@@ -110,5 +110,11 @@ func (s *Service) GenesisTime() time.Time {
|
||||
|
||||
// CurrentFork retrieves the latest fork information of the beacon chain.
|
||||
func (s *Service) CurrentFork() *pb.Fork {
|
||||
if s.headState == nil {
|
||||
return &pb.Fork{
|
||||
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
|
||||
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
|
||||
}
|
||||
}
|
||||
return proto.Clone(s.headState.Fork).(*pb.Fork)
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ go_library(
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/traceutil:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||
@@ -58,6 +59,7 @@ go_test(
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
|
||||
@@ -3,7 +3,6 @@ package forkchoice
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/pkg/errors"
|
||||
@@ -16,7 +15,6 @@ import (
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
@@ -54,7 +52,7 @@ import (
|
||||
// for i in indexed_attestation.custody_bit_0_indices + indexed_attestation.custody_bit_1_indices:
|
||||
// if i not in store.latest_messages or target.epoch > store.latest_messages[i].epoch:
|
||||
// store.latest_messages[i] = LatestMessage(epoch=target.epoch, root=attestation.data.beacon_block_root)
|
||||
func (s *Store) OnAttestation(ctx context.Context, a *ethpb.Attestation) (uint64, error) {
|
||||
func (s *Store) OnAttestation(ctx context.Context, a *ethpb.Attestation) error {
|
||||
ctx, span := trace.StartSpan(ctx, "forkchoice.onAttestation")
|
||||
defer span.End()
|
||||
|
||||
@@ -63,76 +61,60 @@ func (s *Store) OnAttestation(ctx context.Context, a *ethpb.Attestation) (uint64
|
||||
|
||||
// Verify beacon node has seen the target block before.
|
||||
if !s.db.HasBlock(ctx, bytesutil.ToBytes32(tgt.Root)) {
|
||||
return 0, fmt.Errorf("target root %#x does not exist in db", bytesutil.Trunc(tgt.Root))
|
||||
return fmt.Errorf("target root %#x does not exist in db", bytesutil.Trunc(tgt.Root))
|
||||
}
|
||||
|
||||
// Verify attestation target has had a valid pre state produced by the target block.
|
||||
baseState, err := s.verifyAttPreState(ctx, tgt)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return err
|
||||
}
|
||||
|
||||
// Verify Attestations cannot be from future epochs.
|
||||
if err := helpers.VerifySlotTime(baseState.GenesisTime, tgtSlot); err != nil {
|
||||
return 0, errors.Wrap(err, "could not verify attestation target slot")
|
||||
return errors.Wrap(err, "could not verify attestation target slot")
|
||||
}
|
||||
|
||||
// Store target checkpoint state if not yet seen.
|
||||
baseState, err = s.saveCheckpointState(ctx, baseState, tgt)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Delay attestation processing until the subsequent slot.
|
||||
if err := s.waitForAttInclDelay(ctx, a, baseState); err != nil {
|
||||
return 0, err
|
||||
return err
|
||||
}
|
||||
|
||||
// Verify attestations can only affect the fork choice of subsequent slots.
|
||||
if err := s.verifyAttSlotTime(ctx, baseState, a.Data); err != nil {
|
||||
return 0, err
|
||||
if err := helpers.VerifySlotTime(baseState.GenesisTime, a.Data.Slot+1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.attsQueueLock.Lock()
|
||||
defer s.attsQueueLock.Unlock()
|
||||
atts := make([]*ethpb.Attestation, 0, len(s.attsQueue))
|
||||
for root, a := range s.attsQueue {
|
||||
log := log.WithFields(logrus.Fields{
|
||||
"AggregatedBitfield": fmt.Sprintf("%08b", a.AggregationBits),
|
||||
"Root": fmt.Sprintf("%#x", root),
|
||||
})
|
||||
log.Debug("Updating latest votes")
|
||||
|
||||
// Use the target state to to validate attestation and calculate the committees.
|
||||
indexedAtt, err := s.verifyAttestation(ctx, baseState, a)
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("Removing attestation from queue.")
|
||||
delete(s.attsQueue, root)
|
||||
continue
|
||||
}
|
||||
|
||||
// Update every validator's latest vote.
|
||||
if err := s.updateAttVotes(ctx, indexedAtt, tgt.Root, tgt.Epoch); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Mark attestation as seen we don't update votes when it appears in block.
|
||||
if err := s.setSeenAtt(a); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
delete(s.attsQueue, root)
|
||||
att, err := s.aggregatedAttestations(ctx, a)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
atts = append(atts, att...)
|
||||
// Use the target state to to validate attestation and calculate the committees.
|
||||
indexedAtt, err := s.verifyAttestation(ctx, baseState, a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.db.SaveAttestations(ctx, atts); err != nil {
|
||||
return 0, err
|
||||
// Update every validator's latest vote.
|
||||
if err := s.updateAttVotes(ctx, indexedAtt, tgt.Root, tgt.Epoch); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tgtSlot, nil
|
||||
// Mark attestation as seen we don't update votes when it appears in block.
|
||||
if err := s.setSeenAtt(a); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.db.SaveAttestation(ctx, a); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log := log.WithFields(logrus.Fields{
|
||||
"Slot": a.Data.Slot,
|
||||
"Index": a.Data.Index,
|
||||
"AggregatedBitfield": fmt.Sprintf("%08b", a.AggregationBits),
|
||||
"BeaconBlockRoot": fmt.Sprintf("%#x", bytesutil.Trunc(a.Data.BeaconBlockRoot)),
|
||||
})
|
||||
log.Debug("Updated latest votes")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// verifyAttPreState validates input attested check point has a valid pre-state.
|
||||
@@ -178,60 +160,6 @@ func (s *Store) saveCheckpointState(ctx context.Context, baseState *pb.BeaconSta
|
||||
return baseState, nil
|
||||
}
|
||||
|
||||
// waitForAttInclDelay waits until the next slot because attestation can only affect
|
||||
// fork choice of subsequent slot. This is to delay attestation inclusion for fork choice
|
||||
// until the attested slot is in the past.
|
||||
func (s *Store) waitForAttInclDelay(ctx context.Context, a *ethpb.Attestation, targetState *pb.BeaconState) error {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon-chain.forkchoice.waitForAttInclDelay")
|
||||
defer span.End()
|
||||
|
||||
slot, err := helpers.AttestationDataSlot(targetState, a.Data)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get attestation slot")
|
||||
}
|
||||
|
||||
nextSlot := slot + 1
|
||||
duration := time.Duration(nextSlot*params.BeaconConfig().SecondsPerSlot) * time.Second
|
||||
timeToInclude := time.Unix(int64(targetState.GenesisTime), 0).Add(duration)
|
||||
|
||||
if err := s.aggregateAttestation(ctx, a); err != nil {
|
||||
return errors.Wrap(err, "could not aggregate attestation")
|
||||
}
|
||||
|
||||
time.Sleep(time.Until(timeToInclude))
|
||||
return nil
|
||||
}
|
||||
|
||||
// aggregateAttestation aggregates the attestations in the pending queue.
|
||||
func (s *Store) aggregateAttestation(ctx context.Context, att *ethpb.Attestation) error {
|
||||
s.attsQueueLock.Lock()
|
||||
defer s.attsQueueLock.Unlock()
|
||||
root, err := ssz.HashTreeRoot(att.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if a, ok := s.attsQueue[root]; ok {
|
||||
a, err := helpers.AggregateAttestation(a, att)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
s.attsQueue[root] = a
|
||||
return nil
|
||||
}
|
||||
s.attsQueue[root] = proto.Clone(att).(*ethpb.Attestation)
|
||||
return nil
|
||||
}
|
||||
|
||||
// verifyAttSlotTime validates input attestation is not from the future.
|
||||
func (s *Store) verifyAttSlotTime(ctx context.Context, baseState *pb.BeaconState, d *ethpb.AttestationData) error {
|
||||
aSlot, err := helpers.AttestationDataSlot(baseState, d)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get attestation slot")
|
||||
}
|
||||
return helpers.VerifySlotTime(baseState.GenesisTime, aSlot+1)
|
||||
}
|
||||
|
||||
// verifyAttestation validates input attestation is valid.
|
||||
func (s *Store) verifyAttestation(ctx context.Context, baseState *pb.BeaconState, a *ethpb.Attestation) (*ethpb.IndexedAttestation, error) {
|
||||
indexedAtt, err := blocks.ConvertToIndexed(ctx, baseState, a)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package forkchoice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"reflect"
|
||||
"strings"
|
||||
@@ -9,12 +8,10 @@ import (
|
||||
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
)
|
||||
|
||||
func TestStore_OnAttestation(t *testing.T) {
|
||||
@@ -55,8 +52,7 @@ func TestStore_OnAttestation(t *testing.T) {
|
||||
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
|
||||
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
|
||||
},
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
}, BlkWithValidStateRoot); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -98,7 +94,7 @@ func TestStore_OnAttestation(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err := store.OnAttestation(ctx, tt.a)
|
||||
err := store.OnAttestation(ctx, tt.a)
|
||||
if tt.wantErr {
|
||||
if !strings.Contains(err.Error(), tt.wantErrString) {
|
||||
t.Errorf("Store.OnAttestation() error = %v, wantErr = %v", err, tt.wantErrString)
|
||||
@@ -118,30 +114,19 @@ func TestStore_SaveCheckpointState(t *testing.T) {
|
||||
|
||||
store := NewForkChoiceService(ctx, db)
|
||||
|
||||
crosslinks := make([]*ethpb.Crosslink, params.BeaconConfig().ShardCount)
|
||||
for i := 0; i < len(crosslinks); i++ {
|
||||
crosslinks[i] = ðpb.Crosslink{
|
||||
ParentRoot: make([]byte, 32),
|
||||
DataRoot: make([]byte, 32),
|
||||
}
|
||||
}
|
||||
s := &pb.BeaconState{
|
||||
Fork: &pb.Fork{
|
||||
Epoch: 0,
|
||||
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
|
||||
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
|
||||
},
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
StateRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
BlockRoots: make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot),
|
||||
LatestBlockHeader: ðpb.BeaconBlockHeader{},
|
||||
JustificationBits: []byte{0},
|
||||
CurrentJustifiedCheckpoint: ðpb.Checkpoint{},
|
||||
CurrentCrosslinks: crosslinks,
|
||||
CompactCommitteesRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{},
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
StateRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
BlockRoots: make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot),
|
||||
LatestBlockHeader: ðpb.BeaconBlockHeader{},
|
||||
JustificationBits: []byte{0},
|
||||
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{},
|
||||
}
|
||||
if err := store.GenesisStore(ctx, ðpb.Checkpoint{}, ðpb.Checkpoint{}); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -203,51 +188,6 @@ func TestStore_SaveCheckpointState(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestStore_AggregateAttestation(t *testing.T) {
|
||||
_, _, privKeys := testutil.SetupInitialDeposits(t, 100)
|
||||
f := &pb.Fork{
|
||||
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
|
||||
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
|
||||
Epoch: 0,
|
||||
}
|
||||
domain := helpers.Domain(f, 0, params.BeaconConfig().DomainAttestation)
|
||||
sig := privKeys[0].Sign([]byte{}, domain)
|
||||
|
||||
store := &Store{attsQueue: make(map[[32]byte]*ethpb.Attestation)}
|
||||
|
||||
b1 := bitfield.NewBitlist(8)
|
||||
b1.SetBitAt(0, true)
|
||||
a := ðpb.Attestation{Data: ðpb.AttestationData{}, AggregationBits: b1, Signature: sig.Marshal()}
|
||||
|
||||
if err := store.aggregateAttestation(context.Background(), a); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
r, _ := ssz.HashTreeRoot(a.Data)
|
||||
if !bytes.Equal(store.attsQueue[r].AggregationBits, b1) {
|
||||
t.Error("Received incorrect aggregation bitfield")
|
||||
}
|
||||
|
||||
b2 := bitfield.NewBitlist(8)
|
||||
b2.SetBitAt(1, true)
|
||||
a = ðpb.Attestation{Data: ðpb.AttestationData{}, AggregationBits: b2, Signature: sig.Marshal()}
|
||||
if err := store.aggregateAttestation(context.Background(), a); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(store.attsQueue[r].AggregationBits, []byte{3, 1}) {
|
||||
t.Error("Received incorrect aggregation bitfield")
|
||||
}
|
||||
|
||||
b3 := bitfield.NewBitlist(8)
|
||||
b3.SetBitAt(7, true)
|
||||
a = ðpb.Attestation{Data: ðpb.AttestationData{}, AggregationBits: b3, Signature: sig.Marshal()}
|
||||
if err := store.aggregateAttestation(context.Background(), a); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(store.attsQueue[r].AggregationBits, []byte{131, 1}) {
|
||||
t.Error("Received incorrect aggregation bitfield")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStore_ReturnAggregatedAttestation(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
db := testDB.SetupDB(t)
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/traceutil"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
@@ -72,7 +72,6 @@ func (s *Store) OnBlock(ctx context.Context, b *ethpb.BeaconBlock) error {
|
||||
"slot": b.Slot,
|
||||
"root": fmt.Sprintf("0x%s...", hex.EncodeToString(root[:])[:8]),
|
||||
}).Info("Executing state transition on block")
|
||||
|
||||
postState, err := state.ExecuteStateTransition(ctx, preState, b)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not execute state transition")
|
||||
@@ -105,13 +104,16 @@ func (s *Store) OnBlock(ctx context.Context, b *ethpb.BeaconBlock) error {
|
||||
return errors.Wrap(err, "could not save finalized checkpoint")
|
||||
}
|
||||
|
||||
startSlot := helpers.StartSlot(s.finalizedCheckpt.Epoch + 1)
|
||||
endSlot := helpers.StartSlot(postState.FinalizedCheckpoint.Epoch+1) - 1 // Inclusive
|
||||
if err := s.rmStatesOlderThanLastFinalized(ctx, startSlot, endSlot); err != nil {
|
||||
return errors.Wrapf(err, "could not delete states prior to finalized check point, range: %d, %d",
|
||||
startSlot, endSlot+params.BeaconConfig().SlotsPerEpoch)
|
||||
startSlot := helpers.StartSlot(s.prevFinalizedCheckpt.Epoch) + 1
|
||||
endSlot := helpers.StartSlot(s.finalizedCheckpt.Epoch)
|
||||
if endSlot > startSlot {
|
||||
if err := s.rmStatesOlderThanLastFinalized(ctx, startSlot, endSlot); err != nil {
|
||||
return errors.Wrapf(err, "could not delete states prior to finalized check point, range: %d, %d",
|
||||
startSlot, endSlot)
|
||||
}
|
||||
}
|
||||
|
||||
s.prevFinalizedCheckpt = s.finalizedCheckpt
|
||||
s.finalizedCheckpt = postState.FinalizedCheckpoint
|
||||
}
|
||||
|
||||
@@ -185,17 +187,22 @@ func (s *Store) OnBlockNoVerifyStateTransition(ctx context.Context, b *ethpb.Bea
|
||||
if postState.FinalizedCheckpoint.Epoch > s.finalizedCheckpt.Epoch {
|
||||
s.clearSeenAtts()
|
||||
helpers.ClearAllCaches()
|
||||
startSlot := helpers.StartSlot(s.finalizedCheckpt.Epoch + 1)
|
||||
endSlot := helpers.StartSlot(postState.FinalizedCheckpoint.Epoch+1) - 1 // Inclusive
|
||||
if err := s.rmStatesOlderThanLastFinalized(ctx, startSlot, endSlot); err != nil {
|
||||
return errors.Wrapf(err, "could not delete states prior to finalized check point, range: %d, %d",
|
||||
startSlot, endSlot+params.BeaconConfig().SlotsPerEpoch)
|
||||
|
||||
startSlot := helpers.StartSlot(s.prevFinalizedCheckpt.Epoch) + 1
|
||||
endSlot := helpers.StartSlot(s.finalizedCheckpt.Epoch)
|
||||
if endSlot > startSlot {
|
||||
if err := s.rmStatesOlderThanLastFinalized(ctx, startSlot, endSlot); err != nil {
|
||||
return errors.Wrapf(err, "could not delete states prior to finalized check point, range: %d, %d",
|
||||
startSlot, endSlot)
|
||||
}
|
||||
}
|
||||
|
||||
s.finalizedCheckpt = postState.FinalizedCheckpoint
|
||||
if err := s.db.SaveFinalizedCheckpoint(ctx, postState.FinalizedCheckpoint); err != nil {
|
||||
return errors.Wrap(err, "could not save finalized checkpoint")
|
||||
}
|
||||
|
||||
s.prevFinalizedCheckpt = s.finalizedCheckpt
|
||||
s.finalizedCheckpt = postState.FinalizedCheckpoint
|
||||
}
|
||||
|
||||
// Update validator indices in database as needed.
|
||||
@@ -210,6 +217,13 @@ func (s *Store) OnBlockNoVerifyStateTransition(ctx context.Context, b *ethpb.Bea
|
||||
// Epoch boundary bookkeeping such as logging epoch summaries.
|
||||
if helpers.IsEpochStart(postState.Slot) {
|
||||
reportEpochMetrics(postState)
|
||||
|
||||
// Update committee shuffled indices at the end of every epoch
|
||||
if featureconfig.Get().EnableNewCache {
|
||||
if err := helpers.UpdateCommitteeCache(postState); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -219,6 +233,9 @@ func (s *Store) OnBlockNoVerifyStateTransition(ctx context.Context, b *ethpb.Bea
|
||||
// to retrieve the state in DB. It verifies the pre state's validity and the incoming block
|
||||
// is in the correct time window.
|
||||
func (s *Store) getBlockPreState(ctx context.Context, b *ethpb.BeaconBlock) (*pb.BeaconState, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "forkchoice.getBlockPreState")
|
||||
defer span.End()
|
||||
|
||||
// Verify incoming block has a valid pre state.
|
||||
preState, err := s.verifyBlkPreState(ctx, b)
|
||||
if err != nil {
|
||||
@@ -273,6 +290,9 @@ func (s *Store) updateBlockAttestationVote(ctx context.Context, att *ethpb.Attes
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get state for attestation tgt root")
|
||||
}
|
||||
if baseState == nil {
|
||||
return errors.New("no state found in db with attestation tgt root")
|
||||
}
|
||||
indexedAtt, err := blocks.ConvertToIndexed(ctx, baseState, att)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not convert attestation to indexed attestation")
|
||||
@@ -309,6 +329,9 @@ func (s *Store) verifyBlkPreState(ctx context.Context, b *ethpb.BeaconBlock) (*p
|
||||
// verifyBlkDescendant validates input block root is a descendant of the
|
||||
// current finalized block root.
|
||||
func (s *Store) verifyBlkDescendant(ctx context.Context, root [32]byte, slot uint64) error {
|
||||
ctx, span := trace.StartSpan(ctx, "forkchoice.verifyBlkDescendant")
|
||||
defer span.End()
|
||||
|
||||
finalizedBlk, err := s.db.Block(ctx, bytesutil.ToBytes32(s.finalizedCheckpt.Root))
|
||||
if err != nil || finalizedBlk == nil {
|
||||
return errors.Wrap(err, "could not get finalized block")
|
||||
@@ -319,7 +342,10 @@ func (s *Store) verifyBlkDescendant(ctx context.Context, root [32]byte, slot uin
|
||||
return errors.Wrap(err, "could not get finalized block root")
|
||||
}
|
||||
if !bytes.Equal(bFinalizedRoot, s.finalizedCheckpt.Root) {
|
||||
return fmt.Errorf("block from slot %d is not a descendent of the current finalized block", slot)
|
||||
err := fmt.Errorf("block from slot %d is not a descendent of the current finalized block slot %d, %#x != %#x",
|
||||
slot, finalizedBlk.Slot, bytesutil.Trunc(bFinalizedRoot), bytesutil.Trunc(s.finalizedCheckpt.Root))
|
||||
traceutil.AnnotateError(span, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -382,11 +408,29 @@ func (s *Store) rmStatesOlderThanLastFinalized(ctx context.Context, startSlot ui
|
||||
ctx, span := trace.StartSpan(ctx, "forkchoice.rmStatesBySlots")
|
||||
defer span.End()
|
||||
|
||||
// Do not remove genesis state or finalized state at epoch boundary.
|
||||
if startSlot%params.BeaconConfig().SlotsPerEpoch == 0 {
|
||||
if !featureconfig.Get().PruneFinalizedStates {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Make sure finalized slot is not a skipped slot.
|
||||
for i := endSlot; i > 0; i-- {
|
||||
filter := filters.NewFilter().SetStartSlot(i).SetEndSlot(i)
|
||||
b, err := s.db.Blocks(ctx, filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(b) > 0 {
|
||||
endSlot = i - 1
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Do not remove genesis state
|
||||
if startSlot == 0 {
|
||||
startSlot++
|
||||
}
|
||||
|
||||
// Do not remove finalized state that's in the middle of slot ranges.
|
||||
filter := filters.NewFilter().SetStartSlot(startSlot).SetEndSlot(endSlot)
|
||||
roots, err := s.db.BlockRoots(ctx, filter)
|
||||
if err != nil {
|
||||
|
||||
@@ -15,11 +15,18 @@ import (
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
)
|
||||
|
||||
func init() {
|
||||
fc := featureconfig.Get()
|
||||
fc.PruneFinalizedStates = true
|
||||
featureconfig.Init(fc)
|
||||
}
|
||||
|
||||
func TestStore_OnBlock(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
db := testDB.SetupDB(t)
|
||||
@@ -126,7 +133,7 @@ func TestStore_UpdateBlockAttestationVote(t *testing.T) {
|
||||
params.UseMinimalConfig()
|
||||
|
||||
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -137,10 +144,6 @@ func TestStore_UpdateBlockAttestationVote(t *testing.T) {
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 0, Root: params.BeaconConfig().ZeroHash[:]},
|
||||
Target: ðpb.Checkpoint{Epoch: 0, Root: r[:]},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 0,
|
||||
StartEpoch: 0,
|
||||
},
|
||||
},
|
||||
AggregationBits: []byte{255},
|
||||
CustodyBits: []byte{255},
|
||||
@@ -180,7 +183,7 @@ func TestStore_UpdateBlockAttestationsVote(t *testing.T) {
|
||||
params.UseMinimalConfig()
|
||||
|
||||
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -194,10 +197,6 @@ func TestStore_UpdateBlockAttestationsVote(t *testing.T) {
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 0, Root: params.BeaconConfig().ZeroHash[:]},
|
||||
Target: ðpb.Checkpoint{Epoch: 0, Root: r[:]},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: uint64(i),
|
||||
StartEpoch: 0,
|
||||
},
|
||||
},
|
||||
AggregationBits: []byte{255},
|
||||
CustodyBits: []byte{255},
|
||||
@@ -308,6 +307,7 @@ func TestRemoveStateSinceLastFinalized(t *testing.T) {
|
||||
|
||||
// New finalized epoch: 1
|
||||
finalizedEpoch := uint64(1)
|
||||
finalizedSlot := finalizedEpoch * params.BeaconConfig().SlotsPerEpoch
|
||||
endSlot := helpers.StartSlot(finalizedEpoch+1) - 1 // Inclusive
|
||||
if err := store.rmStatesOlderThanLastFinalized(ctx, 0, endSlot); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -318,15 +318,16 @@ func TestRemoveStateSinceLastFinalized(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Also verifies genesis state didnt get deleted
|
||||
if s != nil && s.Slot != 0 && s.Slot < endSlot {
|
||||
if s != nil && s.Slot != finalizedSlot && s.Slot != 0 && s.Slot < endSlot {
|
||||
t.Errorf("State with slot %d should not be in DB", s.Slot)
|
||||
}
|
||||
}
|
||||
|
||||
// New finalized epoch: 5
|
||||
newFinalizedEpoch := uint64(5)
|
||||
newFinalizedSlot := newFinalizedEpoch * params.BeaconConfig().SlotsPerEpoch
|
||||
endSlot = helpers.StartSlot(newFinalizedEpoch+1) - 1 // Inclusive
|
||||
if err := store.rmStatesOlderThanLastFinalized(ctx, helpers.StartSlot(finalizedEpoch+1), endSlot); err != nil {
|
||||
if err := store.rmStatesOlderThanLastFinalized(ctx, helpers.StartSlot(finalizedEpoch+1)-1, endSlot); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, r := range blockRoots {
|
||||
@@ -334,12 +335,9 @@ func TestRemoveStateSinceLastFinalized(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Also verifies boundary state didnt get deleted
|
||||
if s != nil {
|
||||
isBoundary := s.Slot%params.BeaconConfig().SlotsPerEpoch == 0
|
||||
if !isBoundary && s.Slot < endSlot {
|
||||
t.Errorf("State with slot %d should not be in DB", s.Slot)
|
||||
}
|
||||
// Also verifies genesis state didnt get deleted
|
||||
if s != nil && s.Slot != newFinalizedSlot && s.Slot != finalizedSlot && s.Slot != 0 && s.Slot < endSlot {
|
||||
t.Errorf("State with slot %d should not be in DB", s.Slot)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ type ForkChoicer interface {
|
||||
Head(ctx context.Context) ([]byte, error)
|
||||
OnBlock(ctx context.Context, b *ethpb.BeaconBlock) error
|
||||
OnBlockNoVerifyStateTransition(ctx context.Context, b *ethpb.BeaconBlock) error
|
||||
OnAttestation(ctx context.Context, a *ethpb.Attestation) (uint64, error)
|
||||
OnAttestation(ctx context.Context, a *ethpb.Attestation) error
|
||||
GenesisStore(ctx context.Context, justifiedCheckpoint *ethpb.Checkpoint, finalizedCheckpoint *ethpb.Checkpoint) error
|
||||
FinalizedCheckpt() *ethpb.Checkpoint
|
||||
}
|
||||
@@ -31,17 +31,16 @@ type ForkChoicer interface {
|
||||
// Store represents a service struct that handles the forkchoice
|
||||
// logic of managing the full PoS beacon chain.
|
||||
type Store struct {
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
db db.Database
|
||||
justifiedCheckpt *ethpb.Checkpoint
|
||||
finalizedCheckpt *ethpb.Checkpoint
|
||||
checkpointState *cache.CheckpointStateCache
|
||||
checkpointStateLock sync.Mutex
|
||||
attsQueue map[[32]byte]*ethpb.Attestation
|
||||
attsQueueLock sync.Mutex
|
||||
seenAtts map[[32]byte]bool
|
||||
seenAttsLock sync.Mutex
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
db db.Database
|
||||
justifiedCheckpt *ethpb.Checkpoint
|
||||
finalizedCheckpt *ethpb.Checkpoint
|
||||
prevFinalizedCheckpt *ethpb.Checkpoint
|
||||
checkpointState *cache.CheckpointStateCache
|
||||
checkpointStateLock sync.Mutex
|
||||
seenAtts map[[32]byte]bool
|
||||
seenAttsLock sync.Mutex
|
||||
}
|
||||
|
||||
// NewForkChoiceService instantiates a new service instance that will
|
||||
@@ -53,7 +52,6 @@ func NewForkChoiceService(ctx context.Context, db db.Database) *Store {
|
||||
cancel: cancel,
|
||||
db: db,
|
||||
checkpointState: cache.NewCheckpointStateCache(),
|
||||
attsQueue: make(map[[32]byte]*ethpb.Attestation),
|
||||
seenAtts: make(map[[32]byte]bool),
|
||||
}
|
||||
}
|
||||
@@ -82,6 +80,7 @@ func (s *Store) GenesisStore(
|
||||
|
||||
s.justifiedCheckpt = proto.Clone(justifiedCheckpoint).(*ethpb.Checkpoint)
|
||||
s.finalizedCheckpt = proto.Clone(finalizedCheckpoint).(*ethpb.Checkpoint)
|
||||
s.prevFinalizedCheckpt = proto.Clone(finalizedCheckpoint).(*ethpb.Checkpoint)
|
||||
|
||||
justifiedState, err := s.db.State(ctx, bytesutil.ToBytes32(s.justifiedCheckpt.Root))
|
||||
if err != nil {
|
||||
|
||||
@@ -3,57 +3,20 @@ package blockchain
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
// AttestationReceiver interface defines the methods of chain service receive and processing new attestations.
|
||||
type AttestationReceiver interface {
|
||||
ReceiveAttestation(ctx context.Context, att *ethpb.Attestation) error
|
||||
ReceiveAttestationNoPubsub(ctx context.Context, att *ethpb.Attestation) error
|
||||
}
|
||||
|
||||
// ReceiveAttestation is a function that defines the operations that are preformed on
|
||||
// attestation that is received from regular sync. The operations consist of:
|
||||
// 1. Gossip attestation to other peers
|
||||
// 2. Validate attestation, update validator's latest vote
|
||||
// 3. Apply fork choice to the processed attestation
|
||||
// 4. Save latest head info
|
||||
func (s *Service) ReceiveAttestation(ctx context.Context, att *ethpb.Attestation) error {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon-chain.blockchain.ReceiveAttestation")
|
||||
defer span.End()
|
||||
|
||||
// Broadcast the new attestation to the network.
|
||||
if err := s.p2p.Broadcast(ctx, att); err != nil {
|
||||
return errors.Wrap(err, "could not broadcast attestation")
|
||||
}
|
||||
|
||||
attDataRoot, err := ssz.HashTreeRoot(att.Data)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to hash attestation")
|
||||
}
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"attRoot": fmt.Sprintf("%#x", attDataRoot),
|
||||
"blockRoot": fmt.Sprintf("%#x", att.Data.BeaconBlockRoot),
|
||||
}).Debug("Broadcasting attestation")
|
||||
|
||||
if err := s.ReceiveAttestationNoPubsub(ctx, att); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
processedAtt.Inc()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReceiveAttestationNoPubsub is a function that defines the operations that are preformed on
|
||||
// attestation that is received from regular sync. The operations consist of:
|
||||
// 1. Validate attestation, update validator's latest vote
|
||||
@@ -64,8 +27,7 @@ func (s *Service) ReceiveAttestationNoPubsub(ctx context.Context, att *ethpb.Att
|
||||
defer span.End()
|
||||
|
||||
// Update forkchoice store for the new attestation
|
||||
attSlot, err := s.forkChoiceStore.OnAttestation(ctx, att)
|
||||
if err != nil {
|
||||
if err := s.forkChoiceStore.OnAttestation(ctx, att); err != nil {
|
||||
return errors.Wrap(err, "could not process attestation from fork choice service")
|
||||
}
|
||||
|
||||
@@ -85,28 +47,31 @@ func (s *Service) ReceiveAttestationNoPubsub(ctx context.Context, att *ethpb.Att
|
||||
}
|
||||
}
|
||||
|
||||
// Skip checking for competing attestation's target roots at epoch boundary.
|
||||
if !helpers.IsEpochStart(attSlot) {
|
||||
s.headLock.RLock()
|
||||
defer s.headLock.RUnlock()
|
||||
targetRoot, err := helpers.BlockRoot(s.headState, att.Data.Target.Epoch)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not get target root for epoch %d", att.Data.Target.Epoch)
|
||||
}
|
||||
isCompetingAtts(targetRoot, att.Data.Target.Root[:])
|
||||
}
|
||||
|
||||
processedAttNoPubsub.Inc()
|
||||
return nil
|
||||
}
|
||||
|
||||
// This checks if the attestation is from a competing chain, emits warning and updates metrics.
|
||||
func isCompetingAtts(headTargetRoot []byte, attTargetRoot []byte) {
|
||||
if !bytes.Equal(attTargetRoot, headTargetRoot) {
|
||||
log.WithFields(logrus.Fields{
|
||||
"attTargetRoot": hex.EncodeToString(attTargetRoot),
|
||||
"headTargetRoot": hex.EncodeToString(headTargetRoot),
|
||||
}).Warn("target heads different from new attestation")
|
||||
competingAtts.Inc()
|
||||
// This processes attestations from the attestation pool to account for validator votes and fork choice.
|
||||
func (s *Service) processAttestation() {
|
||||
period := time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second
|
||||
ticker := time.NewTicker(period)
|
||||
for {
|
||||
ctx := context.Background()
|
||||
select {
|
||||
case <-ticker.C:
|
||||
atts, err := s.opsPoolService.AttestationPoolForForkchoice(ctx)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not retrieve attestation from pool")
|
||||
}
|
||||
|
||||
for _, a := range atts {
|
||||
if err := s.ReceiveAttestationNoPubsub(ctx, a); err != nil {
|
||||
log.WithError(err).Error("Could not receive attestation in chain service")
|
||||
}
|
||||
}
|
||||
case <-s.ctx.Done():
|
||||
log.Debug("Context closed, exiting routine")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,75 +12,6 @@ import (
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func TestReceiveAttestation_ProcessCorrectly(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
db := testDB.SetupDB(t)
|
||||
defer testDB.TeardownDB(t, db)
|
||||
ctx := context.Background()
|
||||
|
||||
chainService := setupBeaconChain(t, db)
|
||||
r, _ := ssz.SigningRoot(ðpb.BeaconBlock{})
|
||||
chainService.forkChoiceStore = &store{headRoot: r[:]}
|
||||
|
||||
b := ðpb.BeaconBlock{}
|
||||
if err := chainService.beaconDB.SaveBlock(ctx, b); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
root, err := ssz.SigningRoot(b)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := chainService.beaconDB.SaveState(ctx, &pb.BeaconState{}, root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
a := ðpb.Attestation{Data: ðpb.AttestationData{
|
||||
Target: ðpb.Checkpoint{Root: root[:]},
|
||||
Crosslink: ðpb.Crosslink{},
|
||||
}}
|
||||
if err := chainService.ReceiveAttestation(ctx, a); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testutil.AssertLogsContain(t, hook, "Saved new head info")
|
||||
testutil.AssertLogsContain(t, hook, "Broadcasting attestation")
|
||||
}
|
||||
|
||||
func TestReceiveAttestation_SameHead(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
db := testDB.SetupDB(t)
|
||||
defer testDB.TeardownDB(t, db)
|
||||
ctx := context.Background()
|
||||
|
||||
chainService := setupBeaconChain(t, db)
|
||||
r, _ := ssz.SigningRoot(ðpb.BeaconBlock{})
|
||||
chainService.forkChoiceStore = &store{headRoot: r[:]}
|
||||
chainService.canonicalRoots[0] = r[:]
|
||||
|
||||
b := ðpb.BeaconBlock{}
|
||||
if err := chainService.beaconDB.SaveBlock(ctx, b); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
root, err := ssz.SigningRoot(b)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := chainService.beaconDB.SaveState(ctx, &pb.BeaconState{}, root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
a := ðpb.Attestation{Data: ðpb.AttestationData{
|
||||
Target: ðpb.Checkpoint{Root: root[:]},
|
||||
Crosslink: ðpb.Crosslink{},
|
||||
}}
|
||||
if err := chainService.ReceiveAttestation(ctx, a); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testutil.AssertLogsDoNotContain(t, hook, "Saved new head info")
|
||||
testutil.AssertLogsContain(t, hook, "Broadcasting attestation")
|
||||
}
|
||||
|
||||
func TestReceiveAttestationNoPubsub_ProcessCorrectly(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
db := testDB.SetupDB(t)
|
||||
@@ -104,8 +35,7 @@ func TestReceiveAttestationNoPubsub_ProcessCorrectly(t *testing.T) {
|
||||
}
|
||||
|
||||
a := ðpb.Attestation{Data: ðpb.AttestationData{
|
||||
Target: ðpb.Checkpoint{Root: root[:]},
|
||||
Crosslink: ðpb.Crosslink{},
|
||||
Target: ðpb.Checkpoint{Root: root[:]},
|
||||
}}
|
||||
if err := chainService.ReceiveAttestationNoPubsub(ctx, a); err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
@@ -61,14 +62,15 @@ func (s *Service) ReceiveBlock(ctx context.Context, block *ethpb.BeaconBlock) er
|
||||
func (s *Service) ReceiveBlockNoPubsub(ctx context.Context, block *ethpb.BeaconBlock) error {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon-chain.blockchain.ReceiveBlockNoPubsub")
|
||||
defer span.End()
|
||||
blockCopy := proto.Clone(block).(*ethpb.BeaconBlock)
|
||||
|
||||
// Apply state transition on the new block.
|
||||
if err := s.forkChoiceStore.OnBlock(ctx, block); err != nil {
|
||||
if err := s.forkChoiceStore.OnBlock(ctx, blockCopy); err != nil {
|
||||
err := errors.Wrap(err, "could not process block from fork choice service")
|
||||
traceutil.AnnotateError(span, err)
|
||||
return err
|
||||
}
|
||||
root, err := ssz.SigningRoot(block)
|
||||
root, err := ssz.SigningRoot(blockCopy)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get signing root on received block")
|
||||
}
|
||||
@@ -91,18 +93,18 @@ func (s *Service) ReceiveBlockNoPubsub(ctx context.Context, block *ethpb.BeaconB
|
||||
}
|
||||
|
||||
// Remove block's contained deposits, attestations, and other operations from persistent storage.
|
||||
if err := s.cleanupBlockOperations(ctx, block); err != nil {
|
||||
if err := s.cleanupBlockOperations(ctx, blockCopy); err != nil {
|
||||
return errors.Wrap(err, "could not clean up block deposits, attestations, and other operations")
|
||||
}
|
||||
|
||||
// Reports on block and fork choice metrics.
|
||||
s.reportSlotMetrics(block.Slot)
|
||||
s.reportSlotMetrics(blockCopy.Slot)
|
||||
|
||||
// Log if block is a competing block.
|
||||
isCompetingBlock(root[:], block.Slot, headRoot, headBlk.Slot)
|
||||
isCompetingBlock(root[:], blockCopy.Slot, headRoot, headBlk.Slot)
|
||||
|
||||
// Log state transition data.
|
||||
logStateTransitionData(block, root[:])
|
||||
logStateTransitionData(blockCopy, root[:])
|
||||
|
||||
processedBlkNoPubsub.Inc()
|
||||
|
||||
@@ -118,34 +120,35 @@ func (s *Service) ReceiveBlockNoPubsub(ctx context.Context, block *ethpb.BeaconB
|
||||
func (s *Service) ReceiveBlockNoPubsubForkchoice(ctx context.Context, block *ethpb.BeaconBlock) error {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon-chain.blockchain.ReceiveBlockNoForkchoice")
|
||||
defer span.End()
|
||||
blockCopy := proto.Clone(block).(*ethpb.BeaconBlock)
|
||||
|
||||
// Apply state transition on the incoming newly received block.
|
||||
if err := s.forkChoiceStore.OnBlock(ctx, block); err != nil {
|
||||
if err := s.forkChoiceStore.OnBlock(ctx, blockCopy); err != nil {
|
||||
err := errors.Wrap(err, "could not process block from fork choice service")
|
||||
traceutil.AnnotateError(span, err)
|
||||
return err
|
||||
}
|
||||
root, err := ssz.SigningRoot(block)
|
||||
root, err := ssz.SigningRoot(blockCopy)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get signing root on received block")
|
||||
}
|
||||
|
||||
if !bytes.Equal(root[:], s.HeadRoot()) {
|
||||
if err := s.saveHead(ctx, block, root); err != nil {
|
||||
if err := s.saveHead(ctx, blockCopy, root); err != nil {
|
||||
return errors.Wrap(err, "could not save head")
|
||||
}
|
||||
}
|
||||
|
||||
// Remove block's contained deposits, attestations, and other operations from persistent storage.
|
||||
if err := s.cleanupBlockOperations(ctx, block); err != nil {
|
||||
if err := s.cleanupBlockOperations(ctx, blockCopy); err != nil {
|
||||
return errors.Wrap(err, "could not clean up block deposits, attestations, and other operations")
|
||||
}
|
||||
|
||||
// Reports on block and fork choice metrics.
|
||||
s.reportSlotMetrics(block.Slot)
|
||||
s.reportSlotMetrics(blockCopy.Slot)
|
||||
|
||||
// Log state transition data.
|
||||
logStateTransitionData(block, root[:])
|
||||
logStateTransitionData(blockCopy, root[:])
|
||||
|
||||
// We write the latest saved head root to a feed for consumption by other services.
|
||||
s.headUpdatedFeed.Send(root)
|
||||
@@ -159,32 +162,33 @@ func (s *Service) ReceiveBlockNoPubsubForkchoice(ctx context.Context, block *eth
|
||||
func (s *Service) ReceiveBlockNoVerify(ctx context.Context, block *ethpb.BeaconBlock) error {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon-chain.blockchain.ReceiveBlockNoVerify")
|
||||
defer span.End()
|
||||
blockCopy := proto.Clone(block).(*ethpb.BeaconBlock)
|
||||
|
||||
// Apply state transition on the incoming newly received block without verifying its BLS contents.
|
||||
if err := s.forkChoiceStore.OnBlockNoVerifyStateTransition(ctx, block); err != nil {
|
||||
return errors.Wrap(err, "could not process block from fork choice service")
|
||||
// Apply state transition on the incoming newly received blockCopy without verifying its BLS contents.
|
||||
if err := s.forkChoiceStore.OnBlockNoVerifyStateTransition(ctx, blockCopy); err != nil {
|
||||
return errors.Wrap(err, "could not process blockCopy from fork choice service")
|
||||
}
|
||||
root, err := ssz.SigningRoot(block)
|
||||
root, err := ssz.SigningRoot(blockCopy)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get signing root on received block")
|
||||
return errors.Wrap(err, "could not get signing root on received blockCopy")
|
||||
}
|
||||
|
||||
if !bytes.Equal(root[:], s.HeadRoot()) {
|
||||
if err := s.saveHead(ctx, block, root); err != nil {
|
||||
if err := s.saveHead(ctx, blockCopy, root); err != nil {
|
||||
err := errors.Wrap(err, "could not save head")
|
||||
traceutil.AnnotateError(span, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Reports on block and fork choice metrics.
|
||||
s.reportSlotMetrics(block.Slot)
|
||||
// Reports on blockCopy and fork choice metrics.
|
||||
s.reportSlotMetrics(blockCopy.Slot)
|
||||
|
||||
// Log state transition data.
|
||||
log.WithFields(logrus.Fields{
|
||||
"slot": block.Slot,
|
||||
"attestations": len(block.Body.Attestations),
|
||||
"deposits": len(block.Body.Deposits),
|
||||
"slot": blockCopy.Slot,
|
||||
"attestations": len(blockCopy.Body.Attestations),
|
||||
"deposits": len(blockCopy.Body.Deposits),
|
||||
}).Debug("Finished applying state transition")
|
||||
|
||||
// We write the latest saved head root to a feed for consumption by other services.
|
||||
|
||||
@@ -24,16 +24,16 @@ func TestReceiveBlock_ProcessCorrectly(t *testing.T) {
|
||||
|
||||
chainService := setupBeaconChain(t, db)
|
||||
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, 0, ðpb.Eth1Data{})
|
||||
beaconState, err := state.GenesisBeaconState(deposits, 0, ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beaconState.Eth1Data.BlockHash = nil
|
||||
beaconState.Eth1DepositIndex = 100
|
||||
stateRoot, err := ssz.HashTreeRoot(beaconState)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
genesis := b.NewGenesisBlock(stateRoot[:])
|
||||
bodyRoot, err := ssz.HashTreeRoot(genesis.Body)
|
||||
if err != nil {
|
||||
@@ -188,7 +188,7 @@ func TestReceiveBlockNoPubsubForkchoice_ProcessCorrectly(t *testing.T) {
|
||||
|
||||
chainService := setupBeaconChain(t, db)
|
||||
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, 0, ðpb.Eth1Data{})
|
||||
beaconState, err := state.GenesisBeaconState(deposits, 0, ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -137,6 +137,8 @@ func (s *Service) Start() {
|
||||
return
|
||||
}()
|
||||
}
|
||||
|
||||
go s.processAttestation()
|
||||
}
|
||||
|
||||
// processChainStartTime initializes a series of deposits from the ChainStart deposits in the eth1
|
||||
|
||||
@@ -54,8 +54,8 @@ func (s *store) OnBlockNoVerifyStateTransition(ctx context.Context, b *ethpb.Bea
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *store) OnAttestation(ctx context.Context, a *ethpb.Attestation) (uint64, error) {
|
||||
return 0, nil
|
||||
func (s *store) OnAttestation(ctx context.Context, a *ethpb.Attestation) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *store) GenesisStore(ctx context.Context, justifiedCheckpoint *ethpb.Checkpoint, finalizedCheckpoint *ethpb.Checkpoint) error {
|
||||
@@ -80,8 +80,16 @@ func (ms *mockOperationService) IncomingAttFeed() *event.Feed {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ms *mockOperationService) IncomingExitFeed() *event.Feed {
|
||||
return nil
|
||||
func (ms *mockOperationService) AttestationPool(ctx context.Context, requestedSlot uint64) ([]*ethpb.Attestation, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (ms *mockOperationService) AttestationPoolNoVerify(ctx context.Context) ([]*ethpb.Attestation, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (ms *mockOperationService) AttestationPoolForForkchoice(ctx context.Context) ([]*ethpb.Attestation, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type mockClient struct{}
|
||||
@@ -199,13 +207,9 @@ func setupBeaconChain(t *testing.T, beaconDB db.Database) *Service {
|
||||
ctx := context.Background()
|
||||
var web3Service *powchain.Service
|
||||
var err error
|
||||
client := &mockClient{}
|
||||
web3Service, err = powchain.NewService(ctx, &powchain.Web3ServiceConfig{
|
||||
Endpoint: endpoint,
|
||||
ETH1Endpoint: endpoint,
|
||||
DepositContract: common.Address{},
|
||||
Reader: client,
|
||||
Client: client,
|
||||
Logger: client,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unable to set up web3 service: %v", err)
|
||||
@@ -354,13 +358,28 @@ func TestChainService_InitializeChainInfo(t *testing.T) {
|
||||
defer testDB.TeardownDB(t, db)
|
||||
ctx := context.Background()
|
||||
|
||||
genesis := b.NewGenesisBlock([]byte{})
|
||||
genesisRoot, err := ssz.SigningRoot(genesis)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.SaveGenesisBlockRoot(ctx, genesisRoot); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.SaveBlock(ctx, genesis); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
finalizedSlot := params.BeaconConfig().SlotsPerEpoch*2 + 1
|
||||
headBlock := ðpb.BeaconBlock{Slot: finalizedSlot}
|
||||
headBlock := ðpb.BeaconBlock{Slot: finalizedSlot, ParentRoot: genesisRoot[:]}
|
||||
headState := &pb.BeaconState{Slot: finalizedSlot}
|
||||
headRoot, _ := ssz.SigningRoot(headBlock)
|
||||
if err := db.SaveState(ctx, headState, headRoot); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.SaveBlock(ctx, headBlock); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.SaveFinalizedCheckpoint(ctx, ðpb.Checkpoint{
|
||||
Epoch: helpers.SlotToEpoch(finalizedSlot),
|
||||
Root: headRoot[:],
|
||||
|
||||
@@ -7,8 +7,12 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = [
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/event:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// ChainService defines the mock interface for testing
|
||||
@@ -18,6 +23,8 @@ type ChainService struct {
|
||||
StateFeed *event.Feed
|
||||
BlocksReceived []*ethpb.BeaconBlock
|
||||
Genesis time.Time
|
||||
Fork *pb.Fork
|
||||
DB db.Database
|
||||
}
|
||||
|
||||
// ReceiveBlock mocks ReceiveBlock method in chain service.
|
||||
@@ -40,8 +47,23 @@ func (ms *ChainService) ReceiveBlockNoPubsubForkchoice(ctx context.Context, bloc
|
||||
if ms.State == nil {
|
||||
ms.State = &pb.BeaconState{}
|
||||
}
|
||||
if !bytes.Equal(ms.Root, block.ParentRoot) {
|
||||
return errors.Errorf("wanted %#x but got %#x", ms.Root, block.ParentRoot)
|
||||
}
|
||||
ms.State.Slot = block.Slot
|
||||
ms.BlocksReceived = append(ms.BlocksReceived, block)
|
||||
signingRoot, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ms.DB != nil {
|
||||
if err := ms.DB.SaveBlock(ctx, block); err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Infof("Saved block with root: %#x at slot %d", signingRoot, block.Slot)
|
||||
}
|
||||
ms.Root = signingRoot[:]
|
||||
ms.Block = block
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -67,6 +89,11 @@ func (ms *ChainService) HeadState() *pb.BeaconState {
|
||||
return ms.State
|
||||
}
|
||||
|
||||
// CurrentFork mocks HeadState method in chain service.
|
||||
func (ms *ChainService) CurrentFork() *pb.Fork {
|
||||
return ms.Fork
|
||||
}
|
||||
|
||||
// FinalizedCheckpt mocks FinalizedCheckpt method in chain service.
|
||||
func (ms *ChainService) FinalizedCheckpt() *ethpb.Checkpoint {
|
||||
return ms.FinalizedCheckPoint
|
||||
|
||||
2
beacon-chain/cache/BUILD.bazel
vendored
2
beacon-chain/cache/BUILD.bazel
vendored
@@ -10,7 +10,6 @@ go_library(
|
||||
"committee.go",
|
||||
"common.go",
|
||||
"eth1_data.go",
|
||||
"shuffled_indices.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/cache",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
@@ -41,7 +40,6 @@ go_test(
|
||||
"committee_test.go",
|
||||
"eth1_data_test.go",
|
||||
"feature_flag_test.go",
|
||||
"shuffled_indices_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
race = "on",
|
||||
|
||||
4
beacon-chain/cache/active_count.go
vendored
4
beacon-chain/cache/active_count.go
vendored
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
@@ -62,6 +63,9 @@ func NewActiveCountCache() *ActiveCountCache {
|
||||
// ActiveCountInEpoch fetches ActiveCountByEpoch by epoch. Returns true with a
|
||||
// reference to the ActiveCountInEpoch info, if exists. Otherwise returns false, nil.
|
||||
func (c *ActiveCountCache) ActiveCountInEpoch(epoch uint64) (uint64, error) {
|
||||
if !featureconfig.Get().EnableActiveCountCache {
|
||||
return params.BeaconConfig().FarFutureEpoch, nil
|
||||
}
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
obj, exists, err := c.activeCountCache.GetByKey(strconv.Itoa(int(epoch)))
|
||||
|
||||
4
beacon-chain/cache/active_indices.go
vendored
4
beacon-chain/cache/active_indices.go
vendored
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
@@ -61,6 +62,9 @@ func NewActiveIndicesCache() *ActiveIndicesCache {
|
||||
// ActiveIndicesInEpoch fetches ActiveIndicesByEpoch by epoch. Returns true with a
|
||||
// reference to the ActiveIndicesInEpoch info, if exists. Otherwise returns false, nil.
|
||||
func (c *ActiveIndicesCache) ActiveIndicesInEpoch(epoch uint64) ([]uint64, error) {
|
||||
if !featureconfig.Get().EnableActiveIndicesCache {
|
||||
return nil, nil
|
||||
}
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
obj, exists, err := c.activeIndicesCache.GetByKey(strconv.Itoa(int(epoch)))
|
||||
|
||||
2
beacon-chain/cache/attestation_data.go
vendored
2
beacon-chain/cache/attestation_data.go
vendored
@@ -181,7 +181,7 @@ func wrapperToKey(i interface{}) (string, error) {
|
||||
}
|
||||
|
||||
func reqToKey(req *pb.AttestationRequest) (string, error) {
|
||||
return fmt.Sprintf("%d-%d", req.Shard, req.Slot), nil
|
||||
return fmt.Sprintf("%d-%d", req.CommitteeIndex, req.Slot), nil
|
||||
}
|
||||
|
||||
type attestationReqResWrapper struct {
|
||||
|
||||
4
beacon-chain/cache/attestation_data_test.go
vendored
4
beacon-chain/cache/attestation_data_test.go
vendored
@@ -15,8 +15,8 @@ func TestAttestationCache_RoundTrip(t *testing.T) {
|
||||
c := cache.NewAttestationCache()
|
||||
|
||||
req := &pb.AttestationRequest{
|
||||
Shard: 0,
|
||||
Slot: 1,
|
||||
CommitteeIndex: 0,
|
||||
Slot: 1,
|
||||
}
|
||||
|
||||
response, err := c.Get(ctx, req)
|
||||
|
||||
81
beacon-chain/cache/committee.go
vendored
81
beacon-chain/cache/committee.go
vendored
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/sliceutil"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
@@ -33,15 +34,14 @@ var (
|
||||
})
|
||||
)
|
||||
|
||||
// Committee defines the committee per epoch and shard.
|
||||
// Committee defines the committee per epoch and index.
|
||||
type Committee struct {
|
||||
StartShard uint64
|
||||
CommitteeCount uint64
|
||||
Epoch uint64
|
||||
Committee []uint64
|
||||
}
|
||||
|
||||
// CommitteeCache is a struct with 1 queue for looking up shuffled indices list by epoch and shard.
|
||||
// CommitteeCache is a struct with 1 queue for looking up shuffled indices list by epoch and committee index.
|
||||
type CommitteeCache struct {
|
||||
CommitteeCache *cache.FIFO
|
||||
lock sync.RWMutex
|
||||
@@ -64,12 +64,17 @@ func NewCommitteeCache() *CommitteeCache {
|
||||
}
|
||||
}
|
||||
|
||||
// ShuffledIndices fetches the shuffled indices by epoch and shard. Every list of indices
|
||||
// represent one committee. Returns true if the list exists with epoch and shard. Otherwise returns false, nil.
|
||||
func (c *CommitteeCache) ShuffledIndices(epoch uint64, shard uint64) ([]uint64, error) {
|
||||
// ShuffledIndices fetches the shuffled indices by slot and committee index. Every list of indices
|
||||
// represent one committee. Returns true if the list exists with slot and committee index. Otherwise returns false, nil.
|
||||
func (c *CommitteeCache) ShuffledIndices(slot uint64, index uint64) ([]uint64, error) {
|
||||
if !featureconfig.Get().EnableShuffledIndexCache && !featureconfig.Get().EnableNewCache {
|
||||
return nil, nil
|
||||
}
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
obj, exists, err := c.CommitteeCache.GetByKey(strconv.Itoa(int(epoch)))
|
||||
|
||||
epoch := int(slot / params.BeaconConfig().SlotsPerEpoch)
|
||||
obj, exists, err := c.CommitteeCache.GetByKey(strconv.Itoa(epoch))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -86,14 +91,22 @@ func (c *CommitteeCache) ShuffledIndices(epoch uint64, shard uint64) ([]uint64,
|
||||
return nil, ErrNotCommittee
|
||||
}
|
||||
|
||||
start, end := startEndIndices(item, shard)
|
||||
committeeCountPerSlot := uint64(1)
|
||||
if item.CommitteeCount/params.BeaconConfig().SlotsPerEpoch > 1 {
|
||||
committeeCountPerSlot = item.CommitteeCount / params.BeaconConfig().SlotsPerEpoch
|
||||
}
|
||||
|
||||
indexOffSet := index + (slot%params.BeaconConfig().SlotsPerEpoch)*committeeCountPerSlot
|
||||
start, end := startEndIndices(item, indexOffSet)
|
||||
return item.Committee[start:end], nil
|
||||
}
|
||||
|
||||
// AddCommitteeShuffledList adds Committee shuffled list object to the cache. T
|
||||
// his method also trims the least recently list if the cache size has ready the max cache size limit.
|
||||
func (c *CommitteeCache) AddCommitteeShuffledList(committee *Committee) error {
|
||||
if !featureconfig.Get().EnableShuffledIndexCache && !featureconfig.Get().EnableNewCache {
|
||||
return nil
|
||||
}
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
if err := c.CommitteeCache.AddIfNotPresent(committee); err != nil {
|
||||
@@ -105,6 +118,9 @@ func (c *CommitteeCache) AddCommitteeShuffledList(committee *Committee) error {
|
||||
|
||||
// Epochs returns the epochs stored in the committee cache. These are the keys to the cache.
|
||||
func (c *CommitteeCache) Epochs() ([]uint64, error) {
|
||||
if !featureconfig.Get().EnableShuffledIndexCache {
|
||||
return nil, nil
|
||||
}
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
|
||||
@@ -121,6 +137,9 @@ func (c *CommitteeCache) Epochs() ([]uint64, error) {
|
||||
|
||||
// EpochInCache returns true if an input epoch is part of keys in cache.
|
||||
func (c *CommitteeCache) EpochInCache(wantedEpoch uint64) (bool, error) {
|
||||
if !featureconfig.Get().EnableShuffledIndexCache && !featureconfig.Get().EnableNewCache {
|
||||
return false, nil
|
||||
}
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
|
||||
@@ -136,10 +155,14 @@ func (c *CommitteeCache) EpochInCache(wantedEpoch uint64) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// CommitteeCount returns the total number of committees in a given epoch as stored in cache.
|
||||
func (c *CommitteeCache) CommitteeCount(epoch uint64) (uint64, bool, error) {
|
||||
// CommitteeCountPerSlot returns the number of committees in a given slot as stored in cache.
|
||||
func (c *CommitteeCache) CommitteeCountPerSlot(slot uint64) (uint64, bool, error) {
|
||||
if !featureconfig.Get().EnableShuffledIndexCache && !featureconfig.Get().EnableNewCache {
|
||||
return 0, false, nil
|
||||
}
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
epoch := int(slot / params.BeaconConfig().SlotsPerEpoch)
|
||||
obj, exists, err := c.CommitteeCache.GetByKey(strconv.Itoa(int(epoch)))
|
||||
if err != nil {
|
||||
return 0, false, err
|
||||
@@ -157,35 +180,15 @@ func (c *CommitteeCache) CommitteeCount(epoch uint64) (uint64, bool, error) {
|
||||
return 0, false, ErrNotCommittee
|
||||
}
|
||||
|
||||
return item.CommitteeCount, true, nil
|
||||
}
|
||||
|
||||
// StartShard returns the start shard number in a given epoch as stored in cache.
|
||||
func (c *CommitteeCache) StartShard(epoch uint64) (uint64, bool, error) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
obj, exists, err := c.CommitteeCache.GetByKey(strconv.Itoa(int(epoch)))
|
||||
if err != nil {
|
||||
return 0, false, err
|
||||
}
|
||||
|
||||
if exists {
|
||||
CommitteeCacheHit.Inc()
|
||||
} else {
|
||||
CommitteeCacheMiss.Inc()
|
||||
return 0, false, nil
|
||||
}
|
||||
|
||||
item, ok := obj.(*Committee)
|
||||
if !ok {
|
||||
return 0, false, ErrNotCommittee
|
||||
}
|
||||
|
||||
return item.StartShard, true, nil
|
||||
return item.CommitteeCount / params.BeaconConfig().SlotsPerEpoch, true, nil
|
||||
}
|
||||
|
||||
// ActiveIndices returns the active indices of a given epoch stored in cache.
|
||||
func (c *CommitteeCache) ActiveIndices(epoch uint64) ([]uint64, error) {
|
||||
if !featureconfig.Get().EnableShuffledIndexCache && !featureconfig.Get().EnableNewCache {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
obj, exists, err := c.CommitteeCache.GetByKey(strconv.Itoa(int(epoch)))
|
||||
@@ -208,12 +211,10 @@ func (c *CommitteeCache) ActiveIndices(epoch uint64) ([]uint64, error) {
|
||||
return item.Committee, nil
|
||||
}
|
||||
|
||||
func startEndIndices(c *Committee, wantedShard uint64) (uint64, uint64) {
|
||||
shardCount := params.BeaconConfig().ShardCount
|
||||
currentShard := (wantedShard + shardCount - c.StartShard) % shardCount
|
||||
func startEndIndices(c *Committee, index uint64) (uint64, uint64) {
|
||||
validatorCount := uint64(len(c.Committee))
|
||||
start := sliceutil.SplitOffset(validatorCount, c.CommitteeCount, currentShard)
|
||||
end := sliceutil.SplitOffset(validatorCount, c.CommitteeCount, currentShard+1)
|
||||
start := sliceutil.SplitOffset(validatorCount, c.CommitteeCount, index)
|
||||
end := sliceutil.SplitOffset(validatorCount, c.CommitteeCount, index+1)
|
||||
|
||||
return start, end
|
||||
}
|
||||
|
||||
78
beacon-chain/cache/committee_test.go
vendored
78
beacon-chain/cache/committee_test.go
vendored
@@ -4,6 +4,8 @@ import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
func TestCommitteeKeyFn_OK(t *testing.T) {
|
||||
@@ -36,12 +38,11 @@ func TestCommitteeCache_CommitteesByEpoch(t *testing.T) {
|
||||
Epoch: 1,
|
||||
Committee: []uint64{1, 2, 3, 4, 5, 6},
|
||||
CommitteeCount: 3,
|
||||
StartShard: 1,
|
||||
}
|
||||
|
||||
epoch := uint64(1)
|
||||
startShard := uint64(1)
|
||||
indices, err := cache.ShuffledIndices(epoch, startShard)
|
||||
slot := uint64(item.Epoch * params.BeaconConfig().SlotsPerEpoch)
|
||||
committeeIndex := uint64(1)
|
||||
indices, err := cache.ShuffledIndices(slot, committeeIndex)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -52,12 +53,13 @@ func TestCommitteeCache_CommitteesByEpoch(t *testing.T) {
|
||||
if err := cache.AddCommitteeShuffledList(item); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wantedShard := uint64(2)
|
||||
indices, err = cache.ShuffledIndices(epoch, wantedShard)
|
||||
wantedIndex := uint64(0)
|
||||
indices, err = cache.ShuffledIndices(slot, wantedIndex)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
start, end := startEndIndices(item, wantedShard)
|
||||
|
||||
start, end := startEndIndices(item, wantedIndex)
|
||||
if !reflect.DeepEqual(indices, item.Committee[start:end]) {
|
||||
t.Errorf(
|
||||
"Expected fetched active indices to be %v, got %v",
|
||||
@@ -143,68 +145,6 @@ func TestCommitteeCache_EpochInCache(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommitteeCache_CommitteesCount(t *testing.T) {
|
||||
cache := NewCommitteeCache()
|
||||
|
||||
committeeCount := uint64(3)
|
||||
epoch := uint64(10)
|
||||
item := &Committee{Epoch: epoch, CommitteeCount: committeeCount}
|
||||
|
||||
_, exists, err := cache.CommitteeCount(1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if exists {
|
||||
t.Error("Expected committee count not to exist in empty cache")
|
||||
}
|
||||
|
||||
if err := cache.AddCommitteeShuffledList(item); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
count, exists, err := cache.CommitteeCount(epoch)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !exists {
|
||||
t.Error("Expected committee count to be in cache")
|
||||
}
|
||||
if count != committeeCount {
|
||||
t.Errorf("wanted: %d, got: %d", committeeCount, count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommitteeCache_ShardCount(t *testing.T) {
|
||||
cache := NewCommitteeCache()
|
||||
|
||||
startShard := uint64(7)
|
||||
epoch := uint64(3)
|
||||
item := &Committee{Epoch: epoch, StartShard: startShard}
|
||||
|
||||
_, exists, err := cache.StartShard(1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if exists {
|
||||
t.Error("Expected start shard not to exist in empty cache")
|
||||
}
|
||||
|
||||
if err := cache.AddCommitteeShuffledList(item); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
shard, exists, err := cache.StartShard(epoch)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !exists {
|
||||
t.Error("Expected start shard to be in cache")
|
||||
}
|
||||
if shard != startShard {
|
||||
t.Errorf("wanted: %d, got: %d", startShard, shard)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommitteeCache_ActiveIndices(t *testing.T) {
|
||||
cache := NewCommitteeCache()
|
||||
|
||||
|
||||
8
beacon-chain/cache/feature_flag_test.go
vendored
8
beacon-chain/cache/feature_flag_test.go
vendored
@@ -4,7 +4,11 @@ import "github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
|
||||
func init() {
|
||||
featureconfig.Init(&featureconfig.Flag{
|
||||
EnableAttestationCache: true,
|
||||
EnableEth1DataVoteCache: true,
|
||||
EnableAttestationCache: true,
|
||||
EnableEth1DataVoteCache: true,
|
||||
EnableShuffledIndexCache: true,
|
||||
EnableCommitteeCache: true,
|
||||
EnableActiveCountCache: true,
|
||||
EnableActiveIndicesCache: true,
|
||||
})
|
||||
}
|
||||
|
||||
99
beacon-chain/cache/shuffled_indices.go
vendored
99
beacon-chain/cache/shuffled_indices.go
vendored
@@ -1,99 +0,0 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotValidatorListInfo will be returned when a cache object is not a pointer to
|
||||
// a ValidatorList struct.
|
||||
ErrNotValidatorListInfo = errors.New("object is not a shuffled validator list")
|
||||
|
||||
// maxShuffledListSize defines the max number of shuffled list can cache.
|
||||
maxShuffledListSize = 1000
|
||||
|
||||
// Metrics.
|
||||
shuffledIndicesCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "shuffled_validators_cache_miss",
|
||||
Help: "The number of shuffled validators requests that aren't present in the cache.",
|
||||
})
|
||||
shuffledIndicesCacheHit = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "shuffled_validators_cache_hit",
|
||||
Help: "The number of shuffled validators requests that are present in the cache.",
|
||||
})
|
||||
)
|
||||
|
||||
// IndicesByIndexSeed defines the shuffled validator indices per randao seed.
|
||||
type IndicesByIndexSeed struct {
|
||||
Index uint64
|
||||
Seed []byte
|
||||
ShuffledIndices []uint64
|
||||
}
|
||||
|
||||
// ShuffledIndicesCache is a struct with 1 queue for looking up shuffled validators by seed.
|
||||
type ShuffledIndicesCache struct {
|
||||
shuffledIndicesCache *cache.FIFO
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// slotKeyFn takes the randao seed as the key for the shuffled validators of a given epoch.
|
||||
func shuffleKeyFn(obj interface{}) (string, error) {
|
||||
sInfo, ok := obj.(*IndicesByIndexSeed)
|
||||
if !ok {
|
||||
return "", ErrNotValidatorListInfo
|
||||
}
|
||||
|
||||
return string(sInfo.Seed) + strconv.Itoa(int(sInfo.Index)), nil
|
||||
}
|
||||
|
||||
// NewShuffledIndicesCache creates a new shuffled validators cache for storing/accessing shuffled validator indices
|
||||
func NewShuffledIndicesCache() *ShuffledIndicesCache {
|
||||
return &ShuffledIndicesCache{
|
||||
shuffledIndicesCache: cache.NewFIFO(shuffleKeyFn),
|
||||
}
|
||||
}
|
||||
|
||||
// IndicesByIndexSeed fetches IndicesByIndexSeed by epoch and seed. Returns true with a
|
||||
// reference to the ShuffledIndicesInEpoch info, if exists. Otherwise returns false, nil.
|
||||
func (c *ShuffledIndicesCache) IndicesByIndexSeed(index uint64, seed []byte) ([]uint64, error) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
key := string(seed) + strconv.Itoa(int(index))
|
||||
obj, exists, err := c.shuffledIndicesCache.GetByKey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if exists {
|
||||
shuffledIndicesCacheHit.Inc()
|
||||
} else {
|
||||
shuffledIndicesCacheMiss.Inc()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
cInfo, ok := obj.(*IndicesByIndexSeed)
|
||||
if !ok {
|
||||
return nil, ErrNotValidatorListInfo
|
||||
}
|
||||
|
||||
return cInfo.ShuffledIndices, nil
|
||||
}
|
||||
|
||||
// AddShuffledValidatorList adds IndicesByIndexSeed object to the cache. This method also trims the least
|
||||
// recently added IndicesByIndexSeed object if the cache size has ready the max cache size limit.
|
||||
func (c *ShuffledIndicesCache) AddShuffledValidatorList(shuffledIndices *IndicesByIndexSeed) error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
if err := c.shuffledIndicesCache.AddIfNotPresent(shuffledIndices); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
trim(c.shuffledIndicesCache, maxShuffledListSize)
|
||||
return nil
|
||||
}
|
||||
85
beacon-chain/cache/shuffled_indices_test.go
vendored
85
beacon-chain/cache/shuffled_indices_test.go
vendored
@@ -1,85 +0,0 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestShuffleKeyFn_OK(t *testing.T) {
|
||||
sInfo := &IndicesByIndexSeed{
|
||||
Index: 999,
|
||||
Seed: []byte{'A'},
|
||||
ShuffledIndices: []uint64{1, 2, 3, 4, 5},
|
||||
}
|
||||
|
||||
key, err := shuffleKeyFn(sInfo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if key != string(sInfo.Seed)+strconv.Itoa(int(sInfo.Index)) {
|
||||
t.Errorf("Incorrect hash key: %s, expected %s", key, string(sInfo.Seed)+strconv.Itoa(int(sInfo.Index)))
|
||||
}
|
||||
}
|
||||
|
||||
func TestShuffleKeyFn_InvalidObj(t *testing.T) {
|
||||
_, err := shuffleKeyFn("bad")
|
||||
if err != ErrNotValidatorListInfo {
|
||||
t.Errorf("Expected error %v, got %v", ErrNotValidatorListInfo, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShuffledIndicesCache_ShuffledIndicesBySeed2(t *testing.T) {
|
||||
cache := NewShuffledIndicesCache()
|
||||
|
||||
sInfo := &IndicesByIndexSeed{
|
||||
Index: 99,
|
||||
Seed: []byte{'A'},
|
||||
ShuffledIndices: []uint64{1, 2, 3, 4},
|
||||
}
|
||||
|
||||
shuffledIndices, err := cache.IndicesByIndexSeed(sInfo.Index, sInfo.Seed)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if shuffledIndices != nil {
|
||||
t.Error("Expected shuffled indices not to exist in empty cache")
|
||||
}
|
||||
|
||||
if err := cache.AddShuffledValidatorList(sInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
shuffledIndices, err = cache.IndicesByIndexSeed(sInfo.Index, sInfo.Seed)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(shuffledIndices, sInfo.ShuffledIndices) {
|
||||
t.Errorf(
|
||||
"Expected fetched info committee to be %v, got %v",
|
||||
sInfo.ShuffledIndices,
|
||||
shuffledIndices,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShuffledIndices_MaxSize(t *testing.T) {
|
||||
cache := NewShuffledIndicesCache()
|
||||
|
||||
for i := uint64(0); i < 1001; i++ {
|
||||
sInfo := &IndicesByIndexSeed{
|
||||
Index: i,
|
||||
Seed: []byte{byte(i)},
|
||||
}
|
||||
if err := cache.AddShuffledValidatorList(sInfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(cache.shuffledIndicesCache.ListKeys()) != maxShuffledListSize {
|
||||
t.Errorf(
|
||||
"Expected hash cache key size to be %d, got %d",
|
||||
maxShuffledListSize,
|
||||
len(cache.shuffledIndicesCache.ListKeys()),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,6 @@ go_test(
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/bls:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"//shared/trieutil:go_default_library",
|
||||
|
||||
@@ -161,7 +161,7 @@ func ProcessBlockHeader(
|
||||
) (*pb.BeaconState, error) {
|
||||
beaconState, err := ProcessBlockHeaderNoVerify(beaconState, block)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process block header")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
idx, err := helpers.BeaconProposerIndex(beaconState)
|
||||
@@ -320,8 +320,8 @@ func ProcessRandaoNoVerify(
|
||||
// Process ``ProposerSlashing`` operation.
|
||||
// """
|
||||
// proposer = state.validator_registry[proposer_slashing.proposer_index]
|
||||
// # Verify that the epoch is the same
|
||||
// assert slot_to_epoch(proposer_slashing.header_1.slot) == slot_to_epoch(proposer_slashing.header_2.slot)
|
||||
// # Verify slots match
|
||||
// assert proposer_slashing.header_1.slot == proposer_slashing.header_2.slot
|
||||
// # But the headers are different
|
||||
// assert proposer_slashing.header_1 != proposer_slashing.header_2
|
||||
// # Check proposer is slashable
|
||||
@@ -356,12 +356,10 @@ func VerifyProposerSlashing(
|
||||
beaconState *pb.BeaconState,
|
||||
slashing *ethpb.ProposerSlashing,
|
||||
) error {
|
||||
headerEpoch1 := helpers.SlotToEpoch(slashing.Header_1.Slot)
|
||||
headerEpoch2 := helpers.SlotToEpoch(slashing.Header_2.Slot)
|
||||
proposer := beaconState.Validators[slashing.ProposerIndex]
|
||||
|
||||
if headerEpoch1 != headerEpoch2 {
|
||||
return fmt.Errorf("mismatched header epochs, received %d == %d", headerEpoch1, headerEpoch2)
|
||||
if slashing.Header_1.Slot != slashing.Header_2.Slot {
|
||||
return fmt.Errorf("mismatched header slots, received %d == %d", slashing.Header_1.Slot, slashing.Header_2.Slot)
|
||||
}
|
||||
if proto.Equal(slashing.Header_1, slashing.Header_2) {
|
||||
return errors.New("expected slashing headers to differ")
|
||||
@@ -370,7 +368,7 @@ func VerifyProposerSlashing(
|
||||
return fmt.Errorf("validator with key %#x is not slashable", proposer.PublicKey)
|
||||
}
|
||||
// Using headerEpoch1 here because both of the headers should have the same epoch.
|
||||
domain := helpers.Domain(beaconState.Fork, headerEpoch1, params.BeaconConfig().DomainBeaconProposer)
|
||||
domain := helpers.Domain(beaconState.Fork, helpers.StartSlot(slashing.Header_1.Slot), params.BeaconConfig().DomainBeaconProposer)
|
||||
headers := append([]*ethpb.BeaconBlockHeader{slashing.Header_1}, slashing.Header_2)
|
||||
for _, header := range headers {
|
||||
if err := verifySigningRoot(header, proposer.PublicKey, header.Signature, domain); err != nil {
|
||||
@@ -508,40 +506,27 @@ func ProcessAttestationsNoVerify(ctx context.Context, beaconState *pb.BeaconStat
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def process_attestation(state: BeaconState, attestation: Attestation) -> None:
|
||||
// """
|
||||
// Process ``Attestation`` operation.
|
||||
// """
|
||||
// data = attestation.data
|
||||
// assert data.crosslink.shard < SHARD_COUNT
|
||||
// assert data.index < get_committee_count_at_slot(state, data.slot)
|
||||
// assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state))
|
||||
// assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= data.slot + SLOTS_PER_EPOCH
|
||||
//
|
||||
// attestation_slot = get_attestation_data_slot(state, data)
|
||||
// assert attestation_slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= attestation_slot + SLOTS_PER_EPOCH
|
||||
//
|
||||
// committee = get_crosslink_committee(state, data.target.epoch, data.crosslink.shard)
|
||||
// committee = get_beacon_committee(state, data.slot, data.index)
|
||||
// assert len(attestation.aggregation_bits) == len(attestation.custody_bits) == len(committee)
|
||||
//
|
||||
// pending_attestation = PendingAttestation(
|
||||
// data=data,
|
||||
// aggregation_bitfield=attestation.aggregation_bitfield,
|
||||
// inclusion_delay=state.slot - attestation_slot,
|
||||
// aggregation_bits=attestation.aggregation_bits,
|
||||
// inclusion_delay=state.slot - data.slot,
|
||||
// proposer_index=get_beacon_proposer_index(state),
|
||||
// )
|
||||
//
|
||||
// if data.target_epoch == get_current_epoch(state):
|
||||
// assert data.source == state.current_justified_checkpoint
|
||||
// parent_crosslink = state.current_crosslinks[data.crosslink.shard]
|
||||
// state.current_epoch_attestations.append(pending_attestation)
|
||||
// if data.target.epoch == get_current_epoch(state):
|
||||
// assert data.source == state.current_justified_checkpoint
|
||||
// state.current_epoch_attestations.append(pending_attestation)
|
||||
// else:
|
||||
// assert data.source == state.previous_justified_checkpoint
|
||||
// parent_crosslink = state.previous_crosslinks[data.crosslink.shard]
|
||||
// state.previous_epoch_attestations.append(pending_attestation)
|
||||
//
|
||||
// # Check crosslink against expected parent crosslink
|
||||
// assert data.crosslink.parent_root == hash_tree_root(parent_crosslink)
|
||||
// assert data.crosslink.start_epoch == parent_crosslink.end_epoch
|
||||
// assert data.crosslink.end_epoch == min(data.target.epoch, parent_crosslink.end_epoch + MAX_EPOCHS_PER_CROSSLINK)
|
||||
// assert data.crosslink.data_root == Bytes32() # [to be removed in phase 1]
|
||||
// assert data.source == state.previous_justified_checkpoint
|
||||
// state.previous_epoch_attestations.append(pending_attestation)
|
||||
//
|
||||
// # Check signature
|
||||
// assert is_valid_indexed_attestation(state, get_indexed_attestation(state, attestation))
|
||||
@@ -560,15 +545,6 @@ func ProcessAttestationNoVerify(ctx context.Context, beaconState *pb.BeaconState
|
||||
defer span.End()
|
||||
|
||||
data := att.Data
|
||||
|
||||
if data.Crosslink.Shard > params.BeaconConfig().ShardCount {
|
||||
return nil, fmt.Errorf(
|
||||
"expected crosslink shard %d to be less than SHARD_COUNT %d",
|
||||
data.Crosslink.Shard,
|
||||
params.BeaconConfig().ShardCount,
|
||||
)
|
||||
}
|
||||
|
||||
if data.Target.Epoch != helpers.PrevEpoch(beaconState) && data.Target.Epoch != helpers.CurrentEpoch(beaconState) {
|
||||
return nil, fmt.Errorf(
|
||||
"expected target epoch (%d) to be the previous epoch (%d) or the current epoch (%d)",
|
||||
@@ -578,16 +554,13 @@ func ProcessAttestationNoVerify(ctx context.Context, beaconState *pb.BeaconState
|
||||
)
|
||||
}
|
||||
|
||||
attestationSlot, err := helpers.AttestationDataSlot(beaconState, data)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get attestation slot")
|
||||
}
|
||||
minInclusionCheck := attestationSlot+params.BeaconConfig().MinAttestationInclusionDelay <= beaconState.Slot
|
||||
epochInclusionCheck := beaconState.Slot <= attestationSlot+params.BeaconConfig().SlotsPerEpoch
|
||||
s := att.Data.Slot
|
||||
minInclusionCheck := s+params.BeaconConfig().MinAttestationInclusionDelay <= beaconState.Slot
|
||||
epochInclusionCheck := beaconState.Slot <= s+params.BeaconConfig().SlotsPerEpoch
|
||||
if !minInclusionCheck {
|
||||
return nil, fmt.Errorf(
|
||||
"attestation slot %d + inclusion delay %d > state slot %d",
|
||||
attestationSlot,
|
||||
s,
|
||||
params.BeaconConfig().MinAttestationInclusionDelay,
|
||||
beaconState.Slot,
|
||||
)
|
||||
@@ -596,7 +569,7 @@ func ProcessAttestationNoVerify(ctx context.Context, beaconState *pb.BeaconState
|
||||
return nil, fmt.Errorf(
|
||||
"state slot %d > attestation slot %d + SLOTS_PER_EPOCH %d",
|
||||
beaconState.Slot,
|
||||
attestationSlot,
|
||||
s,
|
||||
params.BeaconConfig().SlotsPerEpoch,
|
||||
)
|
||||
}
|
||||
@@ -612,34 +585,22 @@ func ProcessAttestationNoVerify(ctx context.Context, beaconState *pb.BeaconState
|
||||
pendingAtt := &pb.PendingAttestation{
|
||||
Data: data,
|
||||
AggregationBits: att.AggregationBits,
|
||||
InclusionDelay: beaconState.Slot - attestationSlot,
|
||||
InclusionDelay: beaconState.Slot - s,
|
||||
ProposerIndex: proposerIndex,
|
||||
}
|
||||
|
||||
var ffgSourceEpoch uint64
|
||||
var ffgSourceRoot []byte
|
||||
var ffgTargetEpoch uint64
|
||||
var parentCrosslink *ethpb.Crosslink
|
||||
if data.Target.Epoch == helpers.CurrentEpoch(beaconState) {
|
||||
ffgSourceEpoch = beaconState.CurrentJustifiedCheckpoint.Epoch
|
||||
ffgSourceRoot = beaconState.CurrentJustifiedCheckpoint.Root
|
||||
ffgTargetEpoch = helpers.CurrentEpoch(beaconState)
|
||||
crosslinkShard := data.Crosslink.Shard
|
||||
if int(crosslinkShard) >= len(beaconState.CurrentCrosslinks) {
|
||||
return nil, fmt.Errorf("invalid shard given in attestation: %d", crosslinkShard)
|
||||
}
|
||||
|
||||
parentCrosslink = beaconState.CurrentCrosslinks[crosslinkShard]
|
||||
beaconState.CurrentEpochAttestations = append(beaconState.CurrentEpochAttestations, pendingAtt)
|
||||
} else {
|
||||
ffgSourceEpoch = beaconState.PreviousJustifiedCheckpoint.Epoch
|
||||
ffgSourceRoot = beaconState.PreviousJustifiedCheckpoint.Root
|
||||
ffgTargetEpoch = helpers.PrevEpoch(beaconState)
|
||||
crosslinkShard := data.Crosslink.Shard
|
||||
if int(crosslinkShard) >= len(beaconState.PreviousCrosslinks) {
|
||||
return nil, fmt.Errorf("invalid shard given in attestation: %d", crosslinkShard)
|
||||
}
|
||||
parentCrosslink = beaconState.PreviousCrosslinks[crosslinkShard]
|
||||
beaconState.PreviousEpochAttestations = append(beaconState.PreviousEpochAttestations, pendingAtt)
|
||||
}
|
||||
if data.Source.Epoch != ffgSourceEpoch {
|
||||
@@ -651,34 +612,7 @@ func ProcessAttestationNoVerify(ctx context.Context, beaconState *pb.BeaconState
|
||||
if data.Target.Epoch != ffgTargetEpoch {
|
||||
return nil, fmt.Errorf("expected target epoch %d, received %d", ffgTargetEpoch, data.Target.Epoch)
|
||||
}
|
||||
endEpoch := parentCrosslink.EndEpoch + params.BeaconConfig().MaxEpochsPerCrosslink
|
||||
if data.Target.Epoch < endEpoch {
|
||||
endEpoch = data.Target.Epoch
|
||||
}
|
||||
if data.Crosslink.StartEpoch != parentCrosslink.EndEpoch {
|
||||
return nil, fmt.Errorf("expected crosslink start epoch %d, received %d",
|
||||
parentCrosslink.EndEpoch, data.Crosslink.StartEpoch)
|
||||
}
|
||||
if data.Crosslink.EndEpoch != endEpoch {
|
||||
return nil, fmt.Errorf("expected crosslink end epoch %d, received %d",
|
||||
endEpoch, data.Crosslink.EndEpoch)
|
||||
}
|
||||
crosslinkParentRoot, err := ssz.HashTreeRoot(parentCrosslink)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not tree hash parent crosslink")
|
||||
}
|
||||
if !bytes.Equal(data.Crosslink.ParentRoot, crosslinkParentRoot[:]) {
|
||||
return nil, fmt.Errorf(
|
||||
"mismatched parent crosslink root, expected %#x, received %#x",
|
||||
crosslinkParentRoot,
|
||||
data.Crosslink.ParentRoot,
|
||||
)
|
||||
}
|
||||
|
||||
// To be removed in Phase 1
|
||||
if !bytes.Equal(data.Crosslink.DataRoot, params.BeaconConfig().ZeroHash[:]) {
|
||||
return nil, fmt.Errorf("expected data root %#x == ZERO_HASH", data.Crosslink.DataRoot)
|
||||
}
|
||||
return beaconState, nil
|
||||
}
|
||||
|
||||
@@ -708,6 +642,7 @@ func ConvertToIndexed(ctx context.Context, state *pb.BeaconState, attestation *e
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get attesting indices")
|
||||
}
|
||||
|
||||
cb1i, err := helpers.AttestingIndices(state, attestation.Data, attestation.CustodyBits)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -816,7 +751,7 @@ func VerifyIndexedAttestation(ctx context.Context, beaconState *pb.BeaconState,
|
||||
return fmt.Errorf("custody Bit1 indices are not sorted, got %v", custodyBit1Indices)
|
||||
}
|
||||
|
||||
domain := helpers.Domain(beaconState.Fork, indexedAtt.Data.Target.Epoch, params.BeaconConfig().DomainAttestation)
|
||||
domain := helpers.Domain(beaconState.Fork, indexedAtt.Data.Target.Epoch, params.BeaconConfig().DomainBeaconAttester)
|
||||
var pubkeys []*bls.PublicKey
|
||||
if len(custodyBit0Indices) > 0 {
|
||||
pubkey, err := bls.PublicKeyFromBytes(beaconState.Validators[custodyBit0Indices[0]].PublicKey)
|
||||
@@ -913,47 +848,45 @@ func ProcessDeposits(ctx context.Context, beaconState *pb.BeaconState, body *eth
|
||||
// into the registry as a new validator or balance change.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def process_deposit(state: BeaconState, deposit: Deposit) -> None:
|
||||
// """
|
||||
// Process an Eth1 deposit, registering a validator or increasing its balance.
|
||||
// """
|
||||
// # Verify the Merkle branch
|
||||
// assert verify_merkle_branch(
|
||||
// leaf=hash_tree_root(deposit.data),
|
||||
// proof=deposit.proof,
|
||||
// depth=DEPOSIT_CONTRACT_TREE_DEPTH,
|
||||
// index=deposit.index,
|
||||
// root=state.latest_eth1_data.deposit_root,
|
||||
// )
|
||||
// def process_deposit(state: BeaconState, deposit: Deposit) -> None:
|
||||
// # Verify the Merkle branch
|
||||
// assert is_valid_merkle_branch(
|
||||
// leaf=hash_tree_root(deposit.data),
|
||||
// branch=deposit.proof,
|
||||
// depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the `List` length mix-in
|
||||
// index=state.eth1_deposit_index,
|
||||
// root=state.eth1_data.deposit_root,
|
||||
// )
|
||||
//
|
||||
// # Deposits must be processed in order
|
||||
// assert deposit.index == state.deposit_index
|
||||
// state.deposit_index += 1
|
||||
// # Deposits must be processed in order
|
||||
// state.eth1_deposit_index += 1
|
||||
//
|
||||
// pubkey = deposit.data.pubkey
|
||||
// amount = deposit.data.amount
|
||||
// validator_pubkeys = [v.pubkey for v in state.validator_registry]
|
||||
// if pubkey not in validator_pubkeys:
|
||||
// # Verify the deposit signature (proof of possession).
|
||||
// # Invalid signatures are allowed by the deposit contract, and hence included on-chain, but must not be processed.
|
||||
// if not bls_verify(pubkey, signing_root(deposit.data), deposit.data.signature%d, get_domain(state, DOMAIN_DEPOSIT)):
|
||||
// return
|
||||
// pubkey = deposit.data.pubkey
|
||||
// amount = deposit.data.amount
|
||||
// validator_pubkeys = [v.pubkey for v in state.validators]
|
||||
// if pubkey not in validator_pubkeys:
|
||||
// # Verify the deposit signature (proof of possession) for new validators.
|
||||
// # Note: The deposit contract does not check signatures.
|
||||
// # Note: Deposits are valid across forks, thus the deposit domain is retrieved directly from `compute_domain`.
|
||||
// domain = compute_domain(DOMAIN_DEPOSIT)
|
||||
// if not bls_verify(pubkey, signing_root(deposit.data), deposit.data.signature, domain):
|
||||
// return
|
||||
//
|
||||
// # Add validator and balance entries
|
||||
// state.validator_registry.append(Validator(
|
||||
// pubkey=pubkey,
|
||||
// withdrawal_credentials=deposit.data.withdrawal_credentials,
|
||||
// activation_eligibility_epoch=FAR_FUTURE_EPOCH,
|
||||
// activation_epoch=FAR_FUTURE_EPOCH,
|
||||
// exit_epoch=FAR_FUTURE_EPOCH,
|
||||
// withdrawable_epoch=FAR_FUTURE_EPOCH,
|
||||
// effective_balance=min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE)
|
||||
// ))
|
||||
// state.balances.append(amount)
|
||||
// else:
|
||||
// # Increase balance by deposit amount
|
||||
// index = validator_pubkeys.index(pubkey)
|
||||
// increase_balance(state, index, amount)
|
||||
// # Add validator and balance entries
|
||||
// state.validators.append(Validator(
|
||||
// pubkey=pubkey,
|
||||
// withdrawal_credentials=deposit.data.withdrawal_credentials,
|
||||
// activation_eligibility_epoch=FAR_FUTURE_EPOCH,
|
||||
// activation_epoch=FAR_FUTURE_EPOCH,
|
||||
// exit_epoch=FAR_FUTURE_EPOCH,
|
||||
// withdrawable_epoch=FAR_FUTURE_EPOCH,
|
||||
// effective_balance=min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE),
|
||||
// ))
|
||||
// state.balances.append(amount)
|
||||
// else:
|
||||
// # Increase balance by deposit amount
|
||||
// index = ValidatorIndex(validator_pubkeys.index(pubkey))
|
||||
// increase_balance(state, index, amount)
|
||||
func ProcessDeposit(beaconState *pb.BeaconState, deposit *ethpb.Deposit, valIndexMap map[[48]byte]int) (*pb.BeaconState, error) {
|
||||
if err := verifyDeposit(beaconState, deposit); err != nil {
|
||||
return nil, errors.Wrapf(err, "could not verify deposit from %#x", bytesutil.Trunc(deposit.Data.PublicKey))
|
||||
@@ -963,7 +896,7 @@ func ProcessDeposit(beaconState *pb.BeaconState, deposit *ethpb.Deposit, valInde
|
||||
amount := deposit.Data.Amount
|
||||
index, ok := valIndexMap[bytesutil.ToBytes48(pubKey)]
|
||||
if !ok {
|
||||
domain := helpers.Domain(beaconState.Fork, helpers.CurrentEpoch(beaconState), params.BeaconConfig().DomainDeposit)
|
||||
domain := bls.ComputeDomain(params.BeaconConfig().DomainDeposit)
|
||||
depositSig := deposit.Data.Signature
|
||||
if err := verifySigningRoot(deposit.Data, pubKey, depositSig, domain); err != nil {
|
||||
// Ignore this error as in the spec pseudo code.
|
||||
@@ -1123,130 +1056,6 @@ func VerifyExit(beaconState *pb.BeaconState, exit *ethpb.VoluntaryExit) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProcessTransfers is one of the operations performed
|
||||
// on each processed beacon block to determine transfers between beacon chain balances.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def process_transfer(state: BeaconState, transfer: Transfer) -> None:
|
||||
// """
|
||||
// Process ``Transfer`` operation.
|
||||
// """
|
||||
// # Verify the balance the covers amount and fee (with overflow protection)
|
||||
// assert state.balances[transfer.sender] >= max(transfer.amount + transfer.fee, transfer.amount, transfer.fee)
|
||||
// # A transfer is valid in only one slot
|
||||
// assert state.slot == transfer.slot
|
||||
// # SenderIndex must satisfy at least one of the following conditions in the parenthesis:
|
||||
// assert (
|
||||
// # * Has not been activated
|
||||
// state.validator_registry[transfer.sender].activation_eligibility_epoch == FAR_FUTURE_EPOCH or
|
||||
// # * Is withdrawable
|
||||
// get_current_epoch(state) >= state.validator_registry[transfer.sender].withdrawable_epoch or
|
||||
// # * Balance after transfer is more than the effective balance threshold
|
||||
// transfer.amount + transfer.fee + MAX_EFFECTIVE_BALANCE <= state.balances[transfer.sender]
|
||||
// )
|
||||
// # Verify that the pubkey is valid
|
||||
// assert (
|
||||
// state.validator_registry[transfer.sender].withdrawal_credentials ==
|
||||
// int_to_bytes(BLS_WITHDRAWAL_PREFIX, length=1) + hash(transfer.pubkey)[1:]
|
||||
// )
|
||||
// # Verify that the signature is valid
|
||||
// assert bls_verify(transfer.pubkey, signing_root(transfer), transfer.signature, get_domain(state, DOMAIN_TRANSFER))
|
||||
// # Process the transfer
|
||||
// decrease_balance(state, transfer.sender, transfer.amount + transfer.fee)
|
||||
// increase_balance(state, transfer.recipient, transfer.amount)
|
||||
// increase_balance(state, get_beacon_proposer_index(state), transfer.fee)
|
||||
// # Verify balances are not dust
|
||||
// assert not (0 < state.balances[transfer.sender] < MIN_DEPOSIT_AMOUNT)
|
||||
// assert not (0 < state.balances[transfer.recipient] < MIN_DEPOSIT_AMOUNT)
|
||||
func ProcessTransfers(
|
||||
beaconState *pb.BeaconState,
|
||||
body *ethpb.BeaconBlockBody,
|
||||
) (*pb.BeaconState, error) {
|
||||
transfers := body.Transfers
|
||||
|
||||
for idx, transfer := range transfers {
|
||||
if err := verifyTransfer(beaconState, transfer); err != nil {
|
||||
return nil, errors.Wrapf(err, "could not verify transfer %d", idx)
|
||||
}
|
||||
// Process the transfer between accounts.
|
||||
beaconState = helpers.DecreaseBalance(beaconState, transfer.SenderIndex, transfer.Amount+transfer.Fee)
|
||||
beaconState = helpers.IncreaseBalance(beaconState, transfer.RecipientIndex, transfer.Amount)
|
||||
proposerIndex, err := helpers.BeaconProposerIndex(beaconState)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not determine beacon proposer index")
|
||||
}
|
||||
beaconState = helpers.IncreaseBalance(beaconState, proposerIndex, transfer.Fee)
|
||||
|
||||
// Finally, we verify balances will not go below the mininum.
|
||||
if beaconState.Balances[transfer.SenderIndex] < params.BeaconConfig().MinDepositAmount &&
|
||||
0 < beaconState.Balances[transfer.SenderIndex] {
|
||||
return nil, fmt.Errorf(
|
||||
"sender balance below critical level: %v",
|
||||
beaconState.Balances[transfer.SenderIndex],
|
||||
)
|
||||
}
|
||||
if beaconState.Balances[transfer.RecipientIndex] < params.BeaconConfig().MinDepositAmount &&
|
||||
0 < beaconState.Balances[transfer.RecipientIndex] {
|
||||
return nil, fmt.Errorf(
|
||||
"recipient balance below critical level: %v",
|
||||
beaconState.Balances[transfer.RecipientIndex],
|
||||
)
|
||||
}
|
||||
}
|
||||
return beaconState, nil
|
||||
}
|
||||
|
||||
func verifyTransfer(beaconState *pb.BeaconState, transfer *ethpb.Transfer) error {
|
||||
if transfer.SenderIndex > uint64(len(beaconState.Validators)) {
|
||||
return errors.New("transfer sender index out of bounds in validator registry")
|
||||
}
|
||||
|
||||
maxVal := transfer.Fee
|
||||
if transfer.Amount > maxVal {
|
||||
maxVal = transfer.Amount
|
||||
}
|
||||
if transfer.Amount+transfer.Fee > maxVal {
|
||||
maxVal = transfer.Amount + transfer.Fee
|
||||
}
|
||||
sender := beaconState.Validators[transfer.SenderIndex]
|
||||
senderBalance := beaconState.Balances[transfer.SenderIndex]
|
||||
// Verify the balance the covers amount and fee (with overflow protection).
|
||||
if senderBalance < maxVal {
|
||||
return fmt.Errorf("expected sender balance %d >= %d", senderBalance, maxVal)
|
||||
}
|
||||
// A transfer is valid in only one slot.
|
||||
if beaconState.Slot != transfer.Slot {
|
||||
return fmt.Errorf("expected beacon state slot %d == transfer slot %d", beaconState.Slot, transfer.Slot)
|
||||
}
|
||||
|
||||
// Sender must be not yet eligible for activation, withdrawn, or transfer balance over MAX_EFFECTIVE_BALANCE.
|
||||
senderNotActivationEligible := sender.ActivationEligibilityEpoch == params.BeaconConfig().FarFutureEpoch
|
||||
senderNotWithdrawn := helpers.CurrentEpoch(beaconState) >= sender.WithdrawableEpoch
|
||||
underMaxTransfer := transfer.Amount+transfer.Fee+params.BeaconConfig().MaxEffectiveBalance <= senderBalance
|
||||
|
||||
if !(senderNotActivationEligible || senderNotWithdrawn || underMaxTransfer) {
|
||||
return fmt.Errorf(
|
||||
"expected activation eligiblity: false or withdrawn: false or over max transfer: false, received %v %v %v",
|
||||
senderNotActivationEligible,
|
||||
senderNotWithdrawn,
|
||||
underMaxTransfer,
|
||||
)
|
||||
}
|
||||
// Verify that the pubkey is valid.
|
||||
buf := []byte{params.BeaconConfig().BLSWithdrawalPrefixByte}
|
||||
hashed := hashutil.Hash(transfer.SenderWithdrawalPublicKey)
|
||||
buf = append(buf, hashed[:][1:]...)
|
||||
if !bytes.Equal(sender.WithdrawalCredentials, buf) {
|
||||
return fmt.Errorf("invalid public key, expected %v, received %v", buf, sender.WithdrawalCredentials)
|
||||
}
|
||||
|
||||
domain := helpers.Domain(beaconState.Fork, helpers.CurrentEpoch(beaconState), params.BeaconConfig().DomainTransfer)
|
||||
if err := verifySigningRoot(transfer, transfer.SenderWithdrawalPublicKey, transfer.Signature, domain); err != nil {
|
||||
return errors.Wrap(err, "could not verify transfer signature")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClearEth1DataVoteCache clears the eth1 data vote count cache.
|
||||
func ClearEth1DataVoteCache() {
|
||||
eth1DataCache = cache.NewEth1DataVoteCache()
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/trieutil"
|
||||
@@ -36,12 +35,8 @@ func init() {
|
||||
}
|
||||
|
||||
func TestProcessBlockHeader_WrongProposerSig(t *testing.T) {
|
||||
if params.BeaconConfig().SlotsPerEpoch != 64 {
|
||||
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
|
||||
}
|
||||
|
||||
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, 0, ðpb.Eth1Data{})
|
||||
beaconState, err := state.GenesisBeaconState(deposits, 0, ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -81,10 +76,6 @@ func TestProcessBlockHeader_WrongProposerSig(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProcessBlockHeader_DifferentSlots(t *testing.T) {
|
||||
if params.BeaconConfig().SlotsPerEpoch != 64 {
|
||||
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
|
||||
}
|
||||
|
||||
validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = ðpb.Validator{
|
||||
@@ -101,8 +92,7 @@ func TestProcessBlockHeader_DifferentSlots(t *testing.T) {
|
||||
PreviousVersion: []byte{0, 0, 0, 0},
|
||||
CurrentVersion: []byte{0, 0, 0, 0},
|
||||
},
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
}
|
||||
|
||||
lbhsr, err := ssz.HashTreeRoot(state.LatestBlockHeader)
|
||||
@@ -134,10 +124,6 @@ func TestProcessBlockHeader_DifferentSlots(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProcessBlockHeader_PreviousBlockRootNotSignedRoot(t *testing.T) {
|
||||
if params.BeaconConfig().SlotsPerEpoch != 64 {
|
||||
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
|
||||
}
|
||||
|
||||
validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = ðpb.Validator{
|
||||
@@ -154,8 +140,7 @@ func TestProcessBlockHeader_PreviousBlockRootNotSignedRoot(t *testing.T) {
|
||||
PreviousVersion: []byte{0, 0, 0, 0},
|
||||
CurrentVersion: []byte{0, 0, 0, 0},
|
||||
},
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
}
|
||||
|
||||
currentEpoch := helpers.CurrentEpoch(state)
|
||||
@@ -183,10 +168,6 @@ func TestProcessBlockHeader_PreviousBlockRootNotSignedRoot(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProcessBlockHeader_SlashedProposer(t *testing.T) {
|
||||
if params.BeaconConfig().SlotsPerEpoch != 64 {
|
||||
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
|
||||
}
|
||||
|
||||
validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = ðpb.Validator{
|
||||
@@ -203,8 +184,7 @@ func TestProcessBlockHeader_SlashedProposer(t *testing.T) {
|
||||
PreviousVersion: []byte{0, 0, 0, 0},
|
||||
CurrentVersion: []byte{0, 0, 0, 0},
|
||||
},
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
}
|
||||
|
||||
parentRoot, err := ssz.SigningRoot(state.LatestBlockHeader)
|
||||
@@ -238,10 +218,6 @@ func TestProcessBlockHeader_SlashedProposer(t *testing.T) {
|
||||
func TestProcessBlockHeader_OK(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
|
||||
if params.BeaconConfig().SlotsPerEpoch != 64 {
|
||||
t.Fatalf("SlotsPerEpoch should be 64 for these tests to pass")
|
||||
}
|
||||
|
||||
validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = ðpb.Validator{
|
||||
@@ -258,8 +234,7 @@ func TestProcessBlockHeader_OK(t *testing.T) {
|
||||
PreviousVersion: []byte{0, 0, 0, 0},
|
||||
CurrentVersion: []byte{0, 0, 0, 0},
|
||||
},
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
}
|
||||
|
||||
latestBlockSignedRoot, err := ssz.SigningRoot(state.LatestBlockHeader)
|
||||
@@ -320,7 +295,7 @@ func TestProcessRandao_IncorrectProposerFailsVerification(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
|
||||
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -353,7 +328,7 @@ func TestProcessRandao_IncorrectProposerFailsVerification(t *testing.T) {
|
||||
|
||||
func TestProcessRandao_SignatureVerifiesAndUpdatesLatestStateMixes(t *testing.T) {
|
||||
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -421,7 +396,7 @@ func TestProcessEth1Data_SetsCorrectly(t *testing.T) {
|
||||
)
|
||||
}
|
||||
}
|
||||
func TestProcessProposerSlashings_UnmatchedHeaderEpochs(t *testing.T) {
|
||||
func TestProcessProposerSlashings_UnmatchedHeaderSlots(t *testing.T) {
|
||||
registry := make([]*ethpb.Validator, 2)
|
||||
currentSlot := uint64(0)
|
||||
slashings := []*ethpb.ProposerSlashing{
|
||||
@@ -445,7 +420,7 @@ func TestProcessProposerSlashings_UnmatchedHeaderEpochs(t *testing.T) {
|
||||
ProposerSlashings: slashings,
|
||||
},
|
||||
}
|
||||
want := "mismatched header epochs"
|
||||
want := "mismatched header slots"
|
||||
if _, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, block.Body); !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("Expected %s, received %v", want, err)
|
||||
}
|
||||
@@ -527,7 +502,6 @@ func TestProcessProposerSlashings_ValidatorNotSlashable(t *testing.T) {
|
||||
func TestProcessProposerSlashings_AppliesCorrectStatus(t *testing.T) {
|
||||
// We test the case when data is correct and verify the validator
|
||||
// registry has been updated.
|
||||
helpers.ClearShuffledValidatorCache()
|
||||
validators := make([]*ethpb.Validator, 100)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = ðpb.Validator{
|
||||
@@ -553,9 +527,8 @@ func TestProcessProposerSlashings_AppliesCorrectStatus(t *testing.T) {
|
||||
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
|
||||
Epoch: 0,
|
||||
},
|
||||
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
}
|
||||
|
||||
domain := helpers.Domain(
|
||||
@@ -643,18 +616,12 @@ func TestProcessAttesterSlashings_DataNotSlashable(t *testing.T) {
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 0},
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
Attestation_2: ðpb.IndexedAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 1},
|
||||
Target: ðpb.Checkpoint{Epoch: 1},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -685,9 +652,6 @@ func TestProcessAttesterSlashings_IndexedAttestationFailedToVerify(t *testing.T)
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 1},
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 4,
|
||||
},
|
||||
},
|
||||
CustodyBit_0Indices: []uint64{0, 1, 2},
|
||||
CustodyBit_1Indices: []uint64{0, 1, 2},
|
||||
@@ -696,9 +660,6 @@ func TestProcessAttesterSlashings_IndexedAttestationFailedToVerify(t *testing.T)
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 0},
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 4,
|
||||
},
|
||||
},
|
||||
CustodyBit_0Indices: []uint64{0, 1, 2},
|
||||
CustodyBit_1Indices: []uint64{0, 1, 2},
|
||||
@@ -729,9 +690,6 @@ func TestProcessAttesterSlashings_IndexedAttestationFailedToVerify(t *testing.T)
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 1},
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 4,
|
||||
},
|
||||
},
|
||||
CustodyBit_0Indices: make([]uint64, params.BeaconConfig().MaxValidatorsPerCommittee+1),
|
||||
},
|
||||
@@ -739,9 +697,6 @@ func TestProcessAttesterSlashings_IndexedAttestationFailedToVerify(t *testing.T)
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 0},
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 4,
|
||||
},
|
||||
},
|
||||
CustodyBit_0Indices: make([]uint64, params.BeaconConfig().MaxValidatorsPerCommittee+1),
|
||||
},
|
||||
@@ -758,7 +713,7 @@ func TestProcessAttesterSlashings_IndexedAttestationFailedToVerify(t *testing.T)
|
||||
|
||||
func TestProcessAttesterSlashings_AppliesCorrectStatus(t *testing.T) {
|
||||
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, 0, ðpb.Eth1Data{})
|
||||
beaconState, err := state.GenesisBeaconState(deposits, 0, ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
for _, vv := range beaconState.Validators {
|
||||
vv.WithdrawableEpoch = 1 * params.BeaconConfig().SlotsPerEpoch
|
||||
}
|
||||
@@ -767,9 +722,6 @@ func TestProcessAttesterSlashings_AppliesCorrectStatus(t *testing.T) {
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 1},
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 4,
|
||||
},
|
||||
},
|
||||
CustodyBit_0Indices: []uint64{0, 1},
|
||||
}
|
||||
@@ -781,7 +733,7 @@ func TestProcessAttesterSlashings_AppliesCorrectStatus(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
domain := helpers.Domain(beaconState.Fork, 0, params.BeaconConfig().DomainAttestation)
|
||||
domain := helpers.Domain(beaconState.Fork, 0, params.BeaconConfig().DomainBeaconAttester)
|
||||
sig0 := privKeys[0].Sign(hashTreeRoot[:], domain)
|
||||
sig1 := privKeys[1].Sign(hashTreeRoot[:], domain)
|
||||
aggregateSig := bls.AggregateSignatures([]*bls.Signature{sig0, sig1})
|
||||
@@ -791,9 +743,6 @@ func TestProcessAttesterSlashings_AppliesCorrectStatus(t *testing.T) {
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 0},
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 4,
|
||||
},
|
||||
},
|
||||
CustodyBit_0Indices: []uint64{0, 1},
|
||||
}
|
||||
@@ -851,9 +800,7 @@ func TestProcessAttestations_InclusionDelayFailure(t *testing.T) {
|
||||
{
|
||||
Data: ðpb.AttestationData{
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 0,
|
||||
},
|
||||
Slot: 5,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -863,19 +810,14 @@ func TestProcessAttestations_InclusionDelayFailure(t *testing.T) {
|
||||
},
|
||||
}
|
||||
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
attestationSlot, err := helpers.AttestationDataSlot(beaconState, attestations[0].Data)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
want := fmt.Sprintf(
|
||||
"attestation slot %d + inclusion delay %d > state slot %d",
|
||||
attestationSlot,
|
||||
attestations[0].Data.Slot,
|
||||
params.BeaconConfig().MinAttestationInclusionDelay,
|
||||
beaconState.Slot,
|
||||
)
|
||||
@@ -891,13 +833,7 @@ func TestProcessAttestations_NeitherCurrentNorPrevEpoch(t *testing.T) {
|
||||
att := ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 0,
|
||||
StartEpoch: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
Target: ðpb.Checkpoint{Epoch: 0}}}
|
||||
|
||||
block := ðpb.BeaconBlock{
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
@@ -905,17 +841,12 @@ func TestProcessAttestations_NeitherCurrentNorPrevEpoch(t *testing.T) {
|
||||
},
|
||||
}
|
||||
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
helpers.ClearAllCaches()
|
||||
beaconState.Slot += params.BeaconConfig().SlotsPerEpoch*4 + params.BeaconConfig().MinAttestationInclusionDelay
|
||||
beaconState.PreviousCrosslinks = []*ethpb.Crosslink{
|
||||
{
|
||||
Shard: 0,
|
||||
},
|
||||
}
|
||||
beaconState.PreviousJustifiedCheckpoint.Root = []byte("hello-world")
|
||||
beaconState.PreviousEpochAttestations = []*pb.PendingAttestation{}
|
||||
|
||||
@@ -933,17 +864,13 @@ func TestProcessAttestations_NeitherCurrentNorPrevEpoch(t *testing.T) {
|
||||
func TestProcessAttestations_CurrentEpochFFGDataMismatches(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
|
||||
aggBits := bitfield.NewBitlist(1)
|
||||
custodyBits := bitfield.NewBitlist(1)
|
||||
aggBits := bitfield.NewBitlist(3)
|
||||
custodyBits := bitfield.NewBitlist(3)
|
||||
attestations := []*ethpb.Attestation{
|
||||
{
|
||||
Data: ðpb.AttestationData{
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Source: ðpb.Checkpoint{Epoch: 1},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 0,
|
||||
StartEpoch: 0,
|
||||
},
|
||||
},
|
||||
AggregationBits: aggBits,
|
||||
CustodyBits: custodyBits,
|
||||
@@ -955,16 +882,11 @@ func TestProcessAttestations_CurrentEpochFFGDataMismatches(t *testing.T) {
|
||||
},
|
||||
}
|
||||
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beaconState.Slot += params.BeaconConfig().MinAttestationInclusionDelay
|
||||
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{
|
||||
{
|
||||
Shard: 0,
|
||||
},
|
||||
}
|
||||
beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world")
|
||||
beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{}
|
||||
|
||||
@@ -994,22 +916,20 @@ func TestProcessAttestations_PrevEpochFFGDataMismatches(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
|
||||
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
aggBits := bitfield.NewBitlist(1)
|
||||
aggBits := bitfield.NewBitlist(3)
|
||||
aggBits.SetBitAt(0, true)
|
||||
custodyBits := bitfield.NewBitlist(1)
|
||||
custodyBits := bitfield.NewBitlist(3)
|
||||
attestations := []*ethpb.Attestation{
|
||||
{
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 1},
|
||||
Target: ðpb.Checkpoint{Epoch: 1},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 0,
|
||||
},
|
||||
Slot: 1,
|
||||
},
|
||||
AggregationBits: aggBits,
|
||||
CustodyBits: custodyBits,
|
||||
@@ -1023,11 +943,6 @@ func TestProcessAttestations_PrevEpochFFGDataMismatches(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
|
||||
beaconState.Slot += params.BeaconConfig().SlotsPerEpoch + params.BeaconConfig().MinAttestationInclusionDelay
|
||||
beaconState.PreviousCrosslinks = []*ethpb.Crosslink{
|
||||
{
|
||||
Shard: 0,
|
||||
},
|
||||
}
|
||||
beaconState.PreviousJustifiedCheckpoint.Root = []byte("hello-world")
|
||||
beaconState.PreviousEpochAttestations = []*pb.PendingAttestation{}
|
||||
|
||||
@@ -1055,86 +970,20 @@ func TestProcessAttestations_PrevEpochFFGDataMismatches(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessAttestations_CrosslinkMismatches(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
|
||||
aggBits := bitfield.NewBitlist(1)
|
||||
aggBits.SetBitAt(0, true)
|
||||
custodyBits := bitfield.NewBitlist(1)
|
||||
attestations := []*ethpb.Attestation{
|
||||
{
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 0,
|
||||
},
|
||||
},
|
||||
AggregationBits: aggBits,
|
||||
CustodyBits: custodyBits,
|
||||
},
|
||||
}
|
||||
block := ðpb.BeaconBlock{
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
Attestations: attestations,
|
||||
},
|
||||
}
|
||||
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beaconState.Slot += params.BeaconConfig().MinAttestationInclusionDelay
|
||||
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{
|
||||
{
|
||||
Shard: 0,
|
||||
StartEpoch: 0,
|
||||
},
|
||||
}
|
||||
beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world")
|
||||
beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{}
|
||||
|
||||
want := "mismatched parent crosslink root"
|
||||
if _, err := blocks.ProcessAttestations(context.Background(), beaconState, block.Body); !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("Expected %s, received %v", want, err)
|
||||
}
|
||||
|
||||
block.Body.Attestations[0].Data.Crosslink.StartEpoch = 0
|
||||
if _, err := blocks.ProcessAttestations(context.Background(), beaconState, block.Body); !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("Expected %s, received %v", want, err)
|
||||
}
|
||||
encoded, err := ssz.HashTreeRoot(beaconState.CurrentCrosslinks[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block.Body.Attestations[0].Data.Crosslink.ParentRoot = encoded[:]
|
||||
block.Body.Attestations[0].Data.Crosslink.DataRoot = encoded[:]
|
||||
|
||||
want = fmt.Sprintf("expected data root %#x == ZERO_HASH", encoded)
|
||||
if _, err := blocks.ProcessAttestations(context.Background(), beaconState, block.Body); !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("Expected %s, received %v", want, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessAttestations_InvalidAggregationBitsLength(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
aggBits := bitfield.NewBitlist(2)
|
||||
custodyBits := bitfield.NewBitlist(2)
|
||||
aggBits := bitfield.NewBitlist(4)
|
||||
custodyBits := bitfield.NewBitlist(4)
|
||||
att := ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 0,
|
||||
StartEpoch: 0,
|
||||
},
|
||||
},
|
||||
Target: ðpb.Checkpoint{Epoch: 0}},
|
||||
AggregationBits: aggBits,
|
||||
CustodyBits: custodyBits,
|
||||
}
|
||||
@@ -1146,67 +995,39 @@ func TestProcessAttestations_InvalidAggregationBitsLength(t *testing.T) {
|
||||
}
|
||||
|
||||
beaconState.Slot += params.BeaconConfig().MinAttestationInclusionDelay
|
||||
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{
|
||||
{
|
||||
Shard: 0,
|
||||
StartEpoch: 0,
|
||||
},
|
||||
}
|
||||
|
||||
beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world")
|
||||
beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{}
|
||||
|
||||
encoded, err := ssz.HashTreeRoot(beaconState.CurrentCrosslinks[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block.Body.Attestations[0].Data.Crosslink.ParentRoot = encoded[:]
|
||||
block.Body.Attestations[0].Data.Crosslink.DataRoot = params.BeaconConfig().ZeroHash[:]
|
||||
|
||||
expected := "failed to verify aggregation bitfield: wanted participants bitfield length 1, got: 2"
|
||||
expected := "failed to verify aggregation bitfield: wanted participants bitfield length 3, got: 4"
|
||||
_, err = blocks.ProcessAttestations(context.Background(), beaconState, block.Body)
|
||||
if !strings.Contains(err.Error(), expected) {
|
||||
t.Errorf("Expected error checking aggregation and custody bit length, received: %v", err)
|
||||
t.Errorf("Did not receive wanted error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessAttestations_OK(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
aggBits := bitfield.NewBitlist(1)
|
||||
aggBits := bitfield.NewBitlist(3)
|
||||
aggBits.SetBitAt(0, true)
|
||||
custodyBits := bitfield.NewBitlist(1)
|
||||
custodyBits := bitfield.NewBitlist(3)
|
||||
att := ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
|
||||
Target: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 0,
|
||||
StartEpoch: 0,
|
||||
},
|
||||
},
|
||||
AggregationBits: aggBits,
|
||||
CustodyBits: custodyBits,
|
||||
}
|
||||
|
||||
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{
|
||||
{
|
||||
Shard: 0,
|
||||
StartEpoch: 0,
|
||||
},
|
||||
}
|
||||
beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world")
|
||||
beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{}
|
||||
encoded, err := ssz.HashTreeRoot(beaconState.CurrentCrosslinks[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
att.Data.Crosslink.ParentRoot = encoded[:]
|
||||
att.Data.Crosslink.DataRoot = params.BeaconConfig().ZeroHash[:]
|
||||
|
||||
attestingIndices, err := helpers.AttestingIndices(beaconState, att.Data, att.AggregationBits)
|
||||
if err != nil {
|
||||
@@ -1220,7 +1041,7 @@ func TestProcessAttestations_OK(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
domain := helpers.Domain(beaconState.Fork, 0, params.BeaconConfig().DomainAttestation)
|
||||
domain := helpers.Domain(beaconState.Fork, 0, params.BeaconConfig().DomainBeaconAttester)
|
||||
sigs := make([]*bls.Signature, len(attestingIndices))
|
||||
for i, indice := range attestingIndices {
|
||||
sig := privKeys[indice].Sign(hashTreeRoot[:], domain)
|
||||
@@ -1244,19 +1065,15 @@ func TestProcessAttestations_OK(t *testing.T) {
|
||||
func TestProcessAggregatedAttestation_OverlappingBits(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 300)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
domain := helpers.Domain(beaconState.Fork, 0, params.BeaconConfig().DomainAttestation)
|
||||
domain := helpers.Domain(beaconState.Fork, 0, params.BeaconConfig().DomainBeaconAttester)
|
||||
data := ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
|
||||
Target: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 0,
|
||||
StartEpoch: 0,
|
||||
},
|
||||
}
|
||||
aggBits1 := bitfield.NewBitlist(4)
|
||||
aggBits1.SetBitAt(0, true)
|
||||
@@ -1269,15 +1086,8 @@ func TestProcessAggregatedAttestation_OverlappingBits(t *testing.T) {
|
||||
CustodyBits: custodyBits1,
|
||||
}
|
||||
|
||||
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{{Shard: 0, StartEpoch: 0}}
|
||||
beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world")
|
||||
beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{}
|
||||
encoded, err := ssz.HashTreeRoot(beaconState.CurrentCrosslinks[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
att1.Data.Crosslink.ParentRoot = encoded[:]
|
||||
att1.Data.Crosslink.DataRoot = params.BeaconConfig().ZeroHash[:]
|
||||
|
||||
attestingIndices1, err := helpers.AttestingIndices(beaconState, att1.Data, att1.AggregationBits)
|
||||
if err != nil {
|
||||
@@ -1309,9 +1119,6 @@ func TestProcessAggregatedAttestation_OverlappingBits(t *testing.T) {
|
||||
CustodyBits: custodyBits2,
|
||||
}
|
||||
|
||||
att2.Data.Crosslink.ParentRoot = encoded[:]
|
||||
att2.Data.Crosslink.DataRoot = params.BeaconConfig().ZeroHash[:]
|
||||
|
||||
attestingIndices2, err := helpers.AttestingIndices(beaconState, att2.Data, att2.AggregationBits)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -1339,39 +1146,28 @@ func TestProcessAggregatedAttestation_OverlappingBits(t *testing.T) {
|
||||
func TestProcessAggregatedAttestation_NoOverlappingBits(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 300)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
domain := helpers.Domain(beaconState.Fork, 0, params.BeaconConfig().DomainAttestation)
|
||||
domain := helpers.Domain(beaconState.Fork, 0, params.BeaconConfig().DomainBeaconAttester)
|
||||
data := ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
|
||||
Target: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 0,
|
||||
StartEpoch: 0,
|
||||
},
|
||||
}
|
||||
aggBits1 := bitfield.NewBitlist(4)
|
||||
aggBits1 := bitfield.NewBitlist(9)
|
||||
aggBits1.SetBitAt(0, true)
|
||||
aggBits1.SetBitAt(1, true)
|
||||
custodyBits1 := bitfield.NewBitlist(4)
|
||||
custodyBits1 := bitfield.NewBitlist(9)
|
||||
att1 := ðpb.Attestation{
|
||||
Data: data,
|
||||
AggregationBits: aggBits1,
|
||||
CustodyBits: custodyBits1,
|
||||
}
|
||||
|
||||
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{{Shard: 0, StartEpoch: 0}}
|
||||
beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world")
|
||||
beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{}
|
||||
encoded, err := ssz.HashTreeRoot(beaconState.CurrentCrosslinks[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
att1.Data.Crosslink.ParentRoot = encoded[:]
|
||||
att1.Data.Crosslink.DataRoot = params.BeaconConfig().ZeroHash[:]
|
||||
|
||||
attestingIndices1, err := helpers.AttestingIndices(beaconState, att1.Data, att1.AggregationBits)
|
||||
if err != nil {
|
||||
@@ -1392,19 +1188,16 @@ func TestProcessAggregatedAttestation_NoOverlappingBits(t *testing.T) {
|
||||
}
|
||||
att1.Signature = bls.AggregateSignatures(sigs).Marshal()[:]
|
||||
|
||||
aggBits2 := bitfield.NewBitlist(4)
|
||||
aggBits2 := bitfield.NewBitlist(9)
|
||||
aggBits2.SetBitAt(2, true)
|
||||
aggBits2.SetBitAt(3, true)
|
||||
custodyBits2 := bitfield.NewBitlist(4)
|
||||
custodyBits2 := bitfield.NewBitlist(9)
|
||||
att2 := ðpb.Attestation{
|
||||
Data: data,
|
||||
AggregationBits: aggBits2,
|
||||
CustodyBits: custodyBits2,
|
||||
}
|
||||
|
||||
att2.Data.Crosslink.ParentRoot = encoded[:]
|
||||
att2.Data.Crosslink.DataRoot = params.BeaconConfig().ZeroHash[:]
|
||||
|
||||
attestingIndices2, err := helpers.AttestingIndices(beaconState, att2.Data, att2.AggregationBits)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -1445,22 +1238,18 @@ func TestProcessAttestationsNoVerify_OK(t *testing.T) {
|
||||
// Attestation with an empty signature
|
||||
helpers.ClearAllCaches()
|
||||
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
aggBits := bitfield.NewBitlist(1)
|
||||
aggBits := bitfield.NewBitlist(3)
|
||||
aggBits.SetBitAt(1, true)
|
||||
custodyBits := bitfield.NewBitlist(1)
|
||||
custodyBits := bitfield.NewBitlist(3)
|
||||
att := ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 0,
|
||||
StartEpoch: 0,
|
||||
},
|
||||
},
|
||||
AggregationBits: aggBits,
|
||||
CustodyBits: custodyBits,
|
||||
@@ -1470,22 +1259,9 @@ func TestProcessAttestationsNoVerify_OK(t *testing.T) {
|
||||
att.Signature = zeroSig[:]
|
||||
|
||||
beaconState.Slot += params.BeaconConfig().MinAttestationInclusionDelay
|
||||
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{
|
||||
{
|
||||
Shard: 0,
|
||||
StartEpoch: 0,
|
||||
},
|
||||
}
|
||||
beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world")
|
||||
beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{}
|
||||
|
||||
encoded, err := ssz.HashTreeRoot(beaconState.CurrentCrosslinks[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
att.Data.Crosslink.ParentRoot = encoded[:]
|
||||
att.Data.Crosslink.DataRoot = params.BeaconConfig().ZeroHash[:]
|
||||
|
||||
if _, err := blocks.ProcessAttestationNoVerify(context.TODO(), beaconState, att); err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
@@ -1493,9 +1269,6 @@ func TestProcessAttestationsNoVerify_OK(t *testing.T) {
|
||||
|
||||
func TestConvertToIndexed_OK(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
if params.BeaconConfig().SlotsPerEpoch != 64 {
|
||||
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
|
||||
}
|
||||
|
||||
validators := make([]*ethpb.Validator, 2*params.BeaconConfig().SlotsPerEpoch)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
@@ -1505,10 +1278,9 @@ func TestConvertToIndexed_OK(t *testing.T) {
|
||||
}
|
||||
|
||||
state := &pb.BeaconState{
|
||||
Slot: 5,
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
Slot: 5,
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
}
|
||||
tests := []struct {
|
||||
aggregationBitfield bitfield.Bitlist
|
||||
@@ -1519,20 +1291,20 @@ func TestConvertToIndexed_OK(t *testing.T) {
|
||||
{
|
||||
aggregationBitfield: bitfield.Bitlist{0x07},
|
||||
custodyBitfield: bitfield.Bitlist{0x05},
|
||||
wantedCustodyBit0Indices: []uint64{71},
|
||||
wantedCustodyBit1Indices: []uint64{127},
|
||||
wantedCustodyBit0Indices: []uint64{4},
|
||||
wantedCustodyBit1Indices: []uint64{30},
|
||||
},
|
||||
{
|
||||
aggregationBitfield: bitfield.Bitlist{0x07},
|
||||
custodyBitfield: bitfield.Bitlist{0x06},
|
||||
wantedCustodyBit0Indices: []uint64{127},
|
||||
wantedCustodyBit1Indices: []uint64{71},
|
||||
wantedCustodyBit0Indices: []uint64{30},
|
||||
wantedCustodyBit1Indices: []uint64{4},
|
||||
},
|
||||
{
|
||||
aggregationBitfield: bitfield.Bitlist{0x07},
|
||||
custodyBitfield: bitfield.Bitlist{0x07},
|
||||
wantedCustodyBit0Indices: []uint64{},
|
||||
wantedCustodyBit1Indices: []uint64{71, 127},
|
||||
wantedCustodyBit1Indices: []uint64{4, 30},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1541,9 +1313,6 @@ func TestConvertToIndexed_OK(t *testing.T) {
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 0},
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 3,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -1571,10 +1340,8 @@ func TestConvertToIndexed_OK(t *testing.T) {
|
||||
|
||||
func TestVerifyIndexedAttestation_OK(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
if params.BeaconConfig().SlotsPerEpoch != 64 {
|
||||
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
|
||||
}
|
||||
numOfValidators := 2 * params.BeaconConfig().SlotsPerEpoch
|
||||
|
||||
numOfValidators := 4 * params.BeaconConfig().SlotsPerEpoch
|
||||
validators := make([]*ethpb.Validator, numOfValidators)
|
||||
_, _, keys := testutil.SetupInitialDeposits(t, numOfValidators)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
@@ -1592,8 +1359,7 @@ func TestVerifyIndexedAttestation_OK(t *testing.T) {
|
||||
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
|
||||
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
|
||||
},
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
}
|
||||
tests := []struct {
|
||||
attestation *ethpb.IndexedAttestation
|
||||
@@ -1640,7 +1406,7 @@ func TestVerifyIndexedAttestation_OK(t *testing.T) {
|
||||
CustodyBit: false,
|
||||
}
|
||||
|
||||
domain := helpers.Domain(state.Fork, tt.attestation.Data.Target.Epoch, params.BeaconConfig().DomainAttestation)
|
||||
domain := helpers.Domain(state.Fork, tt.attestation.Data.Target.Epoch, params.BeaconConfig().DomainBeaconAttester)
|
||||
|
||||
root, err := ssz.HashTreeRoot(attDataAndCustodyBit)
|
||||
if err != nil {
|
||||
@@ -2102,203 +1868,3 @@ func TestProcessVoluntaryExits_AppliesCorrectStatus(t *testing.T) {
|
||||
helpers.DelayedActivationExitEpoch(state.Slot/params.BeaconConfig().SlotsPerEpoch), newRegistry[0].ExitEpoch)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessBeaconTransfers_NotEnoughSenderIndexBalance(t *testing.T) {
|
||||
registry := []*ethpb.Validator{
|
||||
{
|
||||
ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
},
|
||||
}
|
||||
balances := []uint64{params.BeaconConfig().MaxEffectiveBalance}
|
||||
state := &pb.BeaconState{
|
||||
Validators: registry,
|
||||
Balances: balances,
|
||||
}
|
||||
transfers := []*ethpb.Transfer{
|
||||
{
|
||||
Fee: params.BeaconConfig().MaxEffectiveBalance,
|
||||
Amount: params.BeaconConfig().MaxEffectiveBalance,
|
||||
},
|
||||
}
|
||||
block := ðpb.BeaconBlock{
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
Transfers: transfers,
|
||||
},
|
||||
}
|
||||
want := fmt.Sprintf(
|
||||
"expected sender balance %d >= %d",
|
||||
balances[0],
|
||||
transfers[0].Fee+transfers[0].Amount,
|
||||
)
|
||||
if _, err := blocks.ProcessTransfers(state, block.Body); !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("Expected %s, received %v", want, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessBeaconTransfers_FailsVerification(t *testing.T) {
|
||||
testConfig := params.BeaconConfig()
|
||||
testConfig.MaxTransfers = 1
|
||||
params.OverrideBeaconConfig(testConfig)
|
||||
registry := []*ethpb.Validator{
|
||||
{
|
||||
ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
},
|
||||
{
|
||||
ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
},
|
||||
}
|
||||
balances := []uint64{params.BeaconConfig().MaxEffectiveBalance}
|
||||
state := &pb.BeaconState{
|
||||
Slot: 0,
|
||||
Validators: registry,
|
||||
Balances: balances,
|
||||
}
|
||||
transfers := []*ethpb.Transfer{
|
||||
{
|
||||
Fee: params.BeaconConfig().MaxEffectiveBalance + 1,
|
||||
},
|
||||
}
|
||||
block := ðpb.BeaconBlock{
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
Transfers: transfers,
|
||||
},
|
||||
}
|
||||
want := fmt.Sprintf(
|
||||
"expected sender balance %d >= %d",
|
||||
balances[0],
|
||||
transfers[0].Fee,
|
||||
)
|
||||
if _, err := blocks.ProcessTransfers(state, block.Body); !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("Expected %s, received %v", want, err)
|
||||
}
|
||||
|
||||
block.Body.Transfers = []*ethpb.Transfer{
|
||||
{
|
||||
Fee: params.BeaconConfig().MinDepositAmount,
|
||||
Slot: state.Slot + 1,
|
||||
},
|
||||
}
|
||||
want = fmt.Sprintf(
|
||||
"expected beacon state slot %d == transfer slot %d",
|
||||
state.Slot,
|
||||
block.Body.Transfers[0].Slot,
|
||||
)
|
||||
if _, err := blocks.ProcessTransfers(state, block.Body); !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("Expected %s, received %v", want, err)
|
||||
}
|
||||
|
||||
state.Validators[0].WithdrawableEpoch = params.BeaconConfig().FarFutureEpoch
|
||||
state.Validators[0].ActivationEligibilityEpoch = 0
|
||||
state.Balances[0] = params.BeaconConfig().MinDepositAmount + params.BeaconConfig().MaxEffectiveBalance
|
||||
block.Body.Transfers = []*ethpb.Transfer{
|
||||
{
|
||||
Fee: params.BeaconConfig().MinDepositAmount,
|
||||
Amount: params.BeaconConfig().MaxEffectiveBalance,
|
||||
Slot: state.Slot,
|
||||
},
|
||||
}
|
||||
want = "over max transfer"
|
||||
if _, err := blocks.ProcessTransfers(state, block.Body); !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("Expected %s, received %v", want, err)
|
||||
}
|
||||
|
||||
state.Validators[0].WithdrawableEpoch = 0
|
||||
state.Validators[0].ActivationEligibilityEpoch = params.BeaconConfig().FarFutureEpoch
|
||||
buf := []byte{params.BeaconConfig().BLSWithdrawalPrefixByte}
|
||||
pubKey := []byte("B")
|
||||
hashed := hashutil.Hash(pubKey)
|
||||
buf = append(buf, hashed[:]...)
|
||||
state.Validators[0].WithdrawalCredentials = buf
|
||||
block.Body.Transfers = []*ethpb.Transfer{
|
||||
{
|
||||
Fee: params.BeaconConfig().MinDepositAmount,
|
||||
Amount: params.BeaconConfig().MinDepositAmount,
|
||||
Slot: state.Slot,
|
||||
SenderWithdrawalPublicKey: []byte("A"),
|
||||
},
|
||||
}
|
||||
want = "invalid public key"
|
||||
if _, err := blocks.ProcessTransfers(state, block.Body); !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("Expected %s, received %v", want, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessBeaconTransfers_OK(t *testing.T) {
|
||||
helpers.ClearShuffledValidatorCache()
|
||||
testConfig := params.BeaconConfig()
|
||||
testConfig.MaxTransfers = 1
|
||||
params.OverrideBeaconConfig(testConfig)
|
||||
validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount/32)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = ðpb.Validator{
|
||||
ActivationEpoch: 0,
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
Slashed: false,
|
||||
WithdrawableEpoch: 0,
|
||||
}
|
||||
}
|
||||
validatorBalances := make([]uint64, len(validators))
|
||||
for i := 0; i < len(validatorBalances); i++ {
|
||||
validatorBalances[i] = params.BeaconConfig().MaxEffectiveBalance
|
||||
}
|
||||
|
||||
state := &pb.BeaconState{
|
||||
Validators: validators,
|
||||
Slot: 0,
|
||||
Balances: validatorBalances,
|
||||
Fork: &pb.Fork{
|
||||
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
|
||||
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
|
||||
},
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
}
|
||||
|
||||
transfer := ðpb.Transfer{
|
||||
SenderIndex: 0,
|
||||
RecipientIndex: 1,
|
||||
Fee: params.BeaconConfig().MinDepositAmount,
|
||||
Amount: params.BeaconConfig().MinDepositAmount,
|
||||
Slot: state.Slot,
|
||||
}
|
||||
|
||||
priv, err := bls.RandKey(rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pubKey := priv.PublicKey().Marshal()[:]
|
||||
transfer.SenderWithdrawalPublicKey = pubKey
|
||||
state.Validators[transfer.SenderIndex].PublicKey = pubKey
|
||||
signingRoot, err := ssz.SigningRoot(transfer)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get signing root of block: %v", err)
|
||||
}
|
||||
epoch := helpers.CurrentEpoch(state)
|
||||
dt := helpers.Domain(state.Fork, epoch, params.BeaconConfig().DomainTransfer)
|
||||
transferSig := priv.Sign(signingRoot[:], dt)
|
||||
transfer.Signature = transferSig.Marshal()[:]
|
||||
|
||||
block := ðpb.BeaconBlock{
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
Transfers: []*ethpb.Transfer{transfer},
|
||||
},
|
||||
}
|
||||
buf := []byte{params.BeaconConfig().BLSWithdrawalPrefixByte}
|
||||
hashed := hashutil.Hash(pubKey)
|
||||
buf = append(buf, hashed[:][1:]...)
|
||||
state.Validators[0].WithdrawalCredentials = buf
|
||||
state.Validators[0].ActivationEligibilityEpoch = params.BeaconConfig().FarFutureEpoch
|
||||
newState, err := blocks.ProcessTransfers(state, block.Body)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
expectedRecipientIndex := params.BeaconConfig().MaxEffectiveBalance + block.Body.Transfers[0].Amount
|
||||
if newState.Balances[1] != expectedRecipientIndex {
|
||||
t.Errorf("Expected recipient balance %d, received %d", newState.Balances[1], expectedRecipientIndex)
|
||||
}
|
||||
expectedSenderIndex := params.BeaconConfig().MaxEffectiveBalance - block.Body.Transfers[0].Amount - block.Body.Transfers[0].Fee
|
||||
if newState.Balances[0] != expectedSenderIndex {
|
||||
t.Errorf("Expected sender balance %d, received %d", newState.Balances[0], expectedSenderIndex)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,9 @@ func runBlockProcessingTest(t *testing.T, config string) {
|
||||
t.Fatalf("Failed to unmarshal: %v", err)
|
||||
}
|
||||
beaconState, transitionError = state.ExecuteStateTransition(context.Background(), beaconState, block)
|
||||
if transitionError != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If the post.ssz is not present, it means the test should fail on our end.
|
||||
@@ -73,7 +76,7 @@ func runBlockProcessingTest(t *testing.T, config string) {
|
||||
|
||||
if postSSZExists {
|
||||
if transitionError != nil {
|
||||
t.Fatalf("Unexpected error: %v", transitionError)
|
||||
t.Errorf("Unexpected error: %v", transitionError)
|
||||
}
|
||||
|
||||
postBeaconStateFile, err := ioutil.ReadFile(postSSZFilepath)
|
||||
|
||||
@@ -15,7 +15,6 @@ go_library(
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/mathutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
],
|
||||
@@ -33,6 +32,7 @@ go_test(
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
@@ -79,16 +78,9 @@ func MatchAttestations(state *pb.BeaconState, epoch uint64) (*MatchedAttestation
|
||||
if bytes.Equal(srcAtt.Data.Target.Root, targetRoot) {
|
||||
tgtAtts = append(tgtAtts, srcAtt)
|
||||
}
|
||||
|
||||
// If the block root at slot matches attestation's block root at slot,
|
||||
// then we know this attestation has correctly voted for head.
|
||||
slot, err := helpers.AttestationDataSlot(state, srcAtt.Data)
|
||||
headRoot, err := helpers.BlockRootAtSlot(state, srcAtt.Data.Slot)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get attestation slot")
|
||||
}
|
||||
headRoot, err := helpers.BlockRootAtSlot(state, slot)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not get block root for slot %d", slot)
|
||||
return nil, errors.Wrapf(err, "could not get block root for slot %d", srcAtt.Data.Slot)
|
||||
}
|
||||
if bytes.Equal(srcAtt.Data.BeaconBlockRoot, headRoot) {
|
||||
headAtts = append(headAtts, srcAtt)
|
||||
@@ -229,54 +221,6 @@ func ProcessJustificationAndFinalization(state *pb.BeaconState, prevAttestedBal
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// ProcessCrosslinks processes crosslink and finds the crosslink
|
||||
// with enough state to make it canonical in state.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def process_crosslinks(state: BeaconState) -> None:
|
||||
// state.previous_crosslinks = [c for c in state.current_crosslinks]
|
||||
// for epoch in (get_previous_epoch(state), get_current_epoch(state)):
|
||||
// for offset in range(get_epoch_committee_count(state, epoch)):
|
||||
// shard = (get_epoch_start_shard(state, epoch) + offset) % SHARD_COUNT
|
||||
// crosslink_committee = get_crosslink_committee(state, epoch, shard)
|
||||
// winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices(state, epoch, shard)
|
||||
// if 3 * get_total_balance(state, attesting_indices) >= 2 * get_total_balance(state, crosslink_committee):
|
||||
// state.current_crosslinks[shard] = winning_crosslink
|
||||
func ProcessCrosslinks(state *pb.BeaconState) (*pb.BeaconState, error) {
|
||||
copy(state.PreviousCrosslinks, state.CurrentCrosslinks)
|
||||
epochs := []uint64{helpers.PrevEpoch(state), helpers.CurrentEpoch(state)}
|
||||
for _, e := range epochs {
|
||||
count, err := helpers.CommitteeCount(state, e)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get epoch committee count")
|
||||
}
|
||||
startShard, err := helpers.StartShard(state, e)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get epoch start shards")
|
||||
}
|
||||
for offset := uint64(0); offset < count; offset++ {
|
||||
shard := (startShard + offset) % params.BeaconConfig().ShardCount
|
||||
committee, err := helpers.CrosslinkCommittee(state, e, shard)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get crosslink committee")
|
||||
}
|
||||
crosslink, indices, err := WinningCrosslink(state, shard, e)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get winning crosslink")
|
||||
}
|
||||
attestedBalance := helpers.TotalBalance(state, indices)
|
||||
totalBalance := helpers.TotalBalance(state, committee)
|
||||
|
||||
// In order for a crosslink to get included in state, the attesting balance needs to
|
||||
// be greater than 2/3 of the total balance.
|
||||
if 3*attestedBalance >= 2*totalBalance {
|
||||
state.CurrentCrosslinks[shard] = crosslink
|
||||
}
|
||||
}
|
||||
}
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// ProcessRewardsAndPenalties processes the rewards and penalties of individual validator.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
@@ -298,14 +242,10 @@ func ProcessRewardsAndPenalties(state *pb.BeaconState) (*pb.BeaconState, error)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get attestation delta")
|
||||
}
|
||||
clRewards, clPenalties, err := crosslinkDelta(state)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not get crosslink delta")
|
||||
}
|
||||
|
||||
for i := 0; i < len(state.Validators); i++ {
|
||||
state = helpers.IncreaseBalance(state, uint64(i), attsRewards[i]+clRewards[i])
|
||||
state = helpers.DecreaseBalance(state, uint64(i), attsPenalties[i]+clPenalties[i])
|
||||
state = helpers.IncreaseBalance(state, uint64(i), attsRewards[i])
|
||||
state = helpers.DecreaseBalance(state, uint64(i), attsPenalties[i])
|
||||
}
|
||||
return state, nil
|
||||
}
|
||||
@@ -374,7 +314,12 @@ func ProcessRegistryUpdates(state *pb.BeaconState) (*pb.BeaconState, error) {
|
||||
|
||||
// Only activate just enough validators according to the activation churn limit.
|
||||
limit := len(activationQ)
|
||||
churnLimit, err := helpers.ValidatorChurnLimit(state)
|
||||
activeValidatorCount, err := helpers.ActiveValidatorCount(state, currentEpoch)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get active validator count")
|
||||
}
|
||||
|
||||
churnLimit, err := helpers.ValidatorChurnLimit(activeValidatorCount)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get churn limit")
|
||||
}
|
||||
@@ -490,30 +435,6 @@ func ProcessFinalUpdates(state *pb.BeaconState) (*pb.BeaconState, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Set active index root.
|
||||
// index_epoch = Epoch(next_epoch + ACTIVATION_EXIT_DELAY)
|
||||
// index_root_position = index_epoch % EPOCHS_PER_HISTORICAL_VECTOR
|
||||
// indices_list = List[ValidatorIndex, VALIDATOR_REGISTRY_LIMIT](get_active_validator_indices(state, index_epoch))
|
||||
// state.active_index_roots[index_root_position] = hash_tree_root(indices_list)
|
||||
activationDelay := params.BeaconConfig().ActivationExitDelay
|
||||
idxRootPosition := (nextEpoch + activationDelay) % params.BeaconConfig().EpochsPerHistoricalVector
|
||||
activeIndices, err := helpers.ActiveValidatorIndices(state, nextEpoch+activationDelay)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get active indices")
|
||||
}
|
||||
idxRoot, err := ssz.HashTreeRootWithCapacity(activeIndices, uint64(1099511627776))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not tree hash active indices")
|
||||
}
|
||||
state.ActiveIndexRoots[idxRootPosition] = idxRoot[:]
|
||||
|
||||
commRootPosition := nextEpoch % params.BeaconConfig().EpochsPerHistoricalVector
|
||||
comRoot, err := helpers.CompactCommitteesRoot(state, nextEpoch)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get compact committee root")
|
||||
}
|
||||
state.CompactCommitteesRoots[commRootPosition] = comRoot[:]
|
||||
|
||||
// Set total slashed balances.
|
||||
slashedExitLength := params.BeaconConfig().EpochsPerSlashingsVector
|
||||
state.Slashings[nextEpoch%slashedExitLength] = 0
|
||||
@@ -537,13 +458,6 @@ func ProcessFinalUpdates(state *pb.BeaconState) (*pb.BeaconState, error) {
|
||||
state.HistoricalRoots = append(state.HistoricalRoots, batchRoot[:])
|
||||
}
|
||||
|
||||
// Update start shard.
|
||||
delta, err := helpers.ShardDelta(state, currentEpoch)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get shard delta")
|
||||
}
|
||||
state.StartShard = (state.StartShard + delta) % params.BeaconConfig().ShardCount
|
||||
|
||||
// Rotate current and previous epoch attestations.
|
||||
state.PreviousEpochAttestations = state.CurrentEpochAttestations
|
||||
state.CurrentEpochAttestations = []*pb.PendingAttestation{}
|
||||
@@ -591,93 +505,6 @@ func unslashedAttestingIndices(state *pb.BeaconState, atts []*pb.PendingAttestat
|
||||
return setIndices, nil
|
||||
}
|
||||
|
||||
// WinningCrosslink returns the most staked balance-wise crosslink of a given shard and epoch.
|
||||
// It also returns the attesting inaidces of the winning cross link.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_winning_crosslink_and_attesting_indices(state: BeaconState,
|
||||
// epoch: Epoch,
|
||||
// shard: Shard) -> Tuple[Crosslink, List[ValidatorIndex]]:
|
||||
// attestations = [a for a in get_matching_source_attestations(state, epoch) if a.data.crosslink.shard == shard]
|
||||
// crosslinks = list(filter(
|
||||
// lambda c: hash_tree_root(state.current_crosslinks[shard]) in (c.parent_root, hash_tree_root(c)),
|
||||
// [a.data.crosslink for a in attestations]
|
||||
// ))
|
||||
// # Winning crosslink has the crosslink data root with the most balance voting for it (ties broken lexicographically)
|
||||
// winning_crosslink = max(crosslinks, key=lambda c: (
|
||||
// get_attesting_balance(state, [a for a in attestations if a.data.crosslink == c]), c.data_root
|
||||
// ), default=Crosslink())
|
||||
// winning_attestations = [a for a in attestations if a.data.crosslink == winning_crosslink]
|
||||
// return winning_crosslink, get_unslashed_attesting_indices(state, winning_attestations)
|
||||
func WinningCrosslink(state *pb.BeaconState, shard uint64, epoch uint64) (*ethpb.Crosslink, []uint64, error) {
|
||||
var shardAtts []*pb.PendingAttestation
|
||||
matchedAtts, err := MatchAttestations(state, epoch)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not get matching attestations")
|
||||
}
|
||||
|
||||
// Filter out source attestations by shard.
|
||||
for _, att := range matchedAtts.source {
|
||||
if att.Data.Crosslink.Shard == shard {
|
||||
shardAtts = append(shardAtts, att)
|
||||
}
|
||||
}
|
||||
var candidateCrosslinks []*ethpb.Crosslink
|
||||
// Filter out shard crosslinks with correct current or previous crosslink data.
|
||||
for _, a := range shardAtts {
|
||||
stateCrosslink := state.CurrentCrosslinks[shard]
|
||||
stateCrosslinkRoot, err := ssz.HashTreeRoot(stateCrosslink)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not hash tree root crosslink from state")
|
||||
}
|
||||
attCrosslinkRoot, err := ssz.HashTreeRoot(a.Data.Crosslink)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not hash tree root crosslink from attestation")
|
||||
}
|
||||
currCrosslinkMatches := bytes.Equal(stateCrosslinkRoot[:], attCrosslinkRoot[:])
|
||||
prevCrosslinkMatches := bytes.Equal(stateCrosslinkRoot[:], a.Data.Crosslink.ParentRoot)
|
||||
if currCrosslinkMatches || prevCrosslinkMatches {
|
||||
candidateCrosslinks = append(candidateCrosslinks, a.Data.Crosslink)
|
||||
}
|
||||
}
|
||||
|
||||
if len(candidateCrosslinks) == 0 {
|
||||
return ðpb.Crosslink{
|
||||
DataRoot: params.BeaconConfig().ZeroHash[:],
|
||||
ParentRoot: params.BeaconConfig().ZeroHash[:],
|
||||
}, nil, nil
|
||||
}
|
||||
var crosslinkAtts []*pb.PendingAttestation
|
||||
var winnerBalance uint64
|
||||
var winnerCrosslink *ethpb.Crosslink
|
||||
// Out of the existing shard crosslinks, pick the one that has the
|
||||
// most balance staked.
|
||||
crosslinkAtts = attsForCrosslink(candidateCrosslinks[0], shardAtts)
|
||||
winnerBalance, err = AttestingBalance(state, crosslinkAtts)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
winnerCrosslink = candidateCrosslinks[0]
|
||||
for _, c := range candidateCrosslinks {
|
||||
crosslinkAtts = attsForCrosslink(c, shardAtts)
|
||||
attestingBalance, err := AttestingBalance(state, crosslinkAtts)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not get crosslink's attesting balance")
|
||||
}
|
||||
if attestingBalance > winnerBalance {
|
||||
winnerCrosslink = c
|
||||
}
|
||||
}
|
||||
|
||||
crosslinkIndices, err := unslashedAttestingIndices(state, attsForCrosslink(winnerCrosslink, shardAtts))
|
||||
if err != nil {
|
||||
return nil, nil, errors.New("could not get crosslink indices")
|
||||
}
|
||||
|
||||
return winnerCrosslink, crosslinkIndices, nil
|
||||
}
|
||||
|
||||
// BaseReward takes state and validator index and calculate
|
||||
// individual validator's base reward quotient.
|
||||
//
|
||||
@@ -742,11 +569,7 @@ func BaseReward(state *pb.BeaconState, index uint64) (uint64, error) {
|
||||
// proposer_reward = Gwei(get_base_reward(state, index) // PROPOSER_REWARD_QUOTIENT)
|
||||
// rewards[attestation.proposer_index] += proposer_reward
|
||||
// max_attester_reward = get_base_reward(state, index) - proposer_reward
|
||||
// rewards[index] += Gwei(
|
||||
// max_attester_reward
|
||||
// * (SLOTS_PER_EPOCH + MIN_ATTESTATION_INCLUSION_DELAY - attestation.inclusion_delay)
|
||||
// // SLOTS_PER_EPOCH
|
||||
// )
|
||||
// rewards[index] += Gwei(max_attester_reward // attestation.inclusion_delay)
|
||||
//
|
||||
// # Inactivity penalty
|
||||
// finality_delay = previous_epoch - state.finalized_checkpoint.epoch
|
||||
@@ -845,8 +668,6 @@ func attestationDelta(state *pb.BeaconState) ([]uint64, []uint64, error) {
|
||||
}
|
||||
|
||||
for i, a := range attestersVotedSource {
|
||||
slotsPerEpoch := params.BeaconConfig().SlotsPerEpoch
|
||||
|
||||
baseReward, err := BaseReward(state, i)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not get proposer reward")
|
||||
@@ -854,7 +675,7 @@ func attestationDelta(state *pb.BeaconState) ([]uint64, []uint64, error) {
|
||||
proposerReward := baseReward / params.BeaconConfig().ProposerRewardQuotient
|
||||
rewards[a.ProposerIndex] += proposerReward
|
||||
attesterReward := baseReward - proposerReward
|
||||
rewards[i] += attesterReward * (slotsPerEpoch + params.BeaconConfig().MinAttestationInclusionDelay - a.InclusionDelay) / slotsPerEpoch
|
||||
rewards[i] += attesterReward / a.InclusionDelay
|
||||
}
|
||||
|
||||
// Apply penalties for quadratic leaks.
|
||||
@@ -882,88 +703,6 @@ func attestationDelta(state *pb.BeaconState) ([]uint64, []uint64, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return rewards, penalties, nil
|
||||
}
|
||||
|
||||
// crosslinkDelta calculates the rewards and penalties of individual
|
||||
// validator for submitting the correct crosslink.
|
||||
// Individual rewards and penalties are returned in list.
|
||||
//
|
||||
// Note: we calculated adjusted quotient outside of base reward because it's too inefficient
|
||||
// to repeat the same calculation for every validator versus just doing it once.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_crosslink_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]:
|
||||
// rewards = [0 for index in range(len(state.validator_registry))]
|
||||
// penalties = [0 for index in range(len(state.validator_registry))]
|
||||
// epoch = get_previous_epoch(state)
|
||||
// for offset in range(get_epoch_committee_count(state, epoch)):
|
||||
// shard = (get_epoch_start_shard(state, epoch) + offset) % SHARD_COUNT
|
||||
// crosslink_committee = get_crosslink_committee(state, epoch, shard)
|
||||
// winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices(state, epoch, shard)
|
||||
// attesting_balance = get_total_balance(state, attesting_indices)
|
||||
// committee_balance = get_total_balance(state, crosslink_committee)
|
||||
// for index in crosslink_committee:
|
||||
// base_reward = get_base_reward(state, index)
|
||||
// if index in attesting_indices:
|
||||
// rewards[index] += base_reward * attesting_balance // committee_balance
|
||||
// else:
|
||||
// penalties[index] += base_reward
|
||||
// return rewards, penalties
|
||||
func crosslinkDelta(state *pb.BeaconState) ([]uint64, []uint64, error) {
|
||||
rewards := make([]uint64, len(state.Validators))
|
||||
penalties := make([]uint64, len(state.Validators))
|
||||
epoch := helpers.PrevEpoch(state)
|
||||
count, err := helpers.CommitteeCount(state, epoch)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not get epoch committee count")
|
||||
}
|
||||
startShard, err := helpers.StartShard(state, epoch)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not get epoch start shard")
|
||||
}
|
||||
for i := uint64(0); i < count; i++ {
|
||||
shard := (startShard + i) % params.BeaconConfig().ShardCount
|
||||
committee, err := helpers.CrosslinkCommittee(state, epoch, shard)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not get crosslink's committee")
|
||||
}
|
||||
_, attestingIndices, err := WinningCrosslink(state, shard, epoch)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not get winning crosslink")
|
||||
}
|
||||
|
||||
attested := make(map[uint64]bool)
|
||||
// Construct a map to look up validators that voted for crosslink.
|
||||
for _, index := range attestingIndices {
|
||||
attested[index] = true
|
||||
}
|
||||
committeeBalance := helpers.TotalBalance(state, committee)
|
||||
attestingBalance := helpers.TotalBalance(state, attestingIndices)
|
||||
|
||||
for _, index := range committee {
|
||||
base, err := BaseReward(state, index)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not get base reward")
|
||||
}
|
||||
if _, ok := attested[index]; ok {
|
||||
rewards[index] += base * attestingBalance / committeeBalance
|
||||
} else {
|
||||
penalties[index] += base
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rewards, penalties, nil
|
||||
}
|
||||
|
||||
// attsForCrosslink returns the attestations of the input crosslink.
|
||||
func attsForCrosslink(crosslink *ethpb.Crosslink, atts []*pb.PendingAttestation) []*pb.PendingAttestation {
|
||||
var crosslinkAtts []*pb.PendingAttestation
|
||||
for _, a := range atts {
|
||||
if proto.Equal(a.Data.Crosslink, crosslink) {
|
||||
crosslinkAtts = append(crosslinkAtts, a)
|
||||
}
|
||||
}
|
||||
return crosslinkAtts
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package epoch
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -16,8 +15,6 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
helpers.ClearShuffledValidatorCache()
|
||||
|
||||
// TODO(2312): remove this and use the mainnet count.
|
||||
c := params.BeaconConfig()
|
||||
c.MinGenesisActiveValidatorCount = 16384
|
||||
@@ -31,9 +28,6 @@ func TestUnslashedAttestingIndices_CanSortAndFilter(t *testing.T) {
|
||||
atts[i] = &pb.PendingAttestation{
|
||||
Data: ðpb.AttestationData{Source: ðpb.Checkpoint{},
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: uint64(i),
|
||||
},
|
||||
},
|
||||
AggregationBits: bitfield.Bitlist{0xFF, 0xFF, 0xFF},
|
||||
}
|
||||
@@ -48,9 +42,8 @@ func TestUnslashedAttestingIndices_CanSortAndFilter(t *testing.T) {
|
||||
}
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
}
|
||||
|
||||
indices, err := unslashedAttestingIndices(state, atts)
|
||||
@@ -83,9 +76,7 @@ func TestUnslashedAttestingIndices_DuplicatedAttestations(t *testing.T) {
|
||||
for i := 0; i < len(atts); i++ {
|
||||
atts[i] = &pb.PendingAttestation{
|
||||
Data: ðpb.AttestationData{Source: ðpb.Checkpoint{},
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{},
|
||||
},
|
||||
Target: ðpb.Checkpoint{Epoch: 0}},
|
||||
AggregationBits: bitfield.Bitlist{0xFF, 0xFF, 0xFF},
|
||||
}
|
||||
}
|
||||
@@ -99,9 +90,8 @@ func TestUnslashedAttestingIndices_DuplicatedAttestations(t *testing.T) {
|
||||
}
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
}
|
||||
|
||||
indices, err := unslashedAttestingIndices(state, atts)
|
||||
@@ -124,11 +114,9 @@ func TestAttestingBalance_CorrectBalance(t *testing.T) {
|
||||
for i := 0; i < len(atts); i++ {
|
||||
atts[i] = &pb.PendingAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: uint64(i),
|
||||
},
|
||||
Target: ðpb.Checkpoint{},
|
||||
Source: ðpb.Checkpoint{},
|
||||
Slot: uint64(i),
|
||||
},
|
||||
AggregationBits: bitfield.Bitlist{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01},
|
||||
@@ -146,11 +134,11 @@ func TestAttestingBalance_CorrectBalance(t *testing.T) {
|
||||
balances[i] = params.BeaconConfig().MaxEffectiveBalance
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
Slot: 0,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
Validators: validators,
|
||||
Balances: balances,
|
||||
Slot: 2,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
|
||||
Validators: validators,
|
||||
Balances: balances,
|
||||
}
|
||||
|
||||
balance, err := AttestingBalance(state, atts)
|
||||
@@ -172,20 +160,20 @@ func TestMatchAttestations_PrevEpoch(t *testing.T) {
|
||||
// The correct vote for target is '1'
|
||||
// The correct vote for head is '2'
|
||||
prevAtts := []*pb.PendingAttestation{
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, Target: ðpb.Checkpoint{}}}, // source
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, Target: ðpb.Checkpoint{Root: []byte{1}}}}, // source, target
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, Target: ðpb.Checkpoint{Root: []byte{3}}}}, // source
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, Target: ðpb.Checkpoint{Root: []byte{1}}}}, // source, target
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: ðpb.Checkpoint{}}}, // source, head
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{4}, Target: ðpb.Checkpoint{}}}, // source
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: ðpb.Checkpoint{Root: []byte{1}}}}, // source, target, head
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{5}, Target: ðpb.Checkpoint{Root: []byte{1}}}}, // source, target
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: ðpb.Checkpoint{Root: []byte{6}}}}, // source, head
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Target: ðpb.Checkpoint{}}}, // source
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Target: ðpb.Checkpoint{Root: []byte{1}}}}, // source, target
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Target: ðpb.Checkpoint{Root: []byte{3}}}}, // source
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Target: ðpb.Checkpoint{Root: []byte{1}}}}, // source, target
|
||||
{Data: ðpb.AttestationData{Slot: 33, Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: ðpb.Checkpoint{}}}, // source, head
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{4}, Target: ðpb.Checkpoint{}}}, // source
|
||||
{Data: ðpb.AttestationData{Slot: 33, Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: ðpb.Checkpoint{Root: []byte{1}}}}, // source, target, head
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{5}, Target: ðpb.Checkpoint{Root: []byte{1}}}}, // source, target
|
||||
{Data: ðpb.AttestationData{Slot: 33, Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: ðpb.Checkpoint{Root: []byte{6}}}}, // source, head
|
||||
}
|
||||
|
||||
currentAtts := []*pb.PendingAttestation{
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + e + 1}, Target: ðpb.Checkpoint{}}}, // none
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + e + 1}, BeaconBlockRoot: []byte{2}, Target: ðpb.Checkpoint{Root: []byte{1}}}}, // none
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Target: ðpb.Checkpoint{}}}, // none
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{2}, Target: ðpb.Checkpoint{Root: []byte{1}}}}, // none
|
||||
}
|
||||
|
||||
blockRoots := make([][]byte, 128)
|
||||
@@ -198,7 +186,6 @@ func TestMatchAttestations_PrevEpoch(t *testing.T) {
|
||||
PreviousEpochAttestations: prevAtts,
|
||||
BlockRoots: blockRoots,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
}
|
||||
|
||||
mAtts, err := MatchAttestations(state, 0)
|
||||
@@ -207,35 +194,36 @@ func TestMatchAttestations_PrevEpoch(t *testing.T) {
|
||||
}
|
||||
|
||||
wantedSrcAtts := []*pb.PendingAttestation{
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, Target: ðpb.Checkpoint{}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, Target: ðpb.Checkpoint{Root: []byte{1}}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, Target: ðpb.Checkpoint{Root: []byte{3}}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, Target: ðpb.Checkpoint{Root: []byte{1}}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: ðpb.Checkpoint{}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{4}, Target: ðpb.Checkpoint{}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: ðpb.Checkpoint{Root: []byte{1}}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{5}, Target: ðpb.Checkpoint{Root: []byte{1}}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: ðpb.Checkpoint{Root: []byte{6}}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Target: ðpb.Checkpoint{}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Target: ðpb.Checkpoint{Root: []byte{1}}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Target: ðpb.Checkpoint{Root: []byte{3}}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Target: ðpb.Checkpoint{Root: []byte{1}}}},
|
||||
{Data: ðpb.AttestationData{Slot: 33, Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: ðpb.Checkpoint{}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{4}, Target: ðpb.Checkpoint{}}},
|
||||
{Data: ðpb.AttestationData{Slot: 33, Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: ðpb.Checkpoint{Root: []byte{1}}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{5}, Target: ðpb.Checkpoint{Root: []byte{1}}}},
|
||||
{Data: ðpb.AttestationData{Slot: 33, Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: ðpb.Checkpoint{Root: []byte{6}}}},
|
||||
}
|
||||
if !reflect.DeepEqual(mAtts.source, wantedSrcAtts) {
|
||||
t.Error("source attestations don't match")
|
||||
}
|
||||
|
||||
wantedTgtAtts := []*pb.PendingAttestation{
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, Target: ðpb.Checkpoint{Root: []byte{1}}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, Target: ðpb.Checkpoint{Root: []byte{1}}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: ðpb.Checkpoint{Root: []byte{1}}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{5}, Target: ðpb.Checkpoint{Root: []byte{1}}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Target: ðpb.Checkpoint{Root: []byte{1}}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Target: ðpb.Checkpoint{Root: []byte{1}}}},
|
||||
{Data: ðpb.AttestationData{Slot: 33, Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: ðpb.Checkpoint{Root: []byte{1}}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{5}, Target: ðpb.Checkpoint{Root: []byte{1}}}},
|
||||
}
|
||||
if !reflect.DeepEqual(mAtts.Target, wantedTgtAtts) {
|
||||
t.Error("target attestations don't match")
|
||||
}
|
||||
|
||||
wantedHeadAtts := []*pb.PendingAttestation{
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: ðpb.Checkpoint{}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: ðpb.Checkpoint{Root: []byte{1}}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: ðpb.Checkpoint{Root: []byte{6}}}},
|
||||
{Data: ðpb.AttestationData{Slot: 33, Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: ðpb.Checkpoint{}}},
|
||||
{Data: ðpb.AttestationData{Slot: 33, Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: ðpb.Checkpoint{Root: []byte{1}}}},
|
||||
{Data: ðpb.AttestationData{Slot: 33, Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: ðpb.Checkpoint{Root: []byte{6}}}},
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(mAtts.head, wantedHeadAtts) {
|
||||
t.Error("head attestations don't match")
|
||||
}
|
||||
@@ -247,20 +235,20 @@ func TestMatchAttestations_CurrentEpoch(t *testing.T) {
|
||||
s := uint64(0) // slot
|
||||
|
||||
// The correct epoch for source is the first epoch
|
||||
// The correct vote for target is '65'
|
||||
// The correct vote for head is '66'
|
||||
// The correct vote for target is '33'
|
||||
// The correct vote for head is '34'
|
||||
prevAtts := []*pb.PendingAttestation{
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, Target: ðpb.Checkpoint{}}}, // none
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{2}, Target: ðpb.Checkpoint{Root: []byte{1}}}}, // none
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{5}, Target: ðpb.Checkpoint{Root: []byte{1}}}}, // none
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{2}, Target: ðpb.Checkpoint{Root: []byte{6}}}}, // none
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Target: ðpb.Checkpoint{}}}, // none
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{2}, Target: ðpb.Checkpoint{Root: []byte{1}}}}, // none
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{5}, Target: ðpb.Checkpoint{Root: []byte{1}}}}, // none
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{2}, Target: ðpb.Checkpoint{Root: []byte{6}}}}, // none
|
||||
}
|
||||
|
||||
currentAtts := []*pb.PendingAttestation{
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, Target: ðpb.Checkpoint{}}}, // source
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: ðpb.Checkpoint{Root: []byte{65}}}}, // source, target, head
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{69}, Target: ðpb.Checkpoint{Root: []byte{65}}}}, // source, target
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: ðpb.Checkpoint{Root: []byte{68}}}}, // source, head
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Target: ðpb.Checkpoint{}}}, // source
|
||||
{Data: ðpb.AttestationData{Slot: 33, Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: ðpb.Checkpoint{Root: []byte{33}}}}, // source, target, head
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{69}, Target: ðpb.Checkpoint{Root: []byte{33}}}}, // source, target
|
||||
{Data: ðpb.AttestationData{Slot: 33, Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: ðpb.Checkpoint{Root: []byte{68}}}}, // source, head
|
||||
}
|
||||
|
||||
blockRoots := make([][]byte, 128)
|
||||
@@ -280,26 +268,26 @@ func TestMatchAttestations_CurrentEpoch(t *testing.T) {
|
||||
}
|
||||
|
||||
wantedSrcAtts := []*pb.PendingAttestation{
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, Target: ðpb.Checkpoint{}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: ðpb.Checkpoint{Root: []byte{65}}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{69}, Target: ðpb.Checkpoint{Root: []byte{65}}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: ðpb.Checkpoint{Root: []byte{68}}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Target: ðpb.Checkpoint{}}},
|
||||
{Data: ðpb.AttestationData{Slot: 33, Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: ðpb.Checkpoint{Root: []byte{33}}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{69}, Target: ðpb.Checkpoint{Root: []byte{33}}}},
|
||||
{Data: ðpb.AttestationData{Slot: 33, Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: ðpb.Checkpoint{Root: []byte{68}}}},
|
||||
}
|
||||
if !reflect.DeepEqual(mAtts.source, wantedSrcAtts) {
|
||||
t.Error("source attestations don't match")
|
||||
}
|
||||
|
||||
wantedTgtAtts := []*pb.PendingAttestation{
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: ðpb.Checkpoint{Root: []byte{65}}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{69}, Target: ðpb.Checkpoint{Root: []byte{65}}}},
|
||||
{Data: ðpb.AttestationData{Slot: 33, Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: ðpb.Checkpoint{Root: []byte{33}}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{69}, Target: ðpb.Checkpoint{Root: []byte{33}}}},
|
||||
}
|
||||
if !reflect.DeepEqual(mAtts.Target, wantedTgtAtts) {
|
||||
t.Error("target attestations don't match")
|
||||
}
|
||||
|
||||
wantedHeadAtts := []*pb.PendingAttestation{
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: ðpb.Checkpoint{Root: []byte{65}}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{Shard: s + 1}, BeaconBlockRoot: []byte{66}, Target: ðpb.Checkpoint{Root: []byte{68}}}},
|
||||
{Data: ðpb.AttestationData{Slot: 33, Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: ðpb.Checkpoint{Root: []byte{33}}}},
|
||||
{Data: ðpb.AttestationData{Slot: 33, Source: ðpb.Checkpoint{}, BeaconBlockRoot: []byte{34}, Target: ðpb.Checkpoint{Root: []byte{68}}}},
|
||||
}
|
||||
if !reflect.DeepEqual(mAtts.head, wantedHeadAtts) {
|
||||
t.Error("head attestations don't match")
|
||||
@@ -313,245 +301,6 @@ func TestMatchAttestations_EpochOutOfBound(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttsForCrosslink_CanGetAttestations(t *testing.T) {
|
||||
c := ðpb.Crosslink{
|
||||
DataRoot: []byte{'B'},
|
||||
}
|
||||
atts := []*pb.PendingAttestation{
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{DataRoot: []byte{'A'}}, Target: ðpb.Checkpoint{}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{DataRoot: []byte{'B'}}, Target: ðpb.Checkpoint{}}}, // Selected
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{DataRoot: []byte{'C'}}, Target: ðpb.Checkpoint{}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{DataRoot: []byte{'B'}}, Target: ðpb.Checkpoint{}}}} // Selected
|
||||
|
||||
if !reflect.DeepEqual(attsForCrosslink(c, atts), []*pb.PendingAttestation{
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{DataRoot: []byte{'B'}}, Target: ðpb.Checkpoint{}}},
|
||||
{Data: ðpb.AttestationData{Source: ðpb.Checkpoint{}, Crosslink: ðpb.Crosslink{DataRoot: []byte{'B'}}, Target: ðpb.Checkpoint{}}}}) {
|
||||
t.Error("Incorrect attestations for crosslink")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWinningCrosslink_CantGetMatchingAtts(t *testing.T) {
|
||||
wanted := fmt.Sprintf("could not get matching attestations: input epoch: %d != current epoch: %d or previous epoch: %d",
|
||||
100, 0, 0)
|
||||
_, _, err := WinningCrosslink(&pb.BeaconState{Slot: 0}, 0, 100)
|
||||
if err.Error() != wanted {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWinningCrosslink_ReturnGenesisCrosslink(t *testing.T) {
|
||||
e := params.BeaconConfig().SlotsPerEpoch
|
||||
gs := uint64(0) // genesis slot
|
||||
ge := uint64(0) // genesis epoch
|
||||
|
||||
state := &pb.BeaconState{
|
||||
Slot: gs + e + 2,
|
||||
PreviousEpochAttestations: []*pb.PendingAttestation{},
|
||||
BlockRoots: make([][]byte, 128),
|
||||
CurrentCrosslinks: []*ethpb.Crosslink{{StartEpoch: ge}},
|
||||
}
|
||||
|
||||
gCrosslink := ðpb.Crosslink{
|
||||
StartEpoch: 0,
|
||||
DataRoot: params.BeaconConfig().ZeroHash[:],
|
||||
ParentRoot: params.BeaconConfig().ZeroHash[:],
|
||||
}
|
||||
|
||||
crosslink, indices, err := WinningCrosslink(state, 0, ge)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(indices) != 0 {
|
||||
t.Errorf("genesis crosslink indices is not 0, got: %d", len(indices))
|
||||
}
|
||||
if !reflect.DeepEqual(crosslink, gCrosslink) {
|
||||
t.Errorf("Did not get genesis crosslink, got: %v", crosslink)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWinningCrosslink_CanGetWinningRoot(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
e := params.BeaconConfig().SlotsPerEpoch
|
||||
gs := uint64(0) // genesis slot
|
||||
ge := uint64(0) // genesis epoch
|
||||
|
||||
atts := []*pb.PendingAttestation{
|
||||
{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 1,
|
||||
DataRoot: []byte{'A'},
|
||||
},
|
||||
Target: ðpb.Checkpoint{},
|
||||
Source: ðpb.Checkpoint{},
|
||||
},
|
||||
},
|
||||
{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 1,
|
||||
DataRoot: []byte{'B'}, // Winner
|
||||
},
|
||||
Target: ðpb.Checkpoint{},
|
||||
Source: ðpb.Checkpoint{},
|
||||
},
|
||||
},
|
||||
{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 1,
|
||||
DataRoot: []byte{'C'},
|
||||
},
|
||||
Target: ðpb.Checkpoint{},
|
||||
Source: ðpb.Checkpoint{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
blockRoots := make([][]byte, 128)
|
||||
for i := 0; i < len(blockRoots); i++ {
|
||||
blockRoots[i] = []byte{byte(i + 1)}
|
||||
}
|
||||
|
||||
crosslinks := make([]*ethpb.Crosslink, params.BeaconConfig().ShardCount)
|
||||
for i := uint64(0); i < params.BeaconConfig().ShardCount; i++ {
|
||||
crosslinks[i] = ðpb.Crosslink{
|
||||
StartEpoch: ge,
|
||||
Shard: 1,
|
||||
DataRoot: []byte{'B'},
|
||||
}
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
Slot: gs + e + 2,
|
||||
PreviousEpochAttestations: atts,
|
||||
BlockRoots: blockRoots,
|
||||
CurrentCrosslinks: crosslinks,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
}
|
||||
|
||||
winner, indices, err := WinningCrosslink(state, 1, ge)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(indices) != 0 {
|
||||
t.Errorf("genesis crosslink indices is not 0, got: %d", len(indices))
|
||||
}
|
||||
want := ðpb.Crosslink{StartEpoch: ge, Shard: 1, DataRoot: []byte{'B'}}
|
||||
if !reflect.DeepEqual(winner, want) {
|
||||
t.Errorf("Did not get wanted crosslink, got: %v, want %v", winner, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessCrosslinks_NoUpdate(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
|
||||
validatorCount := 128
|
||||
validators := make([]*ethpb.Validator, validatorCount)
|
||||
balances := make([]uint64, validatorCount)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = ðpb.Validator{
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
||||
}
|
||||
balances[i] = params.BeaconConfig().MaxEffectiveBalance
|
||||
}
|
||||
blockRoots := make([][]byte, 128)
|
||||
for i := 0; i < len(blockRoots); i++ {
|
||||
blockRoots[i] = []byte{byte(i + 1)}
|
||||
}
|
||||
|
||||
var crosslinks []*ethpb.Crosslink
|
||||
for i := uint64(0); i < params.BeaconConfig().ShardCount; i++ {
|
||||
crosslinks = append(crosslinks, ðpb.Crosslink{
|
||||
StartEpoch: 0,
|
||||
DataRoot: []byte{'A'},
|
||||
})
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
Slot: params.BeaconConfig().SlotsPerEpoch + 1,
|
||||
Validators: validators,
|
||||
Balances: balances,
|
||||
BlockRoots: blockRoots,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
CurrentCrosslinks: crosslinks,
|
||||
}
|
||||
newState, err := ProcessCrosslinks(state)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
wanted := ðpb.Crosslink{
|
||||
StartEpoch: 0,
|
||||
DataRoot: []byte{'A'},
|
||||
}
|
||||
// Since there has been no attestation, crosslink stayed the same.
|
||||
if !reflect.DeepEqual(wanted, newState.CurrentCrosslinks[0]) {
|
||||
t.Errorf("Did not get correct crosslink back")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessCrosslinks_SuccessfulUpdate(t *testing.T) {
|
||||
e := params.BeaconConfig().SlotsPerEpoch
|
||||
gs := uint64(0) // genesis slot
|
||||
ge := uint64(0) // genesis epoch
|
||||
|
||||
validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount/8)
|
||||
balances := make([]uint64, params.BeaconConfig().MinGenesisActiveValidatorCount/8)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = ðpb.Validator{
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
||||
}
|
||||
balances[i] = params.BeaconConfig().MaxEffectiveBalance
|
||||
}
|
||||
blockRoots := make([][]byte, 128)
|
||||
for i := 0; i < len(blockRoots); i++ {
|
||||
blockRoots[i] = []byte{byte(i + 1)}
|
||||
}
|
||||
|
||||
crosslinks := make([]*ethpb.Crosslink, params.BeaconConfig().ShardCount)
|
||||
for i := uint64(0); i < params.BeaconConfig().ShardCount; i++ {
|
||||
crosslinks[i] = ðpb.Crosslink{
|
||||
StartEpoch: ge,
|
||||
DataRoot: []byte{'B'},
|
||||
}
|
||||
}
|
||||
var atts []*pb.PendingAttestation
|
||||
startShard := uint64(960)
|
||||
for s := uint64(0); s < params.BeaconConfig().SlotsPerEpoch; s++ {
|
||||
atts = append(atts, &pb.PendingAttestation{
|
||||
Data: ðpb.AttestationData{Source: ðpb.Checkpoint{},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: startShard + s,
|
||||
DataRoot: []byte{'B'},
|
||||
},
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
},
|
||||
AggregationBits: bitfield.Bitlist{0xC0, 0xC0, 0xC0, 0xC0, 0x01},
|
||||
})
|
||||
}
|
||||
state := &pb.BeaconState{
|
||||
Slot: gs + e + 2,
|
||||
Validators: validators,
|
||||
PreviousEpochAttestations: atts,
|
||||
Balances: balances,
|
||||
BlockRoots: blockRoots,
|
||||
CurrentCrosslinks: crosslinks,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
}
|
||||
newState, err := ProcessCrosslinks(state)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(crosslinks[0], newState.CurrentCrosslinks[0]) {
|
||||
t.Errorf("Crosslink is not the same")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBaseReward_AccurateRewards(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
|
||||
@@ -560,10 +309,10 @@ func TestBaseReward_AccurateRewards(t *testing.T) {
|
||||
b uint64
|
||||
c uint64
|
||||
}{
|
||||
{params.BeaconConfig().MinDepositAmount, params.BeaconConfig().MinDepositAmount, 404781},
|
||||
{30 * 1e9, 30 * 1e9, 2217026},
|
||||
{params.BeaconConfig().MaxEffectiveBalance, params.BeaconConfig().MaxEffectiveBalance, 2289739},
|
||||
{40 * 1e9, params.BeaconConfig().MaxEffectiveBalance, 2289739},
|
||||
{params.BeaconConfig().MinDepositAmount, params.BeaconConfig().MinDepositAmount, 505976},
|
||||
{30 * 1e9, 30 * 1e9, 2771282},
|
||||
{params.BeaconConfig().MaxEffectiveBalance, params.BeaconConfig().MaxEffectiveBalance, 2862174},
|
||||
{40 * 1e9, params.BeaconConfig().MaxEffectiveBalance, 2862174},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
helpers.ClearAllCaches()
|
||||
@@ -669,9 +418,9 @@ func TestProcessJustificationAndFinalization_ConsecutiveEpochs(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(128)}) {
|
||||
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(64)}) {
|
||||
t.Errorf("Wanted current justified root: %v, got: %v",
|
||||
[]byte{byte(128)}, newState.CurrentJustifiedCheckpoint.Root)
|
||||
[]byte{byte(64)}, newState.CurrentJustifiedCheckpoint.Root)
|
||||
}
|
||||
if newState.CurrentJustifiedCheckpoint.Epoch != 2 {
|
||||
t.Errorf("Wanted justified epoch: %d, got: %d",
|
||||
@@ -718,9 +467,9 @@ func TestProcessJustificationAndFinalization_JustifyCurrentEpoch(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(128)}) {
|
||||
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(64)}) {
|
||||
t.Errorf("Wanted current justified root: %v, got: %v",
|
||||
[]byte{byte(128)}, newState.CurrentJustifiedCheckpoint.Root)
|
||||
[]byte{byte(64)}, newState.CurrentJustifiedCheckpoint.Root)
|
||||
}
|
||||
if newState.CurrentJustifiedCheckpoint.Epoch != 2 {
|
||||
t.Errorf("Wanted justified epoch: %d, got: %d",
|
||||
@@ -767,9 +516,9 @@ func TestProcessJustificationAndFinalization_JustifyPrevEpoch(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(128)}) {
|
||||
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(64)}) {
|
||||
t.Errorf("Wanted current justified root: %v, got: %v",
|
||||
[]byte{byte(128)}, newState.CurrentJustifiedCheckpoint.Root)
|
||||
[]byte{byte(64)}, newState.CurrentJustifiedCheckpoint.Root)
|
||||
}
|
||||
if newState.PreviousJustifiedCheckpoint.Epoch != 0 {
|
||||
t.Errorf("Wanted previous justified epoch: %d, got: %d",
|
||||
@@ -913,17 +662,6 @@ func TestProcessFinalUpdates_CanProcess(t *testing.T) {
|
||||
t.Errorf("effective balance incorrectly updated, got %d", s.Validators[0].EffectiveBalance)
|
||||
}
|
||||
|
||||
// Verify start shard is correctly updated.
|
||||
if newS.StartShard != 64 {
|
||||
t.Errorf("start shard incorrectly updated, got %d", 64)
|
||||
}
|
||||
|
||||
// Verify latest active index root is correctly updated in the right position.
|
||||
pos := (ne + params.BeaconConfig().ActivationExitDelay) % params.BeaconConfig().EpochsPerHistoricalVector
|
||||
if bytes.Equal(newS.ActiveIndexRoots[pos], params.BeaconConfig().ZeroHash[:]) {
|
||||
t.Error("latest active index roots still zero hashes")
|
||||
}
|
||||
|
||||
// Verify slashed balances correctly updated.
|
||||
if newS.Slashings[ce] != newS.Slashings[ne] {
|
||||
t.Errorf("wanted slashed balance %d, got %d",
|
||||
@@ -946,39 +684,12 @@ func TestProcessFinalUpdates_CanProcess(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrosslinkDelta_NoOneAttested(t *testing.T) {
|
||||
e := params.BeaconConfig().SlotsPerEpoch
|
||||
|
||||
validatorCount := uint64(128)
|
||||
state := buildState(e+2, validatorCount)
|
||||
|
||||
rewards, penalties, err := crosslinkDelta(state)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i := uint64(0); i < validatorCount; i++ {
|
||||
// Since no one attested, all the validators should gain 0 reward
|
||||
if rewards[i] != 0 {
|
||||
t.Errorf("Wanted reward balance 0, got %d", rewards[i])
|
||||
}
|
||||
// Since no one attested, all the validators should get penalized the same
|
||||
base, err := BaseReward(state, i)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if penalties[i] != base {
|
||||
t.Errorf("Wanted penalty balance %d, got %d",
|
||||
base, penalties[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessRegistryUpdates_NoRotation(t *testing.T) {
|
||||
state := &pb.BeaconState{
|
||||
Slot: 5 * params.BeaconConfig().SlotsPerEpoch,
|
||||
Validators: []*ethpb.Validator{
|
||||
{ExitEpoch: params.BeaconConfig().ActivationExitDelay},
|
||||
{ExitEpoch: params.BeaconConfig().ActivationExitDelay},
|
||||
{ExitEpoch: params.BeaconConfig().MaxSeedLookhead},
|
||||
{ExitEpoch: params.BeaconConfig().MaxSeedLookhead},
|
||||
},
|
||||
Balances: []uint64{
|
||||
params.BeaconConfig().MaxEffectiveBalance,
|
||||
@@ -991,88 +702,13 @@ func TestProcessRegistryUpdates_NoRotation(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i, validator := range newState.Validators {
|
||||
if validator.ExitEpoch != params.BeaconConfig().ActivationExitDelay {
|
||||
if validator.ExitEpoch != params.BeaconConfig().MaxSeedLookhead {
|
||||
t.Errorf("Could not update registry %d, wanted exit slot %d got %d",
|
||||
i, params.BeaconConfig().ActivationExitDelay, validator.ExitEpoch)
|
||||
i, params.BeaconConfig().MaxSeedLookhead, validator.ExitEpoch)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrosslinkDelta_SomeAttested(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
e := params.BeaconConfig().SlotsPerEpoch
|
||||
helpers.ClearShuffledValidatorCache()
|
||||
state := buildState(e+2, params.BeaconConfig().MinGenesisActiveValidatorCount/8)
|
||||
startShard := uint64(960)
|
||||
atts := make([]*pb.PendingAttestation, 2)
|
||||
for i := 0; i < len(atts); i++ {
|
||||
atts[i] = &pb.PendingAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: startShard + uint64(i),
|
||||
DataRoot: []byte{'A'},
|
||||
},
|
||||
Target: ðpb.Checkpoint{},
|
||||
Source: ðpb.Checkpoint{},
|
||||
},
|
||||
InclusionDelay: uint64(i + 100),
|
||||
AggregationBits: bitfield.Bitlist{0xC0, 0xC0, 0xC0, 0xC0, 0x01},
|
||||
}
|
||||
}
|
||||
state.PreviousEpochAttestations = atts
|
||||
state.CurrentCrosslinks[startShard] = ðpb.Crosslink{
|
||||
DataRoot: []byte{'A'}, Shard: startShard,
|
||||
}
|
||||
state.CurrentCrosslinks[startShard+1] = ðpb.Crosslink{
|
||||
DataRoot: []byte{'A'}, Shard: startShard + 1,
|
||||
}
|
||||
|
||||
rewards, penalties, err := crosslinkDelta(state)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
attestedIndices := []uint64{5, 16, 336, 797, 1082, 1450, 1770, 1958}
|
||||
for _, i := range attestedIndices {
|
||||
// Since all these validators attested, they should get the same rewards.
|
||||
want := uint64(12649)
|
||||
if rewards[i] != want {
|
||||
t.Errorf("Wanted reward balance %d, got %d", want, rewards[i])
|
||||
}
|
||||
// Since all these validators attested, they shouldn't get penalized.
|
||||
if penalties[i] != 0 {
|
||||
t.Errorf("Wanted penalty balance 0, got %d", penalties[i])
|
||||
}
|
||||
}
|
||||
|
||||
nonAttestedIndices := []uint64{12, 23, 45, 79}
|
||||
for _, i := range nonAttestedIndices {
|
||||
base, err := BaseReward(state, i)
|
||||
if err != nil {
|
||||
t.Errorf("Could not get base reward: %v", err)
|
||||
}
|
||||
wanted := base
|
||||
// Since all these validators did not attest, they shouldn't get rewarded.
|
||||
if rewards[i] != 0 {
|
||||
t.Errorf("Wanted reward balance 0, got %d", rewards[i])
|
||||
}
|
||||
// Base penalties for not attesting.
|
||||
if penalties[i] != wanted {
|
||||
t.Errorf("Wanted penalty balance %d, got %d", wanted, penalties[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrosslinkDelta_CantGetWinningCrosslink(t *testing.T) {
|
||||
state := buildState(0, 1)
|
||||
|
||||
_, _, err := crosslinkDelta(state)
|
||||
wanted := "could not get winning crosslink: could not get matching attestations"
|
||||
if !strings.Contains(err.Error(), wanted) {
|
||||
t.Fatalf("Got: %v, want: %v", err.Error(), wanted)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttestationDelta_CantGetBlockRoot(t *testing.T) {
|
||||
e := params.BeaconConfig().SlotsPerEpoch
|
||||
|
||||
@@ -1096,33 +732,6 @@ func TestAttestationDelta_CantGetAttestation(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttestationDelta_CantGetAttestationIndices(t *testing.T) {
|
||||
e := params.BeaconConfig().SlotsPerEpoch
|
||||
|
||||
state := buildState(e+2, 1)
|
||||
atts := make([]*pb.PendingAttestation, 2)
|
||||
for i := 0; i < len(atts); i++ {
|
||||
atts[i] = &pb.PendingAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: uint64(i),
|
||||
},
|
||||
Target: ðpb.Checkpoint{},
|
||||
Source: ðpb.Checkpoint{},
|
||||
},
|
||||
InclusionDelay: uint64(i + 100),
|
||||
AggregationBits: bitfield.Bitlist{0xFF, 0x01},
|
||||
}
|
||||
}
|
||||
state.PreviousEpochAttestations = atts
|
||||
|
||||
_, _, err := attestationDelta(state)
|
||||
wanted := "could not get attestation indices"
|
||||
if !strings.Contains(err.Error(), wanted) {
|
||||
t.Fatalf("Got: %v, want: %v", err.Error(), wanted)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttestationDelta_NoOneAttested(t *testing.T) {
|
||||
e := params.BeaconConfig().SlotsPerEpoch
|
||||
validatorCount := params.BeaconConfig().MinGenesisActiveValidatorCount / 32
|
||||
@@ -1132,10 +741,6 @@ func TestAttestationDelta_NoOneAttested(t *testing.T) {
|
||||
for i := 0; i < len(atts); i++ {
|
||||
atts[i] = &pb.PendingAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: uint64(i),
|
||||
DataRoot: []byte{'A'},
|
||||
},
|
||||
Target: ðpb.Checkpoint{},
|
||||
Source: ðpb.Checkpoint{},
|
||||
},
|
||||
@@ -1172,15 +777,10 @@ func TestAttestationDelta_SomeAttested(t *testing.T) {
|
||||
e := params.BeaconConfig().SlotsPerEpoch
|
||||
validatorCount := params.BeaconConfig().MinGenesisActiveValidatorCount / 8
|
||||
state := buildState(e+2, validatorCount)
|
||||
startShard := uint64(960)
|
||||
atts := make([]*pb.PendingAttestation, 3)
|
||||
for i := 0; i < len(atts); i++ {
|
||||
atts[i] = &pb.PendingAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: startShard + uint64(i),
|
||||
DataRoot: []byte{'A'},
|
||||
},
|
||||
Target: ðpb.Checkpoint{},
|
||||
Source: ðpb.Checkpoint{},
|
||||
},
|
||||
@@ -1189,18 +789,11 @@ func TestAttestationDelta_SomeAttested(t *testing.T) {
|
||||
}
|
||||
}
|
||||
state.PreviousEpochAttestations = atts
|
||||
state.CurrentCrosslinks[startShard] = ðpb.Crosslink{
|
||||
DataRoot: []byte{'A'},
|
||||
}
|
||||
state.CurrentCrosslinks[startShard+1] = ðpb.Crosslink{
|
||||
DataRoot: []byte{'A'},
|
||||
}
|
||||
|
||||
rewards, penalties, err := attestationDelta(state)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
attestedBalance, err := AttestingBalance(state, atts)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -1210,18 +803,20 @@ func TestAttestationDelta_SomeAttested(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
attestedIndices := []uint64{5, 754, 797, 1637, 1770, 1862, 1192}
|
||||
attestedIndices := []uint64{100, 106, 196, 641, 654, 1606}
|
||||
for _, i := range attestedIndices {
|
||||
base, err := BaseReward(state, i)
|
||||
if err != nil {
|
||||
t.Errorf("Could not get base reward: %v", err)
|
||||
}
|
||||
|
||||
// Base rewards for getting source right
|
||||
wanted := 3 * (base * attestedBalance / totalBalance)
|
||||
// Base rewards for proposer and attesters working together getting attestation
|
||||
// on chain in the fatest manner
|
||||
// on chain in the fastest manner
|
||||
proposerReward := base / params.BeaconConfig().ProposerRewardQuotient
|
||||
wanted += (base - proposerReward) * params.BeaconConfig().MinAttestationInclusionDelay
|
||||
wanted += (base - proposerReward) / params.BeaconConfig().MinAttestationInclusionDelay
|
||||
|
||||
if rewards[i] != wanted {
|
||||
t.Errorf("Wanted reward balance %d, got %d", wanted, rewards[i])
|
||||
}
|
||||
@@ -1254,15 +849,10 @@ func TestAttestationDelta_SomeAttestedFinalityDelay(t *testing.T) {
|
||||
e := params.BeaconConfig().SlotsPerEpoch
|
||||
validatorCount := params.BeaconConfig().MinGenesisActiveValidatorCount / 8
|
||||
state := buildState(e+4, validatorCount)
|
||||
startShard := uint64(960)
|
||||
atts := make([]*pb.PendingAttestation, 3)
|
||||
for i := 0; i < len(atts); i++ {
|
||||
atts[i] = &pb.PendingAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: startShard + uint64(i),
|
||||
DataRoot: []byte{'A'},
|
||||
},
|
||||
Target: ðpb.Checkpoint{},
|
||||
Source: ðpb.Checkpoint{},
|
||||
},
|
||||
@@ -1272,18 +862,11 @@ func TestAttestationDelta_SomeAttestedFinalityDelay(t *testing.T) {
|
||||
}
|
||||
state.PreviousEpochAttestations = atts
|
||||
state.FinalizedCheckpoint.Epoch = 0
|
||||
state.CurrentCrosslinks[startShard] = ðpb.Crosslink{
|
||||
DataRoot: []byte{'A'},
|
||||
}
|
||||
state.CurrentCrosslinks[startShard+1] = ðpb.Crosslink{
|
||||
DataRoot: []byte{'A'},
|
||||
}
|
||||
|
||||
rewards, penalties, err := attestationDelta(state)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
attestedBalance, err := AttestingBalance(state, atts)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -1293,7 +876,7 @@ func TestAttestationDelta_SomeAttestedFinalityDelay(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
attestedIndices := []uint64{5, 754, 797, 1637, 1770, 1862, 1192}
|
||||
attestedIndices := []uint64{100, 106, 196, 641, 654, 1606}
|
||||
for _, i := range attestedIndices {
|
||||
base, err := BaseReward(state, i)
|
||||
if err != nil {
|
||||
@@ -1337,7 +920,7 @@ func TestProcessRegistryUpdates_EligibleToActivate(t *testing.T) {
|
||||
Slot: 5 * params.BeaconConfig().SlotsPerEpoch,
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{},
|
||||
}
|
||||
limit, err := helpers.ValidatorChurnLimit(state)
|
||||
limit, err := helpers.ValidatorChurnLimit(0)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -1373,10 +956,10 @@ func TestProcessRegistryUpdates_ActivationCompletes(t *testing.T) {
|
||||
state := &pb.BeaconState{
|
||||
Slot: 5 * params.BeaconConfig().SlotsPerEpoch,
|
||||
Validators: []*ethpb.Validator{
|
||||
{ExitEpoch: params.BeaconConfig().ActivationExitDelay,
|
||||
ActivationEpoch: 5 + params.BeaconConfig().ActivationExitDelay + 1},
|
||||
{ExitEpoch: params.BeaconConfig().ActivationExitDelay,
|
||||
ActivationEpoch: 5 + params.BeaconConfig().ActivationExitDelay + 1},
|
||||
{ExitEpoch: params.BeaconConfig().MaxSeedLookhead,
|
||||
ActivationEpoch: 5 + params.BeaconConfig().MaxSeedLookhead + 1},
|
||||
{ExitEpoch: params.BeaconConfig().MaxSeedLookhead,
|
||||
ActivationEpoch: 5 + params.BeaconConfig().MaxSeedLookhead + 1},
|
||||
},
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{},
|
||||
}
|
||||
@@ -1385,9 +968,9 @@ func TestProcessRegistryUpdates_ActivationCompletes(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
for i, validator := range newState.Validators {
|
||||
if validator.ExitEpoch != params.BeaconConfig().ActivationExitDelay {
|
||||
if validator.ExitEpoch != params.BeaconConfig().MaxSeedLookhead {
|
||||
t.Errorf("Could not update registry %d, wanted exit slot %d got %d",
|
||||
i, params.BeaconConfig().ActivationExitDelay, validator.ExitEpoch)
|
||||
i, params.BeaconConfig().MaxSeedLookhead, validator.ExitEpoch)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1412,9 +995,9 @@ func TestProcessRegistryUpdates_ValidatorsEjected(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
for i, validator := range newState.Validators {
|
||||
if validator.ExitEpoch != params.BeaconConfig().ActivationExitDelay+1 {
|
||||
if validator.ExitEpoch != params.BeaconConfig().MaxSeedLookhead+1 {
|
||||
t.Errorf("Could not update registry %d, wanted exit slot %d got %d",
|
||||
i, params.BeaconConfig().ActivationExitDelay+1, validator.ExitEpoch)
|
||||
i, params.BeaconConfig().MaxSeedLookhead+1, validator.ExitEpoch)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1451,7 +1034,7 @@ func TestProcessRegistryUpdates_CanExits(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProcessRewardsAndPenalties_GenesisEpoch(t *testing.T) {
|
||||
state := &pb.BeaconState{Slot: params.BeaconConfig().SlotsPerEpoch - 1, StartShard: 999}
|
||||
state := &pb.BeaconState{Slot: params.BeaconConfig().SlotsPerEpoch - 1}
|
||||
newState, err := ProcessRewardsAndPenalties(state)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -1466,15 +1049,10 @@ func TestProcessRewardsAndPenalties_SomeAttested(t *testing.T) {
|
||||
e := params.BeaconConfig().SlotsPerEpoch
|
||||
validatorCount := params.BeaconConfig().MinGenesisActiveValidatorCount / 8
|
||||
state := buildState(e+2, validatorCount)
|
||||
startShard := uint64(960)
|
||||
atts := make([]*pb.PendingAttestation, 3)
|
||||
for i := 0; i < len(atts); i++ {
|
||||
atts[i] = &pb.PendingAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: startShard + uint64(i),
|
||||
DataRoot: []byte{'A'},
|
||||
},
|
||||
Target: ðpb.Checkpoint{},
|
||||
Source: ðpb.Checkpoint{},
|
||||
},
|
||||
@@ -1483,26 +1061,17 @@ func TestProcessRewardsAndPenalties_SomeAttested(t *testing.T) {
|
||||
}
|
||||
}
|
||||
state.PreviousEpochAttestations = atts
|
||||
state.CurrentCrosslinks[startShard] = ðpb.Crosslink{
|
||||
DataRoot: []byte{'A'},
|
||||
}
|
||||
state.CurrentCrosslinks[startShard+1] = ðpb.Crosslink{
|
||||
DataRoot: []byte{'A'},
|
||||
}
|
||||
state.CurrentCrosslinks[startShard+2] = ðpb.Crosslink{
|
||||
DataRoot: []byte{'A'},
|
||||
}
|
||||
|
||||
state, err := ProcessRewardsAndPenalties(state)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wanted := uint64(31999949392)
|
||||
wanted := uint64(31999873505)
|
||||
if state.Balances[0] != wanted {
|
||||
t.Errorf("wanted balance: %d, got: %d",
|
||||
wanted, state.Balances[0])
|
||||
}
|
||||
wanted = uint64(31999995452)
|
||||
wanted = uint64(31999810265)
|
||||
if state.Balances[4] != wanted {
|
||||
t.Errorf("wanted balance: %d, got: %d",
|
||||
wanted, state.Balances[1])
|
||||
@@ -1539,10 +1108,7 @@ func buildState(slot uint64, validatorCount uint64) *pb.BeaconState {
|
||||
Slot: slot,
|
||||
Balances: validatorBalances,
|
||||
Validators: validators,
|
||||
CurrentCrosslinks: make([]*ethpb.Crosslink, params.BeaconConfig().ShardCount),
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
CompactCommitteesRoots: make([][]byte, params.BeaconConfig().EpochsPerSlashingsVector),
|
||||
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
|
||||
BlockRoots: make([][]byte, params.BeaconConfig().SlotsPerEpoch*10),
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{},
|
||||
|
||||
@@ -1,17 +1,30 @@
|
||||
package epoch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
)
|
||||
|
||||
// ComputeValidatorParticipation by matching validator attestations during the epoch,
|
||||
// computing the attesting balance, and how much attested compared to the total balances.
|
||||
func ComputeValidatorParticipation(state *pb.BeaconState) (*ethpb.ValidatorParticipation, error) {
|
||||
currentEpoch := helpers.SlotToEpoch(state.Slot)
|
||||
atts, err := MatchAttestations(state, currentEpoch)
|
||||
// ComputeValidatorParticipation by matching validator attestations from the previous epoch,
|
||||
// computing the attesting balance, and how much attested compared to the total balance.
|
||||
// The previous epoch is used because it is deterministic, as the current epoch may not
|
||||
// have completed yet and will not give accurate results.
|
||||
func ComputeValidatorParticipation(state *pb.BeaconState, epoch uint64) (*ethpb.ValidatorParticipation, error) {
|
||||
currentEpoch := helpers.CurrentEpoch(state)
|
||||
previousEpoch := helpers.PrevEpoch(state)
|
||||
if epoch != currentEpoch && epoch != previousEpoch {
|
||||
return nil, fmt.Errorf(
|
||||
"requested epoch is not previous epoch %d or current epoch %d, requested %d",
|
||||
previousEpoch,
|
||||
currentEpoch,
|
||||
epoch,
|
||||
)
|
||||
}
|
||||
atts, err := MatchAttestations(state, epoch)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not retrieve head attestations")
|
||||
}
|
||||
|
||||
@@ -8,13 +8,14 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
func TestComputeValidatorParticipation(t *testing.T) {
|
||||
func TestComputeValidatorParticipation_PreviousEpoch(t *testing.T) {
|
||||
params.OverrideBeaconConfig(params.MinimalSpecConfig())
|
||||
e := uint64(1)
|
||||
attestedBalance := uint64(1)
|
||||
attestedBalance := uint64(20) * params.BeaconConfig().MaxEffectiveBalance
|
||||
validatorCount := uint64(100)
|
||||
|
||||
validators := make([]*ethpb.Validator, validatorCount)
|
||||
@@ -27,32 +28,128 @@ func TestComputeValidatorParticipation(t *testing.T) {
|
||||
balances[i] = params.BeaconConfig().MaxEffectiveBalance
|
||||
}
|
||||
|
||||
atts := []*pb.PendingAttestation{{Data: ðpb.AttestationData{Crosslink: ðpb.Crosslink{Shard: 0}, Target: ðpb.Checkpoint{}}}}
|
||||
var crosslinks []*ethpb.Crosslink
|
||||
for i := uint64(0); i < params.BeaconConfig().ShardCount; i++ {
|
||||
crosslinks = append(crosslinks, ðpb.Crosslink{
|
||||
StartEpoch: 0,
|
||||
DataRoot: []byte{'A'},
|
||||
})
|
||||
blockRoots := make([][]byte, 256)
|
||||
for i := 0; i < len(blockRoots); i++ {
|
||||
slot := bytesutil.Bytes32(uint64(i))
|
||||
blockRoots[i] = slot
|
||||
}
|
||||
target := ðpb.Checkpoint{
|
||||
Epoch: e,
|
||||
Root: blockRoots[0],
|
||||
}
|
||||
|
||||
atts := []*pb.PendingAttestation{
|
||||
{
|
||||
Data: ðpb.AttestationData{Target: target, Slot: 0},
|
||||
AggregationBits: []byte{0xFF, 0xFF, 0xFF, 0xFF},
|
||||
},
|
||||
{
|
||||
Data: ðpb.AttestationData{Target: target, Slot: 1},
|
||||
AggregationBits: []byte{0xFF, 0xFF, 0xFF, 0xFF},
|
||||
},
|
||||
{
|
||||
Data: ðpb.AttestationData{Target: target, Slot: 2},
|
||||
AggregationBits: []byte{0xFF, 0xFF, 0xFF, 0xFF},
|
||||
},
|
||||
{
|
||||
Data: ðpb.AttestationData{Target: target, Slot: 3},
|
||||
AggregationBits: []byte{0xFF, 0xFF, 0xFF, 0xFF},
|
||||
},
|
||||
{
|
||||
Data: ðpb.AttestationData{Target: target, Slot: 4},
|
||||
AggregationBits: []byte{0xFF, 0xFF, 0xFF, 0xFF},
|
||||
},
|
||||
}
|
||||
|
||||
s := &pb.BeaconState{
|
||||
Slot: e*params.BeaconConfig().SlotsPerEpoch + 1,
|
||||
Validators: validators,
|
||||
Balances: balances,
|
||||
BlockRoots: make([][]byte, 128),
|
||||
Slashings: []uint64{0, 1e9, 1e9},
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
CompactCommitteesRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
CurrentCrosslinks: crosslinks,
|
||||
CurrentEpochAttestations: atts,
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{},
|
||||
JustificationBits: bitfield.Bitvector4{0x00},
|
||||
CurrentJustifiedCheckpoint: ðpb.Checkpoint{},
|
||||
Slot: e*params.BeaconConfig().SlotsPerEpoch + 1,
|
||||
Validators: validators,
|
||||
Balances: balances,
|
||||
BlockRoots: blockRoots,
|
||||
Slashings: []uint64{0, 1e9, 1e9},
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
PreviousEpochAttestations: atts,
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{},
|
||||
JustificationBits: bitfield.Bitvector4{0x00},
|
||||
PreviousJustifiedCheckpoint: target,
|
||||
}
|
||||
|
||||
res, err := epoch.ComputeValidatorParticipation(s)
|
||||
res, err := epoch.ComputeValidatorParticipation(s, e-1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
wanted := ðpb.ValidatorParticipation{
|
||||
VotedEther: attestedBalance,
|
||||
EligibleEther: validatorCount * params.BeaconConfig().MaxEffectiveBalance,
|
||||
GlobalParticipationRate: float32(attestedBalance) / float32(validatorCount*params.BeaconConfig().MaxEffectiveBalance),
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(res, wanted) {
|
||||
t.Errorf("Incorrect validator participation, wanted %v received %v", wanted, res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeValidatorParticipation_CurrentEpoch(t *testing.T) {
|
||||
params.OverrideBeaconConfig(params.MinimalSpecConfig())
|
||||
e := uint64(1)
|
||||
attestedBalance := uint64(16) * params.BeaconConfig().MaxEffectiveBalance
|
||||
validatorCount := uint64(100)
|
||||
|
||||
validators := make([]*ethpb.Validator, validatorCount)
|
||||
balances := make([]uint64, validatorCount)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = ðpb.Validator{
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
||||
}
|
||||
balances[i] = params.BeaconConfig().MaxEffectiveBalance
|
||||
}
|
||||
|
||||
slot := e*params.BeaconConfig().SlotsPerEpoch + 4
|
||||
blockRoots := make([][]byte, 256)
|
||||
for i := 0; i < len(blockRoots); i++ {
|
||||
slot := bytesutil.Bytes32(uint64(i))
|
||||
blockRoots[i] = slot
|
||||
}
|
||||
target := ðpb.Checkpoint{
|
||||
Epoch: e,
|
||||
Root: blockRoots[params.BeaconConfig().SlotsPerEpoch],
|
||||
}
|
||||
|
||||
atts := []*pb.PendingAttestation{
|
||||
{
|
||||
Data: ðpb.AttestationData{Target: target, Slot: slot - 4},
|
||||
AggregationBits: []byte{0xFF, 0xFF, 0xFF, 0xFF},
|
||||
},
|
||||
{
|
||||
Data: ðpb.AttestationData{Target: target, Slot: slot - 3},
|
||||
AggregationBits: []byte{0xFF, 0xFF, 0xFF, 0xFF},
|
||||
},
|
||||
{
|
||||
Data: ðpb.AttestationData{Target: target, Slot: slot - 2},
|
||||
AggregationBits: []byte{0xFF, 0xFF, 0xFF, 0xFF},
|
||||
},
|
||||
{
|
||||
Data: ðpb.AttestationData{Target: target, Slot: slot - 1},
|
||||
AggregationBits: []byte{0xFF, 0xFF, 0xFF, 0xFF},
|
||||
},
|
||||
}
|
||||
|
||||
s := &pb.BeaconState{
|
||||
Slot: slot,
|
||||
Validators: validators,
|
||||
Balances: balances,
|
||||
BlockRoots: blockRoots,
|
||||
Slashings: []uint64{0, 1e9, 1e9},
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
CurrentEpochAttestations: atts,
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{},
|
||||
JustificationBits: bitfield.Bitvector4{0x00},
|
||||
CurrentJustifiedCheckpoint: target,
|
||||
}
|
||||
|
||||
res, err := epoch.ComputeValidatorParticipation(s, e)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/epoch/precompute",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = [
|
||||
"//beacon-chain/core/epoch:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
|
||||
@@ -40,13 +40,7 @@ func ProcessAttestations(
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// Get attestation slot to find lowest inclusion delayed attestation for each attested validators.
|
||||
aSlot, err := helpers.AttestationDataSlot(state, a.Data)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
||||
}
|
||||
vp = UpdateValidator(vp, v, indices, a, aSlot)
|
||||
vp = UpdateValidator(vp, v, indices, a, a.Data.Slot)
|
||||
}
|
||||
|
||||
bp = UpdateBalance(vp, bp)
|
||||
@@ -112,11 +106,7 @@ func SameTarget(state *pb.BeaconState, a *pb.PendingAttestation, e uint64) (bool
|
||||
|
||||
// SameHead returns true if attestation `a` attested to the same block by attestation slot in state.
|
||||
func SameHead(state *pb.BeaconState, a *pb.PendingAttestation) (bool, error) {
|
||||
aSlot, err := helpers.AttestationDataSlot(state, a.Data)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
r, err := helpers.BlockRootAtSlot(state, aSlot)
|
||||
r, err := helpers.BlockRootAtSlot(state, a.Data.Slot)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -139,6 +129,12 @@ func UpdateValidator(vp []*Validator, record *Validator, indices []uint64, a *pb
|
||||
}
|
||||
if record.IsPrevEpochAttester {
|
||||
vp[i].IsPrevEpochAttester = true
|
||||
// Update attestation inclusion info if inclusion slot is lower than before
|
||||
if inclusionSlot < vp[i].InclusionSlot {
|
||||
vp[i].InclusionSlot = aSlot + a.InclusionDelay
|
||||
vp[i].InclusionDistance = a.InclusionDelay
|
||||
vp[i].ProposerIndex = a.ProposerIndex
|
||||
}
|
||||
}
|
||||
if record.IsPrevEpochTargetAttester {
|
||||
vp[i].IsPrevEpochTargetAttester = true
|
||||
@@ -146,13 +142,6 @@ func UpdateValidator(vp []*Validator, record *Validator, indices []uint64, a *pb
|
||||
if record.IsPrevEpochHeadAttester {
|
||||
vp[i].IsPrevEpochHeadAttester = true
|
||||
}
|
||||
|
||||
// Update attestation inclusion info if inclusion slot is lower than before
|
||||
if inclusionSlot < vp[i].InclusionSlot {
|
||||
vp[i].InclusionSlot = aSlot + a.InclusionDelay
|
||||
vp[i].InclusionDistance = a.InclusionDelay
|
||||
vp[i].ProposerIndex = a.ProposerIndex
|
||||
}
|
||||
}
|
||||
return vp
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
)
|
||||
|
||||
func TestUpdateValidator(t *testing.T) {
|
||||
func TestUpdateValidator_Works(t *testing.T) {
|
||||
e := params.BeaconConfig().FarFutureEpoch
|
||||
vp := []*precompute.Validator{{}, {InclusionSlot: e}, {}, {InclusionSlot: e}, {}, {InclusionSlot: e}}
|
||||
record := &precompute.Validator{IsCurrentEpochAttester: true, IsCurrentEpochTargetAttester: true,
|
||||
@@ -33,6 +33,21 @@ func TestUpdateValidator(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateValidator_InclusionOnlyCountsPrevEpoch(t *testing.T) {
|
||||
e := params.BeaconConfig().FarFutureEpoch
|
||||
vp := []*precompute.Validator{{InclusionSlot: e}}
|
||||
record := &precompute.Validator{IsCurrentEpochAttester: true, IsCurrentEpochTargetAttester: true}
|
||||
a := &pb.PendingAttestation{InclusionDelay: 1, ProposerIndex: 2}
|
||||
|
||||
// Verify inclusion info doesnt get updated.
|
||||
vp = precompute.UpdateValidator(vp, record, []uint64{0}, a, 100)
|
||||
wanted := &precompute.Validator{IsCurrentEpochAttester: true, IsCurrentEpochTargetAttester: true, InclusionSlot: e}
|
||||
wantedVp := []*precompute.Validator{wanted}
|
||||
if !reflect.DeepEqual(vp, wantedVp) {
|
||||
t.Error("Incorrect attesting validator calculations")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateBalance(t *testing.T) {
|
||||
vp := []*precompute.Validator{
|
||||
{IsCurrentEpochAttester: true, CurrentEpochEffectiveBalance: 100},
|
||||
@@ -60,20 +75,15 @@ func TestUpdateBalance(t *testing.T) {
|
||||
func TestSameHead(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beaconState.Slot = 1
|
||||
att := ðpb.Attestation{Data: ðpb.AttestationData{
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{Shard: 0}}}
|
||||
attSlot, err := helpers.AttestationDataSlot(beaconState, att.Data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
Target: ðpb.Checkpoint{Epoch: 0}}}
|
||||
r := []byte{'A'}
|
||||
beaconState.BlockRoots[attSlot] = r
|
||||
beaconState.BlockRoots[0] = r
|
||||
att.Data.BeaconBlockRoot = r
|
||||
same, err := precompute.SameHead(beaconState, &pb.PendingAttestation{Data: att.Data})
|
||||
if err != nil {
|
||||
@@ -94,20 +104,15 @@ func TestSameHead(t *testing.T) {
|
||||
|
||||
func TestSameTarget(t *testing.T) {
|
||||
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beaconState.Slot = 1
|
||||
att := ðpb.Attestation{Data: ðpb.AttestationData{
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{Shard: 0}}}
|
||||
attSlot, err := helpers.AttestationDataSlot(beaconState, att.Data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
Target: ðpb.Checkpoint{Epoch: 0}}}
|
||||
r := []byte{'A'}
|
||||
beaconState.BlockRoots[attSlot] = r
|
||||
beaconState.BlockRoots[0] = r
|
||||
att.Data.Target.Root = r
|
||||
same, err := precompute.SameTarget(beaconState, &pb.PendingAttestation{Data: att.Data}, 0)
|
||||
if err != nil {
|
||||
@@ -128,20 +133,15 @@ func TestSameTarget(t *testing.T) {
|
||||
|
||||
func TestAttestedPrevEpoch(t *testing.T) {
|
||||
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beaconState.Slot = params.BeaconConfig().SlotsPerEpoch
|
||||
att := ðpb.Attestation{Data: ðpb.AttestationData{
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{Shard: 960}}}
|
||||
attSlot, err := helpers.AttestationDataSlot(beaconState, att.Data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
Target: ðpb.Checkpoint{Epoch: 0}}}
|
||||
r := []byte{'A'}
|
||||
beaconState.BlockRoots[attSlot] = r
|
||||
beaconState.BlockRoots[0] = r
|
||||
att.Data.Target.Root = r
|
||||
att.Data.BeaconBlockRoot = r
|
||||
votedEpoch, votedTarget, votedHead, err := precompute.AttestedPrevEpoch(beaconState, &pb.PendingAttestation{Data: att.Data})
|
||||
@@ -161,20 +161,15 @@ func TestAttestedPrevEpoch(t *testing.T) {
|
||||
|
||||
func TestAttestedCurrentEpoch(t *testing.T) {
|
||||
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beaconState.Slot = params.BeaconConfig().SlotsPerEpoch + 1
|
||||
att := ðpb.Attestation{Data: ðpb.AttestationData{
|
||||
Target: ðpb.Checkpoint{Epoch: 1},
|
||||
Crosslink: ðpb.Crosslink{}}}
|
||||
attSlot, err := helpers.AttestationDataSlot(beaconState, att.Data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
Target: ðpb.Checkpoint{Epoch: 1}}}
|
||||
r := []byte{'A'}
|
||||
beaconState.BlockRoots[attSlot] = r
|
||||
beaconState.BlockRoots[params.BeaconConfig().SlotsPerEpoch] = r
|
||||
att.Data.Target.Root = r
|
||||
att.Data.BeaconBlockRoot = r
|
||||
votedEpoch, votedTarget, err := precompute.AttestedCurrentEpoch(beaconState, &pb.PendingAttestation{Data: att.Data})
|
||||
@@ -197,7 +192,7 @@ func TestProcessAttestations(t *testing.T) {
|
||||
|
||||
validators := uint64(64)
|
||||
deposits, _, _ := testutil.SetupInitialDeposits(t, validators)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -205,11 +200,11 @@ func TestProcessAttestations(t *testing.T) {
|
||||
|
||||
bf := []byte{0xff}
|
||||
att1 := ðpb.Attestation{Data: ðpb.AttestationData{
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{Shard: 960}}, AggregationBits: bf}
|
||||
Target: ðpb.Checkpoint{Epoch: 0}},
|
||||
AggregationBits: bf}
|
||||
att2 := ðpb.Attestation{Data: ðpb.AttestationData{
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{Shard: 961}}, AggregationBits: bf}
|
||||
Target: ðpb.Checkpoint{Epoch: 0}},
|
||||
AggregationBits: bf}
|
||||
beaconState.BlockRoots[0] = []byte{'A'}
|
||||
att1.Data.Target.Root = []byte{'A'}
|
||||
att1.Data.BeaconBlockRoot = []byte{'A'}
|
||||
|
||||
@@ -40,9 +40,9 @@ func TestProcessJustificationAndFinalizationPreCompute_ConsecutiveEpochs(t *test
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(128)}) {
|
||||
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(64)}) {
|
||||
t.Errorf("Wanted current justified root: %v, got: %v",
|
||||
[]byte{byte(128)}, newState.CurrentJustifiedCheckpoint.Root)
|
||||
[]byte{byte(64)}, newState.CurrentJustifiedCheckpoint.Root)
|
||||
}
|
||||
if newState.CurrentJustifiedCheckpoint.Epoch != 2 {
|
||||
t.Errorf("Wanted justified epoch: %d, got: %d",
|
||||
@@ -90,9 +90,9 @@ func TestProcessJustificationAndFinalizationPreCompute_JustifyCurrentEpoch(t *te
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(128)}) {
|
||||
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(64)}) {
|
||||
t.Errorf("Wanted current justified root: %v, got: %v",
|
||||
[]byte{byte(128)}, newState.CurrentJustifiedCheckpoint.Root)
|
||||
[]byte{byte(64)}, newState.CurrentJustifiedCheckpoint.Root)
|
||||
}
|
||||
if newState.CurrentJustifiedCheckpoint.Epoch != 2 {
|
||||
t.Errorf("Wanted justified epoch: %d, got: %d",
|
||||
@@ -139,9 +139,9 @@ func TestProcessJustificationAndFinalizationPreCompute_JustifyPrevEpoch(t *testi
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(128)}) {
|
||||
if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(64)}) {
|
||||
t.Errorf("Wanted current justified root: %v, got: %v",
|
||||
[]byte{byte(128)}, newState.CurrentJustifiedCheckpoint.Root)
|
||||
[]byte{byte(64)}, newState.CurrentJustifiedCheckpoint.Root)
|
||||
}
|
||||
if newState.PreviousJustifiedCheckpoint.Epoch != 0 {
|
||||
t.Errorf("Wanted previous justified epoch: %d, got: %d",
|
||||
|
||||
@@ -2,7 +2,6 @@ package precompute
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/mathutil"
|
||||
@@ -30,14 +29,9 @@ func ProcessRewardsAndPenaltiesPrecompute(state *pb.BeaconState, bp *Balance, vp
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get attestation delta")
|
||||
}
|
||||
clRewards, clPenalties, err := crosslinkDeltaPreCompute(state, bp, vp)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not get crosslink delta")
|
||||
}
|
||||
|
||||
for i := 0; i < len(state.Validators); i++ {
|
||||
state = helpers.IncreaseBalance(state, uint64(i), attsRewards[i]+clRewards[i]+proposerRewards[i])
|
||||
state = helpers.DecreaseBalance(state, uint64(i), attsPenalties[i]+clPenalties[i])
|
||||
state = helpers.IncreaseBalance(state, uint64(i), attsRewards[i]+proposerRewards[i])
|
||||
state = helpers.DecreaseBalance(state, uint64(i), attsPenalties[i])
|
||||
}
|
||||
return state, nil
|
||||
}
|
||||
@@ -70,8 +64,7 @@ func attestationDelta(state *pb.BeaconState, bp *Balance, v *Validator) (uint64,
|
||||
r += br * bp.PrevEpochAttesters / bp.CurrentEpoch
|
||||
proposerReward := br / params.BeaconConfig().ProposerRewardQuotient
|
||||
maxAtteserReward := br - proposerReward
|
||||
slotsPerEpoch := params.BeaconConfig().SlotsPerEpoch
|
||||
r += maxAtteserReward * (slotsPerEpoch + params.BeaconConfig().MinAttestationInclusionDelay - v.InclusionDistance) / slotsPerEpoch
|
||||
r += maxAtteserReward / v.InclusionDistance
|
||||
} else {
|
||||
p += br
|
||||
}
|
||||
@@ -118,48 +111,3 @@ func proposerDeltaPrecompute(state *pb.BeaconState, bp *Balance, vp []*Validator
|
||||
}
|
||||
return rewards, nil
|
||||
}
|
||||
|
||||
// This computes the rewards and penalties differences for individual validators based on the
|
||||
// crosslink records.
|
||||
func crosslinkDeltaPreCompute(state *pb.BeaconState, bp *Balance, vp []*Validator) ([]uint64, []uint64, error) {
|
||||
rewards := make([]uint64, len(state.Validators))
|
||||
penalties := make([]uint64, len(state.Validators))
|
||||
prevEpoch := helpers.PrevEpoch(state)
|
||||
count, err := helpers.CommitteeCount(state, prevEpoch)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not get epoch committee count")
|
||||
}
|
||||
startShard, err := helpers.StartShard(state, prevEpoch)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not get epoch start shard")
|
||||
}
|
||||
for i := uint64(0); i < count; i++ {
|
||||
shard := (startShard + i) % params.BeaconConfig().ShardCount
|
||||
committee, err := helpers.CrosslinkCommittee(state, prevEpoch, shard)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not get crosslink's committee")
|
||||
}
|
||||
_, attestingIndices, err := epoch.WinningCrosslink(state, shard, prevEpoch)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not get winning crosslink")
|
||||
}
|
||||
|
||||
attested := make(map[uint64]bool)
|
||||
// Construct a map to look up validators that voted for crosslink.
|
||||
for _, index := range attestingIndices {
|
||||
attested[index] = true
|
||||
}
|
||||
committeeBalance := helpers.TotalBalance(state, committee)
|
||||
attestingBalance := helpers.TotalBalance(state, attestingIndices)
|
||||
|
||||
for _, index := range committee {
|
||||
base := vp[i].CurrentEpochEffectiveBalance * params.BeaconConfig().BaseRewardFactor / mathutil.IntegerSquareRoot(bp.CurrentEpoch) / params.BeaconConfig().BaseRewardsPerEpoch
|
||||
if _, ok := attested[index]; ok {
|
||||
rewards[index] += base * attestingBalance / committeeBalance
|
||||
} else {
|
||||
penalties[index] += base
|
||||
}
|
||||
}
|
||||
}
|
||||
return rewards, penalties, nil
|
||||
}
|
||||
|
||||
@@ -17,15 +17,10 @@ func TestProcessRewardsAndPenaltiesPrecompute(t *testing.T) {
|
||||
e := params.BeaconConfig().SlotsPerEpoch
|
||||
validatorCount := uint64(2048)
|
||||
state := buildState(e+3, validatorCount)
|
||||
startShard := uint64(960)
|
||||
atts := make([]*pb.PendingAttestation, 3)
|
||||
for i := 0; i < len(atts); i++ {
|
||||
atts[i] = &pb.PendingAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: startShard + uint64(i),
|
||||
DataRoot: []byte{'A'},
|
||||
},
|
||||
Target: ðpb.Checkpoint{},
|
||||
Source: ðpb.Checkpoint{},
|
||||
},
|
||||
@@ -34,15 +29,6 @@ func TestProcessRewardsAndPenaltiesPrecompute(t *testing.T) {
|
||||
}
|
||||
}
|
||||
state.PreviousEpochAttestations = atts
|
||||
state.CurrentCrosslinks[startShard] = ðpb.Crosslink{
|
||||
DataRoot: []byte{'A'},
|
||||
}
|
||||
state.CurrentCrosslinks[startShard+1] = ðpb.Crosslink{
|
||||
DataRoot: []byte{'A'},
|
||||
}
|
||||
state.CurrentCrosslinks[startShard+2] = ðpb.Crosslink{
|
||||
DataRoot: []byte{'A'},
|
||||
}
|
||||
|
||||
vp, bp := New(context.Background(), state)
|
||||
vp, bp, err := ProcessAttestations(context.Background(), state, vp, bp)
|
||||
@@ -56,14 +42,14 @@ func TestProcessRewardsAndPenaltiesPrecompute(t *testing.T) {
|
||||
}
|
||||
|
||||
// Indices that voted everything except for head, lost a bit money
|
||||
wanted := uint64(31999995452)
|
||||
wanted := uint64(31999810265)
|
||||
if state.Balances[4] != wanted {
|
||||
t.Errorf("wanted balance: %d, got: %d",
|
||||
wanted, state.Balances[4])
|
||||
}
|
||||
|
||||
// Indices that did not vote, lost more money
|
||||
wanted = uint64(31999949392)
|
||||
wanted = uint64(31999873505)
|
||||
if state.Balances[0] != wanted {
|
||||
t.Errorf("wanted balance: %d, got: %d",
|
||||
wanted, state.Balances[0])
|
||||
@@ -75,15 +61,10 @@ func TestAttestationDeltaPrecompute(t *testing.T) {
|
||||
e := params.BeaconConfig().SlotsPerEpoch
|
||||
validatorCount := uint64(2048)
|
||||
state := buildState(e+2, validatorCount)
|
||||
startShard := uint64(960)
|
||||
atts := make([]*pb.PendingAttestation, 3)
|
||||
for i := 0; i < len(atts); i++ {
|
||||
atts[i] = &pb.PendingAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: startShard + uint64(i),
|
||||
DataRoot: []byte{'A'},
|
||||
},
|
||||
Target: ðpb.Checkpoint{},
|
||||
Source: ðpb.Checkpoint{},
|
||||
},
|
||||
@@ -92,12 +73,6 @@ func TestAttestationDeltaPrecompute(t *testing.T) {
|
||||
}
|
||||
}
|
||||
state.PreviousEpochAttestations = atts
|
||||
state.CurrentCrosslinks[startShard] = ðpb.Crosslink{
|
||||
DataRoot: []byte{'A'},
|
||||
}
|
||||
state.CurrentCrosslinks[startShard+1] = ðpb.Crosslink{
|
||||
DataRoot: []byte{'A'},
|
||||
}
|
||||
|
||||
vp, bp := New(context.Background(), state)
|
||||
vp, bp, err := ProcessAttestations(context.Background(), state, vp, bp)
|
||||
@@ -118,7 +93,7 @@ func TestAttestationDeltaPrecompute(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
attestedIndices := []uint64{5, 754, 797, 1637, 1770, 1862, 1192}
|
||||
attestedIndices := []uint64{100, 106, 196, 641, 654, 1606}
|
||||
for _, i := range attestedIndices {
|
||||
base, err := epoch.BaseReward(state, i)
|
||||
if err != nil {
|
||||
@@ -157,78 +132,6 @@ func TestAttestationDeltaPrecompute(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrosslinkDeltaPrecompute(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
e := params.BeaconConfig().SlotsPerEpoch
|
||||
helpers.ClearShuffledValidatorCache()
|
||||
validatorCount := uint64(2048)
|
||||
state := buildState(e+2, validatorCount)
|
||||
startShard := uint64(960)
|
||||
atts := make([]*pb.PendingAttestation, 2)
|
||||
for i := 0; i < len(atts); i++ {
|
||||
atts[i] = &pb.PendingAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: startShard + uint64(i),
|
||||
DataRoot: []byte{'A'},
|
||||
},
|
||||
Target: ðpb.Checkpoint{},
|
||||
Source: ðpb.Checkpoint{},
|
||||
},
|
||||
InclusionDelay: uint64(i + 100),
|
||||
AggregationBits: bitfield.Bitlist{0xC0, 0xC0, 0xC0, 0xC0, 0x01},
|
||||
}
|
||||
}
|
||||
state.PreviousEpochAttestations = atts
|
||||
state.CurrentCrosslinks[startShard] = ðpb.Crosslink{
|
||||
DataRoot: []byte{'A'}, Shard: startShard,
|
||||
}
|
||||
state.CurrentCrosslinks[startShard+1] = ðpb.Crosslink{
|
||||
DataRoot: []byte{'A'}, Shard: startShard + 1,
|
||||
}
|
||||
|
||||
vp, bp := New(context.Background(), state)
|
||||
vp, bp, err := ProcessAttestations(context.Background(), state, vp, bp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rewards, penalties, err := crosslinkDeltaPreCompute(state, bp, vp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
attestedIndices := []uint64{5, 16, 336, 797, 1082, 1450, 1770, 1958}
|
||||
for _, i := range attestedIndices {
|
||||
// Since all these validators attested, they should get the same rewards.
|
||||
want := uint64(12649)
|
||||
if rewards[i] != want {
|
||||
t.Errorf("Wanted reward balance %d, got %d", want, rewards[i])
|
||||
}
|
||||
// Since all these validators attested, they shouldn't get penalized.
|
||||
if penalties[i] != 0 {
|
||||
t.Errorf("Wanted penalty balance 0, got %d", penalties[i])
|
||||
}
|
||||
}
|
||||
|
||||
nonAttestedIndices := []uint64{12, 23, 45, 79}
|
||||
for _, i := range nonAttestedIndices {
|
||||
base, err := epoch.BaseReward(state, i)
|
||||
if err != nil {
|
||||
t.Errorf("Could not get base reward: %v", err)
|
||||
}
|
||||
wanted := base
|
||||
// Since all these validators did not attest, they shouldn't get rewarded.
|
||||
if rewards[i] != 0 {
|
||||
t.Errorf("Wanted reward balance 0, got %d", rewards[i])
|
||||
}
|
||||
// Base penalties for not attesting.
|
||||
if penalties[i] != wanted {
|
||||
t.Errorf("Wanted penalty balance %d, got %d", wanted, penalties[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildState(slot uint64, validatorCount uint64) *pb.BeaconState {
|
||||
validators := make([]*ethpb.Validator, validatorCount)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
@@ -259,10 +162,7 @@ func buildState(slot uint64, validatorCount uint64) *pb.BeaconState {
|
||||
Slot: slot,
|
||||
Balances: validatorBalances,
|
||||
Validators: validators,
|
||||
CurrentCrosslinks: make([]*ethpb.Crosslink, params.BeaconConfig().ShardCount),
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
CompactCommitteesRoots: make([][]byte, params.BeaconConfig().EpochsPerSlashingsVector),
|
||||
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
|
||||
BlockRoots: make([][]byte, params.BeaconConfig().SlotsPerEpoch*10),
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{},
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
package spectest
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCrosslinksProcessingMainnet(t *testing.T) {
|
||||
runCrosslinkProcessingTests(t, "mainnet")
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package spectest
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCrosslinksProcessingMinimal(t *testing.T) {
|
||||
runCrosslinkProcessingTests(t, "minimal")
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package spectest
|
||||
|
||||
import (
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params/spectest"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
)
|
||||
|
||||
func runCrosslinkProcessingTests(t *testing.T, config string) {
|
||||
if err := spectest.SetConfig(config); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testFolders, testsFolderPath := testutil.TestFolders(t, config, "epoch_processing/crosslinks/pyspec_tests")
|
||||
for _, folder := range testFolders {
|
||||
t.Run(folder.Name(), func(t *testing.T) {
|
||||
folderPath := path.Join(testsFolderPath, folder.Name())
|
||||
testutil.RunEpochOperationTest(t, folderPath, processCrosslinksWrapper)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func processCrosslinksWrapper(t *testing.T, state *pb.BeaconState) (*pb.BeaconState, error) {
|
||||
state, err := epoch.ProcessCrosslinks(state)
|
||||
if err != nil {
|
||||
t.Fatalf("could not process crosslinks: %v", err)
|
||||
}
|
||||
return state, nil
|
||||
}
|
||||
@@ -5,6 +5,5 @@ import (
|
||||
)
|
||||
|
||||
func TestJustificationAndFinalizationMinimal(t *testing.T) {
|
||||
t.Skip("Fails for could not get target atts current epoch")
|
||||
runJustificationAndFinalizationTests(t, "minimal")
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package spectest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
@@ -32,19 +34,19 @@ func runJustificationAndFinalizationTests(t *testing.T, config string) {
|
||||
// This is a subset of state.ProcessEpoch. The spec test defines input data for
|
||||
// `justification_and_finalization` only.
|
||||
func processJustificationAndFinalizationWrapper(t *testing.T, state *pb.BeaconState) (*pb.BeaconState, error) {
|
||||
prevEpochAtts, err := epoch.MatchAttestations(state, helpers.PrevEpoch(state))
|
||||
prevEpochAtts, err := targetAtts(state, helpers.PrevEpoch(state))
|
||||
if err != nil {
|
||||
t.Fatalf("could not get target atts prev epoch %d: %v", helpers.PrevEpoch(state), err)
|
||||
}
|
||||
currentEpochAtts, err := epoch.MatchAttestations(state, helpers.CurrentEpoch(state))
|
||||
currentEpochAtts, err := targetAtts(state, helpers.CurrentEpoch(state))
|
||||
if err != nil {
|
||||
t.Fatalf("could not get target atts current epoch %d: %v", helpers.CurrentEpoch(state), err)
|
||||
}
|
||||
prevEpochAttestedBalance, err := epoch.AttestingBalance(state, prevEpochAtts.Target)
|
||||
prevEpochAttestedBalance, err := epoch.AttestingBalance(state, prevEpochAtts)
|
||||
if err != nil {
|
||||
t.Fatalf("could not get attesting balance prev epoch: %v", err)
|
||||
}
|
||||
currentEpochAttestedBalance, err := epoch.AttestingBalance(state, currentEpochAtts.Target)
|
||||
currentEpochAttestedBalance, err := epoch.AttestingBalance(state, currentEpochAtts)
|
||||
if err != nil {
|
||||
t.Fatalf("could not get attesting balance current epoch: %v", err)
|
||||
}
|
||||
@@ -72,3 +74,36 @@ func processJustificationAndFinalizationPrecomputeWrapper(t *testing.T, state *p
|
||||
|
||||
return state, nil
|
||||
}
|
||||
|
||||
func targetAtts(state *pb.BeaconState, epoch uint64) ([]*pb.PendingAttestation, error) {
|
||||
currentEpoch := helpers.CurrentEpoch(state)
|
||||
previousEpoch := helpers.PrevEpoch(state)
|
||||
|
||||
// Input epoch for matching the source attestations has to be within range
|
||||
// of current epoch & previous epoch.
|
||||
if epoch != currentEpoch && epoch != previousEpoch {
|
||||
return nil, fmt.Errorf("input epoch: %d != current epoch: %d or previous epoch: %d",
|
||||
epoch, currentEpoch, previousEpoch)
|
||||
}
|
||||
|
||||
// Decide if the source attestations are coming from current or previous epoch.
|
||||
var srcAtts []*pb.PendingAttestation
|
||||
if epoch == currentEpoch {
|
||||
srcAtts = state.CurrentEpochAttestations
|
||||
} else {
|
||||
srcAtts = state.PreviousEpochAttestations
|
||||
}
|
||||
targetRoot, err := helpers.BlockRoot(state, epoch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tgtAtts := make([]*pb.PendingAttestation, 0, len(srcAtts))
|
||||
for _, srcAtt := range srcAtts {
|
||||
if bytes.Equal(srcAtt.Data.Target.Root, targetRoot) {
|
||||
tgtAtts = append(tgtAtts, srcAtt)
|
||||
}
|
||||
}
|
||||
|
||||
return tgtAtts, nil
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ go_library(
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//shared/testutil:__pkg__",
|
||||
"//slasher:__subpackages__",
|
||||
"//validator:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
@@ -33,9 +34,6 @@ go_library(
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@org_golang_google_grpc//codes:go_default_library",
|
||||
"@org_golang_google_grpc//status:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -55,18 +53,15 @@ go_test(
|
||||
embed = [":go_default_library"],
|
||||
shard_count = 2,
|
||||
deps = [
|
||||
"//beacon-chain/core/state:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/bls:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/sliceutil:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@org_golang_google_grpc//codes:go_default_library",
|
||||
"@org_golang_google_grpc//status:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -3,11 +3,8 @@ package helpers
|
||||
import (
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/pkg/errors"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -22,39 +19,6 @@ var (
|
||||
ErrAttestationAggregationBitsOverlap = errors.New("overlapping aggregation bits")
|
||||
)
|
||||
|
||||
// AttestationDataSlot returns current slot of AttestationData for given state
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_attestation_data_slot(state: BeaconState, data: AttestationData) -> Slot:
|
||||
// """
|
||||
// Return the slot corresponding to the attestation ``data``.
|
||||
// """
|
||||
// committee_count = get_committee_count(state, data.target.epoch)
|
||||
// offset = (data.crosslink.shard + SHARD_COUNT - get_start_shard(state, data.target.epoch)) % SHARD_COUNT
|
||||
// return Slot(compute_start_slot_of_epoch(data.target.epoch) + offset // (committee_count // SLOTS_PER_EPOCH))
|
||||
func AttestationDataSlot(state *pb.BeaconState, data *ethpb.AttestationData) (uint64, error) {
|
||||
if state == nil {
|
||||
return 0, ErrAttestationDataSlotNilState
|
||||
}
|
||||
if data == nil {
|
||||
return 0, ErrAttestationDataSlotNilData
|
||||
}
|
||||
|
||||
committeeCount, err := CommitteeCount(state, data.Target.Epoch)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
epochStartShardNumber, err := StartShard(state, data.Target.Epoch)
|
||||
if err != nil { // This should never happen if CommitteeCount was successful
|
||||
return 0, errors.Wrap(err, "could not determine epoch start shard")
|
||||
}
|
||||
offset := (data.Crosslink.Shard + params.BeaconConfig().ShardCount -
|
||||
epochStartShardNumber) % params.BeaconConfig().ShardCount
|
||||
|
||||
return StartSlot(data.Target.Epoch) + (offset / (committeeCount / params.BeaconConfig().SlotsPerEpoch)), nil
|
||||
}
|
||||
|
||||
// AggregateAttestations such that the minimal number of attestations are returned.
|
||||
// Note: this is currently a naive implementation to the order of O(n^2).
|
||||
func AggregateAttestations(atts []*ethpb.Attestation) ([]*ethpb.Attestation, error) {
|
||||
|
||||
@@ -9,88 +9,10 @@ import (
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
)
|
||||
|
||||
func TestAttestationDataSlot_OK(t *testing.T) {
|
||||
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
offset := uint64(0)
|
||||
committeeCount, _ := helpers.CommitteeCount(beaconState, 0)
|
||||
expect := offset / (committeeCount / params.BeaconConfig().SlotsPerEpoch)
|
||||
attSlot, err := helpers.AttestationDataSlot(beaconState, ðpb.AttestationData{
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 0,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if attSlot != expect {
|
||||
t.Errorf("Expected %d, received %d", expect, attSlot)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttestationDataSlot_ReturnsErrorWithNilState(t *testing.T) {
|
||||
s, err := helpers.AttestationDataSlot(nil /*state*/, ðpb.AttestationData{
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 0,
|
||||
},
|
||||
})
|
||||
if err != helpers.ErrAttestationDataSlotNilState {
|
||||
t.Errorf("Expected an error, but received %v", err)
|
||||
t.Logf("attestation slot=%v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttestationDataSlot_ReturnsErrorWithNilData(t *testing.T) {
|
||||
s, err := helpers.AttestationDataSlot(&pb.BeaconState{}, nil /*data*/)
|
||||
if err != helpers.ErrAttestationDataSlotNilData {
|
||||
t.Errorf("Expected an error, but received %v", err)
|
||||
t.Logf("attestation slot=%v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttestationDataSlot_ReturnsErrorWithErroneousTargetEpoch(t *testing.T) {
|
||||
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s, err := helpers.AttestationDataSlot(beaconState, ðpb.AttestationData{
|
||||
Target: ðpb.Checkpoint{Epoch: 1<<63 - 1 /* Far future epoch */},
|
||||
})
|
||||
if err == nil {
|
||||
t.Error("Expected an error, but received nil")
|
||||
t.Logf("attestation slot=%v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttestationDataSlot_ReturnsErrorWhenTargetEpochLessThanCurrentEpoch(t *testing.T) {
|
||||
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s, err := helpers.AttestationDataSlot(beaconState, ðpb.AttestationData{
|
||||
Target: ðpb.Checkpoint{Epoch: 2},
|
||||
})
|
||||
if err == nil {
|
||||
t.Error("Expected an error, but received nil")
|
||||
t.Logf("attestation slot=%v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAggregateAttestation(t *testing.T) {
|
||||
tests := []struct {
|
||||
a1 *ethpb.Attestation
|
||||
@@ -164,11 +86,11 @@ func TestAggregateAttestations(t *testing.T) {
|
||||
{
|
||||
name: "two attestations with no overlap",
|
||||
inputs: []bitfield.Bitlist{
|
||||
bitfield.Bitlist{0b00000001, 0b1},
|
||||
bitfield.Bitlist{0b00000010, 0b1},
|
||||
{0b00000001, 0b1},
|
||||
{0b00000010, 0b1},
|
||||
},
|
||||
want: []bitfield.Bitlist{
|
||||
bitfield.Bitlist{0b00000011, 0b1},
|
||||
{0b00000011, 0b1},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -188,57 +110,57 @@ func TestAggregateAttestations(t *testing.T) {
|
||||
{
|
||||
name: "two attestations with overlap",
|
||||
inputs: []bitfield.Bitlist{
|
||||
bitfield.Bitlist{0b00000101, 0b1},
|
||||
bitfield.Bitlist{0b00000110, 0b1},
|
||||
{0b00000101, 0b1},
|
||||
{0b00000110, 0b1},
|
||||
},
|
||||
want: []bitfield.Bitlist{
|
||||
bitfield.Bitlist{0b00000101, 0b1},
|
||||
bitfield.Bitlist{0b00000110, 0b1},
|
||||
{0b00000101, 0b1},
|
||||
{0b00000110, 0b1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "some attestations overlap",
|
||||
inputs: []bitfield.Bitlist{
|
||||
bitfield.Bitlist{0b00001001, 0b1},
|
||||
bitfield.Bitlist{0b00010110, 0b1},
|
||||
bitfield.Bitlist{0b00001010, 0b1},
|
||||
bitfield.Bitlist{0b00110001, 0b1},
|
||||
{0b00001001, 0b1},
|
||||
{0b00010110, 0b1},
|
||||
{0b00001010, 0b1},
|
||||
{0b00110001, 0b1},
|
||||
},
|
||||
want: []bitfield.Bitlist{
|
||||
bitfield.Bitlist{0b00111011, 0b1},
|
||||
bitfield.Bitlist{0b00011111, 0b1},
|
||||
{0b00111011, 0b1},
|
||||
{0b00011111, 0b1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "some attestations produce duplicates which are removed",
|
||||
inputs: []bitfield.Bitlist{
|
||||
bitfield.Bitlist{0b00000101, 0b1},
|
||||
bitfield.Bitlist{0b00000110, 0b1},
|
||||
bitfield.Bitlist{0b00001010, 0b1},
|
||||
bitfield.Bitlist{0b00001001, 0b1},
|
||||
{0b00000101, 0b1},
|
||||
{0b00000110, 0b1},
|
||||
{0b00001010, 0b1},
|
||||
{0b00001001, 0b1},
|
||||
},
|
||||
want: []bitfield.Bitlist{
|
||||
bitfield.Bitlist{0b00001111, 0b1}, // both 0&1 and 2&3 produce this bitlist
|
||||
{0b00001111, 0b1}, // both 0&1 and 2&3 produce this bitlist
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "two attestations where one is fully contained within the other",
|
||||
inputs: []bitfield.Bitlist{
|
||||
bitfield.Bitlist{0b00000001, 0b1},
|
||||
bitfield.Bitlist{0b00000011, 0b1},
|
||||
{0b00000001, 0b1},
|
||||
{0b00000011, 0b1},
|
||||
},
|
||||
want: []bitfield.Bitlist{
|
||||
bitfield.Bitlist{0b00000011, 0b1},
|
||||
{0b00000011, 0b1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "two attestations where one is fully contained within the other reversed",
|
||||
inputs: []bitfield.Bitlist{
|
||||
bitfield.Bitlist{0b00000011, 0b1},
|
||||
bitfield.Bitlist{0b00000001, 0b1},
|
||||
{0b00000011, 0b1},
|
||||
{0b00000001, 0b1},
|
||||
},
|
||||
want: []bitfield.Bitlist{
|
||||
bitfield.Bitlist{0b00000011, 0b1},
|
||||
{0b00000011, 0b1},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -4,11 +4,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
)
|
||||
|
||||
// ClearShuffledValidatorCache clears the shuffled indices cache from scratch.
|
||||
func ClearShuffledValidatorCache() {
|
||||
shuffledIndicesCache = cache.NewShuffledIndicesCache()
|
||||
}
|
||||
|
||||
// ClearActiveCountCache restarts the active validator count cache from scratch.
|
||||
func ClearActiveCountCache() {
|
||||
activeCountCache = cache.NewActiveCountCache()
|
||||
@@ -28,5 +23,4 @@ func ActiveIndicesKeys() []string {
|
||||
func ClearAllCaches() {
|
||||
ClearActiveIndicesCache()
|
||||
ClearActiveCountCache()
|
||||
ClearShuffledValidatorCache()
|
||||
}
|
||||
|
||||
@@ -6,82 +6,64 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/sliceutil"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
var shuffledIndicesCache = cache.NewShuffledIndicesCache()
|
||||
var committeeCache = cache.NewCommitteeCache()
|
||||
|
||||
// CommitteeCount returns the number of crosslink committees of an epoch.
|
||||
// CommitteeCountAtSlot returns the number of crosslink committees of a slot.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_committee_count(state: BeaconState, epoch: Epoch) -> uint64:
|
||||
// def get_committee_count_at_slot(state: BeaconState, slot: Slot) -> uint64:
|
||||
// """
|
||||
// Return the number of committees at ``epoch``.
|
||||
// Return the number of committees at ``slot``.
|
||||
// """
|
||||
// committees_per_slot = max(1, min(
|
||||
// SHARD_COUNT // SLOTS_PER_EPOCH,
|
||||
// epoch = compute_epoch_at_slot(slot)
|
||||
// return max(1, min(
|
||||
// MAX_COMMITTEES_PER_SLOT,
|
||||
// len(get_active_validator_indices(state, epoch)) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE,
|
||||
// ))
|
||||
// return committees_per_slot * SLOTS_PER_EPOCH
|
||||
func CommitteeCount(state *pb.BeaconState, epoch uint64) (uint64, error) {
|
||||
if featureconfig.Get().EnableNewCache {
|
||||
count, exists, err := committeeCache.CommitteeCount(epoch)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not interface with committee cache")
|
||||
}
|
||||
if exists {
|
||||
return count, nil
|
||||
}
|
||||
}
|
||||
|
||||
minCommitteePerSlot := uint64(1)
|
||||
// Max committee count per slot will be 0 when shard count is less than epoch length, this
|
||||
// covers the special case to ensure there's always 1 max committee count per slot.
|
||||
var committeeSizesPerSlot = minCommitteePerSlot
|
||||
if params.BeaconConfig().ShardCount/params.BeaconConfig().SlotsPerEpoch > minCommitteePerSlot {
|
||||
committeeSizesPerSlot = params.BeaconConfig().ShardCount / params.BeaconConfig().SlotsPerEpoch
|
||||
}
|
||||
func CommitteeCountAtSlot(state *pb.BeaconState, slot uint64) (uint64, error) {
|
||||
epoch := SlotToEpoch(slot)
|
||||
count, err := ActiveValidatorCount(state, epoch)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not get active count")
|
||||
}
|
||||
|
||||
var currCommitteePerSlot = count / params.BeaconConfig().SlotsPerEpoch / params.BeaconConfig().TargetCommitteeSize
|
||||
|
||||
if currCommitteePerSlot > committeeSizesPerSlot {
|
||||
return committeeSizesPerSlot * params.BeaconConfig().SlotsPerEpoch, nil
|
||||
var committeePerSlot = count / params.BeaconConfig().SlotsPerEpoch / params.BeaconConfig().TargetCommitteeSize
|
||||
if committeePerSlot > params.BeaconConfig().MaxCommitteesPerSlot {
|
||||
return params.BeaconConfig().MaxCommitteesPerSlot, nil
|
||||
}
|
||||
if currCommitteePerSlot < 1 {
|
||||
return minCommitteePerSlot * params.BeaconConfig().SlotsPerEpoch, nil
|
||||
if committeePerSlot == 0 {
|
||||
return 1, nil
|
||||
}
|
||||
return currCommitteePerSlot * params.BeaconConfig().SlotsPerEpoch, nil
|
||||
return committeePerSlot, nil
|
||||
}
|
||||
|
||||
// CrosslinkCommittee returns the crosslink committee of a given epoch.
|
||||
// BeaconCommittee returns the crosslink committee of a given epoch.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_crosslink_committee(state: BeaconState, epoch: Epoch, shard: Shard) -> Sequence[ValidatorIndex]:
|
||||
// def get_beacon_committee(state: BeaconState, slot: Slot, index: CommitteeIndex) -> Sequence[ValidatorIndex]:
|
||||
// """
|
||||
// Return the crosslink committee at ``epoch`` for ``shard``.
|
||||
// Return the beacon committee at ``slot`` for ``index``.
|
||||
// """
|
||||
// epoch = compute_epoch_at_slot(slot)
|
||||
// committees_per_slot = get_committee_count_at_slot(state, slot)
|
||||
// epoch_offset = index + (slot % SLOTS_PER_EPOCH) * committees_per_slot
|
||||
// return compute_committee(
|
||||
// indices=get_active_validator_indices(state, epoch),
|
||||
// seed=get_seed(state, epoch),
|
||||
// index=(shard + SHARD_COUNT - get_start_shard(state, epoch)) % SHARD_COUNT,
|
||||
// count=get_committee_count(state, epoch),
|
||||
// seed=get_seed(state, epoch, DOMAIN_BEACON_ATTESTER),
|
||||
// index=epoch_offset,
|
||||
// count=committees_per_slot * SLOTS_PER_EPOCH,
|
||||
// )
|
||||
func CrosslinkCommittee(state *pb.BeaconState, epoch uint64, shard uint64) ([]uint64, error) {
|
||||
func BeaconCommittee(state *pb.BeaconState, slot uint64, index uint64) ([]uint64, error) {
|
||||
epoch := SlotToEpoch(slot)
|
||||
if featureconfig.Get().EnableNewCache {
|
||||
indices, err := committeeCache.ShuffledIndices(epoch, shard)
|
||||
indices, err := committeeCache.ShuffledIndices(slot, index)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not interface with committee cache")
|
||||
}
|
||||
@@ -90,7 +72,14 @@ func CrosslinkCommittee(state *pb.BeaconState, epoch uint64, shard uint64) ([]ui
|
||||
}
|
||||
}
|
||||
|
||||
seed, err := Seed(state, epoch)
|
||||
committeesPerSlot, err := CommitteeCountAtSlot(state, slot)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get committee count at slot")
|
||||
}
|
||||
epochOffset := index + (slot%params.BeaconConfig().SlotsPerEpoch)*committeesPerSlot
|
||||
count := committeesPerSlot * params.BeaconConfig().SlotsPerEpoch
|
||||
|
||||
seed, err := Seed(state, epoch, params.BeaconConfig().DomainBeaconAttester)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get seed")
|
||||
}
|
||||
@@ -99,20 +88,7 @@ func CrosslinkCommittee(state *pb.BeaconState, epoch uint64, shard uint64) ([]ui
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get active indices")
|
||||
}
|
||||
|
||||
startShard, err := StartShard(state, epoch)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get start shard")
|
||||
}
|
||||
|
||||
shardCount := params.BeaconConfig().ShardCount
|
||||
currentShard := (shard + shardCount - startShard) % shardCount
|
||||
committeeCount, err := CommitteeCount(state, epoch)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get committee count")
|
||||
}
|
||||
|
||||
return ComputeCommittee(indices, seed, currentShard, committeeCount)
|
||||
return ComputeCommittee(indices, seed, epochOffset, count)
|
||||
}
|
||||
|
||||
// ComputeCommittee returns the requested shuffled committee out of the total committees using
|
||||
@@ -130,40 +106,25 @@ func CrosslinkCommittee(state *pb.BeaconState, epoch uint64, shard uint64) ([]ui
|
||||
// end = (len(indices) * (index + 1)) // count
|
||||
// return [indices[compute_shuffled_index(ValidatorIndex(i), len(indices), seed)] for i in range(start, end)
|
||||
func ComputeCommittee(
|
||||
validatorIndices []uint64,
|
||||
indices []uint64,
|
||||
seed [32]byte,
|
||||
indexShard uint64,
|
||||
totalCommittees uint64,
|
||||
index uint64,
|
||||
count uint64,
|
||||
) ([]uint64, error) {
|
||||
validatorCount := uint64(len(validatorIndices))
|
||||
start := sliceutil.SplitOffset(validatorCount, totalCommittees, indexShard)
|
||||
end := sliceutil.SplitOffset(validatorCount, totalCommittees, indexShard+1)
|
||||
validatorCount := uint64(len(indices))
|
||||
start := sliceutil.SplitOffset(validatorCount, count, index)
|
||||
end := sliceutil.SplitOffset(validatorCount, count, index+1)
|
||||
|
||||
// Use cached shuffled indices list if we have seen the seed before.
|
||||
cachedShuffledList, err := shuffledIndicesCache.IndicesByIndexSeed(indexShard, seed[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cachedShuffledList != nil {
|
||||
return cachedShuffledList, nil
|
||||
}
|
||||
|
||||
// Save the shuffled indices in cache, this is only needed once per epoch or once per new shard index.
|
||||
// Save the shuffled indices in cache, this is only needed once per epoch or once per new committee index.
|
||||
shuffledIndices := make([]uint64, end-start)
|
||||
for i := start; i < end; i++ {
|
||||
permutedIndex, err := ShuffledIndex(i, validatorCount, seed)
|
||||
if err != nil {
|
||||
return []uint64{}, errors.Wrapf(err, "could not get shuffled index at index %d", i)
|
||||
}
|
||||
shuffledIndices[i-start] = validatorIndices[permutedIndex]
|
||||
}
|
||||
if err := shuffledIndicesCache.AddShuffledValidatorList(&cache.IndicesByIndexSeed{
|
||||
Index: indexShard,
|
||||
Seed: seed[:],
|
||||
ShuffledIndices: shuffledIndices,
|
||||
}); err != nil {
|
||||
return []uint64{}, errors.Wrap(err, "could not add shuffled indices list to cache")
|
||||
shuffledIndices[i-start] = indices[permutedIndex]
|
||||
}
|
||||
|
||||
return shuffledIndices, nil
|
||||
}
|
||||
|
||||
@@ -176,10 +137,10 @@ func ComputeCommittee(
|
||||
// """
|
||||
// Return the set of attesting indices corresponding to ``data`` and ``bits``.
|
||||
// """
|
||||
// committee = get_crosslink_committee(state, data.target.epoch, data.crosslink.shard)
|
||||
// committee = get_beacon_committee(state, data.slot, data.index)
|
||||
// return set(index for i, index in enumerate(committee) if bits[i])
|
||||
func AttestingIndices(state *pb.BeaconState, data *ethpb.AttestationData, bf bitfield.Bitfield) ([]uint64, error) {
|
||||
committee, err := CrosslinkCommittee(state, data.Target.Epoch, data.Crosslink.Shard)
|
||||
committee, err := BeaconCommittee(state, data.Slot, data.Index)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get committee")
|
||||
}
|
||||
@@ -197,6 +158,77 @@ func AttestingIndices(state *pb.BeaconState, data *ethpb.AttestationData, bf bit
|
||||
return indices, nil
|
||||
}
|
||||
|
||||
// CommitteeAssignment is used to query committee assignment from
|
||||
// current and previous epoch.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_committee_assignment(state: BeaconState,
|
||||
// epoch: Epoch,
|
||||
// validator_index: ValidatorIndex
|
||||
// ) -> Optional[Tuple[Sequence[ValidatorIndex], CommitteeIndex, Slot]]:
|
||||
// """
|
||||
// Return the committee assignment in the ``epoch`` for ``validator_index``.
|
||||
// ``assignment`` returned is a tuple of the following form:
|
||||
// * ``assignment[0]`` is the list of validators in the committee
|
||||
// * ``assignment[1]`` is the index to which the committee is assigned
|
||||
// * ``assignment[2]`` is the slot at which the committee is assigned
|
||||
// Return None if no assignment.
|
||||
// """
|
||||
// next_epoch = get_current_epoch(state) + 1
|
||||
// assert epoch <= next_epoch
|
||||
//
|
||||
// start_slot = compute_start_slot_at_epoch(epoch)
|
||||
// for slot in range(start_slot, start_slot + SLOTS_PER_EPOCH):
|
||||
// for index in range(get_committee_count_at_slot(state, Slot(slot))):
|
||||
// committee = get_beacon_committee(state, Slot(slot), CommitteeIndex(index))
|
||||
// if validator_index in committee:
|
||||
// return committee, CommitteeIndex(index), Slot(slot)
|
||||
// return None
|
||||
func CommitteeAssignment(
|
||||
state *pb.BeaconState,
|
||||
epoch uint64,
|
||||
validatorIndex uint64,
|
||||
) ([]uint64, uint64, uint64, uint64, error) {
|
||||
|
||||
if epoch > NextEpoch(state) {
|
||||
return nil, 0, 0, 0, fmt.Errorf(
|
||||
"epoch %d can't be greater than next epoch %d",
|
||||
epoch, NextEpoch(state))
|
||||
}
|
||||
|
||||
// Track which slot has which proposer.
|
||||
startSlot := StartSlot(epoch)
|
||||
proposerIndexToSlot := make(map[uint64]uint64)
|
||||
for slot := startSlot; slot < startSlot+params.BeaconConfig().SlotsPerEpoch; slot++ {
|
||||
state.Slot = slot
|
||||
i, err := BeaconProposerIndex(state)
|
||||
if err != nil {
|
||||
return nil, 0, 0, 0, errors.Wrapf(err, "could not check proposer at slot %d", state.Slot)
|
||||
}
|
||||
proposerIndexToSlot[i] = slot
|
||||
}
|
||||
|
||||
for slot := startSlot; slot < startSlot+params.BeaconConfig().SlotsPerEpoch; slot++ {
|
||||
countAtSlot, err := CommitteeCountAtSlot(state, slot)
|
||||
if err != nil {
|
||||
return nil, 0, 0, 0, errors.Wrapf(err, "could not get committee count at slot %d", slot)
|
||||
}
|
||||
for i := uint64(0); i < countAtSlot; i++ {
|
||||
committee, err := BeaconCommittee(state, slot, i)
|
||||
if err != nil {
|
||||
return nil, 0, 0, 0, errors.Wrapf(err, "could not get crosslink committee at slot %d", slot)
|
||||
}
|
||||
for _, v := range committee {
|
||||
if validatorIndex == v {
|
||||
proposerSlot, _ := proposerIndexToSlot[v]
|
||||
return committee, i, slot, proposerSlot, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return []uint64{}, 0, 0, 0, fmt.Errorf("validator with index %d not found in assignments", validatorIndex)
|
||||
}
|
||||
|
||||
// VerifyBitfieldLength verifies that a bitfield length matches the given committee size.
|
||||
func VerifyBitfieldLength(bf bitfield.Bitfield, committeeSize uint64) error {
|
||||
if bf.Len() != committeeSize {
|
||||
@@ -208,182 +240,16 @@ func VerifyBitfieldLength(bf bitfield.Bitfield, committeeSize uint64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CommitteeAssignment is used to query committee assignment from
|
||||
// current and previous epoch.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_committee_assignment(state: BeaconState,
|
||||
// epoch: Epoch,
|
||||
// validator_index: ValidatorIndex) -> Optional[Tuple[Sequence[ValidatorIndex], Shard, Slot]]:
|
||||
// """
|
||||
// Return the committee assignment in the ``epoch`` for ``validator_index``.
|
||||
// ``assignment`` returned is a tuple of the following form:
|
||||
// * ``assignment[0]`` is the list of validators in the committee
|
||||
// * ``assignment[1]`` is the shard to which the committee is assigned
|
||||
// * ``assignment[2]`` is the slot at which the committee is assigned
|
||||
// Return None if no assignment.
|
||||
// """
|
||||
// next_epoch = get_current_epoch(state) + 1
|
||||
// assert epoch <= next_epoch
|
||||
//
|
||||
// committees_per_slot = get_committee_count(state, epoch) // SLOTS_PER_EPOCH
|
||||
// start_slot = compute_start_slot_of_epoch(epoch)
|
||||
// for slot in range(start_slot, start_slot + SLOTS_PER_EPOCH):
|
||||
// offset = committees_per_slot * (slot % SLOTS_PER_EPOCH)
|
||||
// slot_start_shard = (get_start_shard(state, epoch) + offset) % SHARD_COUNT
|
||||
// for i in range(committees_per_slot):
|
||||
// shard = Shard((slot_start_shard + i) % SHARD_COUNT)
|
||||
// committee = get_crosslink_committee(state, epoch, shard)
|
||||
// if validator_index in committee:
|
||||
// return committee, shard, Slot(slot)
|
||||
// return None
|
||||
func CommitteeAssignment(
|
||||
state *pb.BeaconState,
|
||||
epoch uint64,
|
||||
validatorIndex uint64) ([]uint64, uint64, uint64, bool, error) {
|
||||
|
||||
if epoch > NextEpoch(state) {
|
||||
return nil, 0, 0, false, fmt.Errorf(
|
||||
"epoch %d can't be greater than next epoch %d",
|
||||
epoch, NextEpoch(state))
|
||||
}
|
||||
|
||||
committeeCount, err := CommitteeCount(state, epoch)
|
||||
if err != nil {
|
||||
return nil, 0, 0, false, errors.Wrap(err, "could not get committee count")
|
||||
}
|
||||
committeesPerSlot := committeeCount / params.BeaconConfig().SlotsPerEpoch
|
||||
|
||||
epochStartShard, err := StartShard(state, epoch)
|
||||
if err != nil {
|
||||
return nil, 0, 0, false, fmt.Errorf(
|
||||
"could not get epoch start shard: %v", err)
|
||||
}
|
||||
startSlot := StartSlot(epoch)
|
||||
for slot := startSlot; slot < startSlot+params.BeaconConfig().SlotsPerEpoch; slot++ {
|
||||
offset := committeesPerSlot * (slot % params.BeaconConfig().SlotsPerEpoch)
|
||||
slotStatShard := (epochStartShard + offset) % params.BeaconConfig().ShardCount
|
||||
for i := uint64(0); i < committeesPerSlot; i++ {
|
||||
shard := (slotStatShard + i) % params.BeaconConfig().ShardCount
|
||||
committee, err := CrosslinkCommittee(state, epoch, shard)
|
||||
if err != nil {
|
||||
return nil, 0, 0, false, fmt.Errorf(
|
||||
"could not get crosslink committee: %v", err)
|
||||
}
|
||||
for _, index := range committee {
|
||||
if validatorIndex == index {
|
||||
state.Slot = slot
|
||||
proposerIndex, err := BeaconProposerIndex(state)
|
||||
if err != nil {
|
||||
return nil, 0, 0, false, fmt.Errorf(
|
||||
"could not check proposer index: %v", err)
|
||||
}
|
||||
isProposer := proposerIndex == validatorIndex
|
||||
return committee, shard, slot, isProposer, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return []uint64{}, 0, 0, false, status.Error(codes.NotFound, "validator not found in assignments")
|
||||
}
|
||||
|
||||
// ShardDelta returns the minimum number of shards get processed in one epoch.
|
||||
//
|
||||
// Note: if you already have the committee count,
|
||||
// use shardDeltaFromCommitteeCount as CommitteeCount (specifically
|
||||
// ActiveValidatorCount) iterates over the entire validator set.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_shard_delta(state: BeaconState, epoch: Epoch) -> uint64:
|
||||
// """
|
||||
// Return the number of shards to increment ``state.start_shard`` at ``epoch``.
|
||||
// """
|
||||
// return min(get_committee_count(state, epoch), SHARD_COUNT - SHARD_COUNT // SLOTS_PER_EPOCH)
|
||||
func ShardDelta(beaconState *pb.BeaconState, epoch uint64) (uint64, error) {
|
||||
committeeCount, err := CommitteeCount(beaconState, epoch)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not get committee count")
|
||||
}
|
||||
return shardDeltaFromCommitteeCount(committeeCount), nil
|
||||
}
|
||||
|
||||
// shardDeltaFromCommitteeCount returns the number of shards that get processed
|
||||
// in one epoch. This method is the inner logic of ShardDelta.
|
||||
// Returns the minimum of the committeeCount and maximum shard delta which is
|
||||
// defined as SHARD_COUNT - SHARD_COUNT // SLOTS_PER_EPOCH.
|
||||
func shardDeltaFromCommitteeCount(committeeCount uint64) uint64 {
|
||||
shardCount := params.BeaconConfig().ShardCount
|
||||
maxShardDelta := shardCount - shardCount/params.BeaconConfig().SlotsPerEpoch
|
||||
if committeeCount < maxShardDelta {
|
||||
return committeeCount
|
||||
}
|
||||
return maxShardDelta
|
||||
}
|
||||
|
||||
// StartShard returns the start shard used to process crosslink
|
||||
// of a given epoch. The start shard is cached using epoch as key,
|
||||
// it gets rewritten where there's a reorg or a new finalized block.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_start_shard(state: BeaconState, epoch: Epoch) -> Shard:
|
||||
// """
|
||||
// Return the start shard of the 0th committee at ``epoch``.
|
||||
// """
|
||||
// assert epoch <= get_current_epoch(state) + 1
|
||||
// check_epoch = Epoch(get_current_epoch(state) + 1)
|
||||
// shard = Shard((state.start_shard + get_shard_delta(state, get_current_epoch(state))) % SHARD_COUNT)
|
||||
// while check_epoch > epoch:
|
||||
// check_epoch -= Epoch(1)
|
||||
// shard = Shard((shard + SHARD_COUNT - get_shard_delta(state, check_epoch)) % SHARD_COUNT)
|
||||
// return shard
|
||||
func StartShard(state *pb.BeaconState, epoch uint64) (uint64, error) {
|
||||
if featureconfig.Get().EnableNewCache {
|
||||
startShard, exists, err := committeeCache.StartShard(epoch)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not interface with committee cache")
|
||||
}
|
||||
if exists {
|
||||
return startShard, nil
|
||||
}
|
||||
}
|
||||
|
||||
currentEpoch := CurrentEpoch(state)
|
||||
checkEpoch := currentEpoch + 1
|
||||
|
||||
if epoch > checkEpoch {
|
||||
return 0, fmt.Errorf("epoch %d can't be greater than %d",
|
||||
epoch, checkEpoch)
|
||||
}
|
||||
|
||||
delta, err := ShardDelta(state, currentEpoch)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not get shard delta")
|
||||
}
|
||||
|
||||
startShard := (state.StartShard + delta) % params.BeaconConfig().ShardCount
|
||||
for checkEpoch > epoch {
|
||||
checkEpoch--
|
||||
d, err := ShardDelta(state, checkEpoch)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not get shard delta")
|
||||
}
|
||||
startShard = (startShard + params.BeaconConfig().ShardCount - d) % params.BeaconConfig().ShardCount
|
||||
}
|
||||
|
||||
return startShard, nil
|
||||
}
|
||||
|
||||
// VerifyAttestationBitfieldLengths verifies that an attestations aggregation and custody bitfields are
|
||||
// a valid length matching the size of the committee.
|
||||
func VerifyAttestationBitfieldLengths(bState *pb.BeaconState, att *ethpb.Attestation) error {
|
||||
committee, err := CrosslinkCommittee(bState, att.Data.Target.Epoch, att.Data.Crosslink.Shard)
|
||||
committee, err := BeaconCommittee(bState, att.Data.Slot, att.Data.Index)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not retrieve crosslink committees")
|
||||
return errors.Wrap(err, "could not retrieve beacon committees")
|
||||
}
|
||||
|
||||
if committee == nil {
|
||||
return errors.New("no committee exist for shard in the attestation")
|
||||
return errors.New("no committee exist for this attestation")
|
||||
}
|
||||
|
||||
if err := VerifyBitfieldLength(att.AggregationBits, uint64(len(committee))); err != nil {
|
||||
@@ -395,95 +261,10 @@ func VerifyAttestationBitfieldLengths(bState *pb.BeaconState, att *ethpb.Attesta
|
||||
return nil
|
||||
}
|
||||
|
||||
// CompactCommitteesRoot returns the index root of a given epoch.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_compact_committees_root(state: BeaconState, epoch: Epoch) -> Hash:
|
||||
// """
|
||||
// Return the compact committee root at ``epoch``.
|
||||
// """
|
||||
// committees = [CompactCommittee() for _ in range(SHARD_COUNT)]
|
||||
// start_shard = get_epoch_start_shard(state, epoch)
|
||||
// for committee_number in range(get_epoch_committee_count(state, epoch)):
|
||||
// shard = Shard((start_shard + committee_number) % SHARD_COUNT)
|
||||
// for index in get_crosslink_committee(state, epoch, shard):
|
||||
// validator = state.validators[index]
|
||||
// committees[shard].pubkeys.append(validator.pubkey)
|
||||
// compact_balance = validator.effective_balance // EFFECTIVE_BALANCE_INCREMENT
|
||||
// # `index` (top 6 bytes) + `slashed` (16th bit) + `compact_balance` (bottom 15 bits)
|
||||
// compact_validator = uint64((index << 16) + (validator.slashed << 15) + compact_balance)
|
||||
// committees[shard].compact_validators.append(compact_validator)
|
||||
// return hash_tree_root(Vector[CompactCommittee, SHARD_COUNT](committees))
|
||||
func CompactCommitteesRoot(state *pb.BeaconState, epoch uint64) ([32]byte, error) {
|
||||
shardCount := params.BeaconConfig().ShardCount
|
||||
switch shardCount {
|
||||
case 1024:
|
||||
compactCommArray := [1024]*pb.CompactCommittee{}
|
||||
for i := range compactCommArray {
|
||||
compactCommArray[i] = &pb.CompactCommittee{}
|
||||
}
|
||||
comCount, err := CommitteeCount(state, epoch)
|
||||
if err != nil {
|
||||
return [32]byte{}, err
|
||||
}
|
||||
startShard, err := StartShard(state, epoch)
|
||||
if err != nil {
|
||||
return [32]byte{}, err
|
||||
}
|
||||
|
||||
for i := uint64(0); i < comCount; i++ {
|
||||
shard := (startShard + i) % shardCount
|
||||
crossComm, err := CrosslinkCommittee(state, epoch, shard)
|
||||
if err != nil {
|
||||
return [32]byte{}, err
|
||||
}
|
||||
|
||||
for _, index := range crossComm {
|
||||
validator := state.Validators[index]
|
||||
compactCommArray[shard].Pubkeys = append(compactCommArray[shard].Pubkeys, validator.PublicKey)
|
||||
compactValidator := compressValidator(validator, index)
|
||||
compactCommArray[shard].CompactValidators = append(compactCommArray[shard].CompactValidators, compactValidator)
|
||||
}
|
||||
}
|
||||
return ssz.HashTreeRoot(compactCommArray)
|
||||
case 8:
|
||||
compactCommArray := [8]*pb.CompactCommittee{}
|
||||
for i := range compactCommArray {
|
||||
compactCommArray[i] = &pb.CompactCommittee{}
|
||||
}
|
||||
comCount, err := CommitteeCount(state, epoch)
|
||||
if err != nil {
|
||||
return [32]byte{}, err
|
||||
}
|
||||
startShard, err := StartShard(state, epoch)
|
||||
if err != nil {
|
||||
return [32]byte{}, err
|
||||
}
|
||||
for i := uint64(0); i < comCount; i++ {
|
||||
shard := (startShard + i) % shardCount
|
||||
crossComm, err := CrosslinkCommittee(state, epoch, shard)
|
||||
if err != nil {
|
||||
return [32]byte{}, err
|
||||
}
|
||||
|
||||
for _, index := range crossComm {
|
||||
validator := state.Validators[index]
|
||||
compactCommArray[shard].Pubkeys = append(compactCommArray[shard].Pubkeys, validator.PublicKey)
|
||||
compactValidator := compressValidator(validator, index)
|
||||
compactCommArray[shard].CompactValidators = append(compactCommArray[shard].CompactValidators, compactValidator)
|
||||
}
|
||||
}
|
||||
return ssz.HashTreeRoot(compactCommArray)
|
||||
default:
|
||||
return [32]byte{}, fmt.Errorf("expected minimal or mainnet config shard count, received %d", shardCount)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 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(state *pb.BeaconState, epoch uint64) ([]uint64, error) {
|
||||
seed, err := Seed(state, epoch)
|
||||
seed, err := Seed(state, epoch, params.BeaconConfig().DomainBeaconAttester)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not get seed for epoch %d", epoch)
|
||||
}
|
||||
@@ -507,7 +288,7 @@ func ShuffledIndices(state *pb.BeaconState, epoch uint64) ([]uint64, error) {
|
||||
}
|
||||
|
||||
// UpdateCommitteeCache gets called at the beginning of every epoch to cache the committee shuffled indices
|
||||
// list with start shard and epoch number. It caches the shuffled indices for current epoch and next epoch.
|
||||
// list with committee index and epoch number. It caches the shuffled indices for current epoch and next epoch.
|
||||
func UpdateCommitteeCache(state *pb.BeaconState) error {
|
||||
currentEpoch := CurrentEpoch(state)
|
||||
for _, epoch := range []uint64{currentEpoch, currentEpoch + 1} {
|
||||
@@ -515,42 +296,17 @@ func UpdateCommitteeCache(state *pb.BeaconState) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
startShard, err := StartShard(state, epoch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
committeeCount, err := CommitteeCount(state, epoch)
|
||||
count, err := CommitteeCountAtSlot(state, epoch*params.BeaconConfig().SlotsPerEpoch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := committeeCache.AddCommitteeShuffledList(&cache.Committee{
|
||||
Epoch: epoch,
|
||||
Committee: committees,
|
||||
StartShard: startShard,
|
||||
CommitteeCount: committeeCount,
|
||||
CommitteeCount: count * params.BeaconConfig().SlotsPerEpoch,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// compressValidator compacts all the validator data such as validator index, slashing info and balance
|
||||
// into a single uint64 field.
|
||||
//
|
||||
// Spec reference:
|
||||
// # `index` (top 6 bytes) + `slashed` (16th bit) + `compact_balance` (bottom 15 bits)
|
||||
// compact_validator = uint64((index << 16) + (validator.slashed << 15) + compact_balance)
|
||||
func compressValidator(validator *ethpb.Validator, idx uint64) uint64 {
|
||||
compactBalance := validator.EffectiveBalance / params.BeaconConfig().EffectiveBalanceIncrement
|
||||
// index (top 6 bytes) + slashed (16th bit) + compact_balance (bottom 15 bits)
|
||||
compactIndex := idx << 16
|
||||
var slashedBit uint64
|
||||
if validator.Slashed {
|
||||
slashedBit = 1 << 15
|
||||
}
|
||||
// Clear all bits except last 15.
|
||||
compactBalance &= 0x7FFF // 0b01111111 0b11111111
|
||||
compactValidator := compactIndex | slashedBit | compactBalance
|
||||
return compactValidator
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,54 +15,28 @@ var ErrInvalidStateLatestActiveIndexRoots = errors.New("state does not have corr
|
||||
// Seed returns the randao seed used for shuffling of a given epoch.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_seed(state: BeaconState, epoch: Epoch) -> Hash:
|
||||
// def get_seed(state: BeaconState, epoch: Epoch, domain_type: DomainType) -> Hash:
|
||||
// """
|
||||
// Return the seed at ``epoch``.
|
||||
// """
|
||||
// mix = get_randao_mix(state, Epoch(epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD - 1)) #Avoid underflow
|
||||
// active_index_root = state.active_index_roots[epoch % EPOCHS_PER_HISTORICAL_VECTOR]
|
||||
// return hash(mix + active_index_root + int_to_bytes(epoch, length=32))
|
||||
func Seed(state *pb.BeaconState, epoch uint64) ([32]byte, error) {
|
||||
// mix = get_randao_mix(state, Epoch(epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD - 1)) # Avoid underflow
|
||||
// return hash(domain_type + int_to_bytes(epoch, length=8) + mix)
|
||||
func Seed(state *pb.BeaconState, epoch uint64, domain []byte) ([32]byte, error) {
|
||||
// See https://github.com/ethereum/eth2.0-specs/pull/1296 for
|
||||
// rationale on why offset has to look down by 1.
|
||||
lookAheadEpoch := epoch + params.BeaconConfig().EpochsPerHistoricalVector -
|
||||
params.BeaconConfig().MinSeedLookahead - 1
|
||||
|
||||
// Check that the state has the correct latest active index roots or
|
||||
// randao mix may panic for index out of bounds.
|
||||
if uint64(len(state.ActiveIndexRoots)) != params.BeaconConfig().EpochsPerHistoricalVector {
|
||||
return [32]byte{}, ErrInvalidStateLatestActiveIndexRoots
|
||||
}
|
||||
randaoMix := RandaoMix(state, lookAheadEpoch)
|
||||
|
||||
indexRoot := ActiveIndexRoot(state, epoch)
|
||||
seed := append(domain, bytesutil.Bytes8(epoch)...)
|
||||
seed = append(seed, randaoMix...)
|
||||
|
||||
th := append(randaoMix, indexRoot...)
|
||||
th = append(th, bytesutil.Bytes32(epoch)...)
|
||||
|
||||
seed32 := hashutil.Hash(th)
|
||||
seed32 := hashutil.Hash(seed)
|
||||
|
||||
return seed32, nil
|
||||
}
|
||||
|
||||
// ActiveIndexRoot returns the index root of a given epoch.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_active_index_root(state: BeaconState,
|
||||
// epoch: Epoch) -> Bytes32:
|
||||
// """
|
||||
// Return the index root at a recent ``epoch``.
|
||||
// ``epoch`` expected to be between
|
||||
// (current_epoch - LATEST_ACTIVE_INDEX_ROOTS_LENGTH + ACTIVATION_EXIT_DELAY, current_epoch + ACTIVATION_EXIT_DELAY].
|
||||
// """
|
||||
// return state.latest_active_index_roots[epoch % LATEST_ACTIVE_INDEX_ROOTS_LENGTH]
|
||||
func ActiveIndexRoot(state *pb.BeaconState, epoch uint64) []byte {
|
||||
newRootLength := len(state.ActiveIndexRoots[epoch%params.BeaconConfig().EpochsPerHistoricalVector])
|
||||
newRoot := make([]byte, newRootLength)
|
||||
copy(newRoot, state.ActiveIndexRoots[epoch%params.BeaconConfig().EpochsPerHistoricalVector])
|
||||
return newRoot
|
||||
}
|
||||
|
||||
// RandaoMix returns the randao mix (xor'ed seed)
|
||||
// of a given slot. It is used to shuffle validators.
|
||||
//
|
||||
|
||||
@@ -87,86 +87,8 @@ func TestRandaoMix_CopyOK(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestActiveIndexRoot_OK(t *testing.T) {
|
||||
|
||||
activeIndexRoots := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
|
||||
for i := 0; i < len(activeIndexRoots); i++ {
|
||||
intInBytes := make([]byte, 32)
|
||||
binary.LittleEndian.PutUint64(intInBytes, uint64(i))
|
||||
activeIndexRoots[i] = intInBytes
|
||||
}
|
||||
state := &pb.BeaconState{ActiveIndexRoots: activeIndexRoots}
|
||||
tests := []struct {
|
||||
epoch uint64
|
||||
}{
|
||||
{
|
||||
epoch: 34,
|
||||
},
|
||||
{
|
||||
epoch: 3444,
|
||||
},
|
||||
{
|
||||
epoch: 999999,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
state.Slot = (test.epoch) * params.BeaconConfig().SlotsPerEpoch
|
||||
for i := 0; i <= int(params.BeaconConfig().ActivationExitDelay); i++ {
|
||||
indexRoot := ActiveIndexRoot(state, test.epoch+uint64(i))
|
||||
|
||||
if !bytes.Equal(activeIndexRoots[(test.epoch+uint64(i))%params.BeaconConfig().EpochsPerHistoricalVector], indexRoot) {
|
||||
t.Errorf("Incorrect index root. Wanted: %#x, got: %#x",
|
||||
activeIndexRoots[(test.epoch+uint64(i))%params.BeaconConfig().EpochsPerHistoricalVector], indexRoot)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestActiveIndexRoot_CopyOK(t *testing.T) {
|
||||
ClearAllCaches()
|
||||
conf := params.BeaconConfig()
|
||||
conf.EpochsPerHistoricalVector = 100
|
||||
params.OverrideBeaconConfig(conf)
|
||||
activeIndexRoots := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
|
||||
for i := 0; i < len(activeIndexRoots); i++ {
|
||||
intInBytes := make([]byte, 32)
|
||||
binary.LittleEndian.PutUint64(intInBytes, uint64(i))
|
||||
activeIndexRoots[i] = intInBytes
|
||||
}
|
||||
state := &pb.BeaconState{ActiveIndexRoots: activeIndexRoots}
|
||||
tests := []struct {
|
||||
epoch uint64
|
||||
}{
|
||||
{
|
||||
epoch: 34,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
state.Slot = (test.epoch) * params.BeaconConfig().SlotsPerEpoch
|
||||
indexRoot := ActiveIndexRoot(state, test.epoch)
|
||||
uniqueNumber := params.BeaconConfig().EpochsPerHistoricalVector + 1000
|
||||
binary.LittleEndian.PutUint64(indexRoot, uniqueNumber)
|
||||
|
||||
for _, root := range activeIndexRoots {
|
||||
rootNum := bytesutil.FromBytes8(root)
|
||||
if rootNum == uniqueNumber {
|
||||
t.Fatalf("two distinct slices which have different representations in memory still contain"+
|
||||
"the same value: %d", rootNum)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateSeed_OK(t *testing.T) {
|
||||
ClearAllCaches()
|
||||
|
||||
activeIndexRoots := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
|
||||
for i := 0; i < len(activeIndexRoots); i++ {
|
||||
intInBytes := make([]byte, 32)
|
||||
binary.LittleEndian.PutUint64(intInBytes, uint64(i))
|
||||
activeIndexRoots[i] = intInBytes
|
||||
}
|
||||
randaoMixes := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
|
||||
for i := 0; i < len(randaoMixes); i++ {
|
||||
intInBytes := make([]byte, 32)
|
||||
@@ -175,17 +97,16 @@ func TestGenerateSeed_OK(t *testing.T) {
|
||||
}
|
||||
slot := 10 * params.BeaconConfig().MinSeedLookahead * params.BeaconConfig().SlotsPerEpoch
|
||||
state := &pb.BeaconState{
|
||||
ActiveIndexRoots: activeIndexRoots,
|
||||
RandaoMixes: randaoMixes,
|
||||
Slot: slot}
|
||||
RandaoMixes: randaoMixes,
|
||||
Slot: slot}
|
||||
|
||||
got, err := Seed(state, 10)
|
||||
got, err := Seed(state, 10, params.BeaconConfig().DomainBeaconAttester)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
wanted := [32]byte{141, 205, 112, 76, 60, 173, 127, 10, 1, 214, 151, 41, 69, 40, 108, 88, 247,
|
||||
210, 88, 5, 150, 112, 64, 93, 208, 110, 194, 137, 234, 180, 40, 245}
|
||||
wanted := [32]byte{102, 82, 23, 40, 226, 79, 171, 11, 203, 23, 175, 7, 88, 202, 80,
|
||||
103, 68, 126, 195, 143, 190, 249, 210, 85, 138, 196, 158, 208, 11, 18, 136, 23}
|
||||
if got != wanted {
|
||||
t.Errorf("Incorrect generated seeds. Got: %v, wanted: %v",
|
||||
got, wanted)
|
||||
|
||||
@@ -35,15 +35,16 @@ func SplitIndices(l []uint64, n uint64) [][]uint64 {
|
||||
// constant between iterations instead of reallocating it each iteration as in the spec. This implementation is based
|
||||
// on the original implementation from protolambda, https://github.com/protolambda/eth2-shuffle
|
||||
func ShuffledIndex(index uint64, indexCount uint64, seed [32]byte) (uint64, error) {
|
||||
return innerShuffledIndex(index, indexCount, seed, true /* shuffle */)
|
||||
return ComputeShuffledIndex(index, indexCount, seed, true /* shuffle */)
|
||||
}
|
||||
|
||||
// UnShuffledIndex returns the inverse of ShuffledIndex. This implementation is based
|
||||
// on the original implementation from protolambda, https://github.com/protolambda/eth2-shuffle
|
||||
func UnShuffledIndex(index uint64, indexCount uint64, seed [32]byte) (uint64, error) {
|
||||
return innerShuffledIndex(index, indexCount, seed, false /* un-shuffle */)
|
||||
return ComputeShuffledIndex(index, indexCount, seed, false /* un-shuffle */)
|
||||
}
|
||||
|
||||
// ComputeShuffledIndex returns the shuffled validator index corresponding to seed and index count.
|
||||
// Spec pseudocode definition:
|
||||
// def compute_shuffled_index(index: ValidatorIndex, index_count: uint64, seed: Hash) -> ValidatorIndex:
|
||||
// """
|
||||
@@ -63,7 +64,7 @@ func UnShuffledIndex(index uint64, indexCount uint64, seed [32]byte) (uint64, er
|
||||
// index = flip if bit else index
|
||||
//
|
||||
// return ValidatorIndex(index)
|
||||
func innerShuffledIndex(index uint64, indexCount uint64, seed [32]byte, shuffle bool) (uint64, error) {
|
||||
func ComputeShuffledIndex(index uint64, indexCount uint64, seed [32]byte, shuffle bool) (uint64, error) {
|
||||
if params.BeaconConfig().ShuffleRoundCount == 0 {
|
||||
return index, nil
|
||||
}
|
||||
|
||||
@@ -13,10 +13,10 @@ func TestSlotToEpoch_OK(t *testing.T) {
|
||||
epoch uint64
|
||||
}{
|
||||
{slot: 0, epoch: 0},
|
||||
{slot: 50, epoch: 0},
|
||||
{slot: 64, epoch: 1},
|
||||
{slot: 128, epoch: 2},
|
||||
{slot: 200, epoch: 3},
|
||||
{slot: 50, epoch: 1},
|
||||
{slot: 64, epoch: 2},
|
||||
{slot: 128, epoch: 4},
|
||||
{slot: 200, epoch: 6},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
if tt.epoch != SlotToEpoch(tt.slot) {
|
||||
@@ -31,10 +31,10 @@ func TestCurrentEpoch_OK(t *testing.T) {
|
||||
epoch uint64
|
||||
}{
|
||||
{slot: 0, epoch: 0},
|
||||
{slot: 50, epoch: 0},
|
||||
{slot: 64, epoch: 1},
|
||||
{slot: 128, epoch: 2},
|
||||
{slot: 200, epoch: 3},
|
||||
{slot: 50, epoch: 1},
|
||||
{slot: 64, epoch: 2},
|
||||
{slot: 128, epoch: 4},
|
||||
{slot: 200, epoch: 6},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
state := &pb.BeaconState{Slot: tt.slot}
|
||||
@@ -67,7 +67,7 @@ func TestNextEpoch_OK(t *testing.T) {
|
||||
epoch uint64
|
||||
}{
|
||||
{slot: 0, epoch: 0/params.BeaconConfig().SlotsPerEpoch + 1},
|
||||
{slot: 50, epoch: 0/params.BeaconConfig().SlotsPerEpoch + 1},
|
||||
{slot: 50, epoch: 0/params.BeaconConfig().SlotsPerEpoch + 2},
|
||||
{slot: 64, epoch: 64/params.BeaconConfig().SlotsPerEpoch + 1},
|
||||
{slot: 128, epoch: 128/params.BeaconConfig().SlotsPerEpoch + 1},
|
||||
{slot: 200, epoch: 200/params.BeaconConfig().SlotsPerEpoch + 1},
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
@@ -135,7 +133,7 @@ func ActiveValidatorCount(state *pb.BeaconState, epoch uint64) (uint64, error) {
|
||||
// """
|
||||
// return Epoch(epoch + 1 + ACTIVATION_EXIT_DELAY)
|
||||
func DelayedActivationExitEpoch(epoch uint64) uint64 {
|
||||
return epoch + 1 + params.BeaconConfig().ActivationExitDelay
|
||||
return epoch + 1 + params.BeaconConfig().MaxSeedLookhead
|
||||
}
|
||||
|
||||
// ValidatorChurnLimit returns the number of validators that are allowed to
|
||||
@@ -148,12 +146,8 @@ func DelayedActivationExitEpoch(epoch uint64) uint64 {
|
||||
// """
|
||||
// active_validator_indices = get_active_validator_indices(state, get_current_epoch(state))
|
||||
// return max(MIN_PER_EPOCH_CHURN_LIMIT, len(active_validator_indices) // CHURN_LIMIT_QUOTIENT)
|
||||
func ValidatorChurnLimit(state *pb.BeaconState) (uint64, error) {
|
||||
validatorCount, err := ActiveValidatorCount(state, CurrentEpoch(state))
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not get validator count")
|
||||
}
|
||||
churnLimit := validatorCount / params.BeaconConfig().ChurnLimitQuotient
|
||||
func ValidatorChurnLimit(activeValidatorCount uint64) (uint64, error) {
|
||||
churnLimit := activeValidatorCount / params.BeaconConfig().ChurnLimitQuotient
|
||||
if churnLimit < params.BeaconConfig().MinPerEpochChurnLimit {
|
||||
churnLimit = params.BeaconConfig().MinPerEpochChurnLimit
|
||||
}
|
||||
@@ -168,59 +162,57 @@ func ValidatorChurnLimit(state *pb.BeaconState) (uint64, error) {
|
||||
// Return the beacon proposer index at the current slot.
|
||||
// """
|
||||
// epoch = get_current_epoch(state)
|
||||
// committees_per_slot = get_committee_count(state, epoch) // SLOTS_PER_EPOCH
|
||||
// offset = committees_per_slot * (state.slot % SLOTS_PER_EPOCH)
|
||||
// shard = Shard((get_start_shard(state, epoch) + offset) % SHARD_COUNT)
|
||||
// first_committee = get_crosslink_committee(state, epoch, shard)
|
||||
// seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + int_to_bytes(state.slot, length=8))
|
||||
// indices = get_active_validator_indices(state, epoch)
|
||||
// return compute_proposer_index(state, indices, seed)
|
||||
func BeaconProposerIndex(state *pb.BeaconState) (uint64, error) {
|
||||
e := CurrentEpoch(state)
|
||||
|
||||
seed, err := Seed(state, e, params.BeaconConfig().DomainBeaconProposer)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not generate seed")
|
||||
}
|
||||
|
||||
seedWithSlot := append(seed[:], bytesutil.Bytes8(state.Slot)...)
|
||||
seedWithSlotHash := hashutil.Hash(seedWithSlot)
|
||||
|
||||
indices, err := ActiveValidatorIndices(state, e)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not get active indices")
|
||||
}
|
||||
|
||||
return ComputeProposerIndex(state, indices, seedWithSlotHash)
|
||||
}
|
||||
|
||||
// ComputeProposerIndex returns the index sampled by effective balance, which is used to calculate proposer.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex], seed: Hash) -> ValidatorIndex:
|
||||
// """
|
||||
// Return from ``indices`` a random index sampled by effective balance.
|
||||
// """
|
||||
// assert len(indices) > 0
|
||||
// MAX_RANDOM_BYTE = 2**8 - 1
|
||||
// seed = get_seed(state, epoch)
|
||||
// i = 0
|
||||
// while True:
|
||||
// candidate_index = first_committee[(epoch + i) % len(first_committee)]
|
||||
// candidate_index = indices[compute_shuffled_index(ValidatorIndex(i % len(indices)), len(indices), seed)]
|
||||
// random_byte = hash(seed + int_to_bytes(i // 32, length=8))[i % 32]
|
||||
// effective_balance = state.validators[candidate_index].effective_balance
|
||||
// if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte:
|
||||
// return ValidatorIndex(candidate_index)
|
||||
// i += 1
|
||||
func BeaconProposerIndex(state *pb.BeaconState) (uint64, error) {
|
||||
// Calculate the offset for slot and shard
|
||||
e := CurrentEpoch(state)
|
||||
committeeCount, err := CommitteeCount(state, e)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
func ComputeProposerIndex(state *pb.BeaconState, indices []uint64, seed [32]byte) (uint64, error) {
|
||||
length := uint64(len(indices))
|
||||
if length == 0 {
|
||||
return 0, errors.New("empty indices list")
|
||||
}
|
||||
committesPerSlot := committeeCount / params.BeaconConfig().SlotsPerEpoch
|
||||
offSet := committesPerSlot * (state.Slot % params.BeaconConfig().SlotsPerEpoch)
|
||||
|
||||
// Calculate which shards get assigned given the epoch start shard
|
||||
// and the offset
|
||||
startShard, err := StartShard(state, e)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not get start shard")
|
||||
}
|
||||
shard := (startShard + offSet) % params.BeaconConfig().ShardCount
|
||||
|
||||
// Use the first committee of the given slot and shard
|
||||
// to select proposer
|
||||
firstCommittee, err := CrosslinkCommittee(state, e, shard)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not get first committee")
|
||||
}
|
||||
if len(firstCommittee) == 0 {
|
||||
return 0, fmt.Errorf("empty first committee at slot %d", state.Slot)
|
||||
}
|
||||
|
||||
// Use the generated seed to select proposer from the first committee
|
||||
maxRandomByte := uint64(1<<8 - 1)
|
||||
seed, err := Seed(state, e)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not generate seed")
|
||||
}
|
||||
|
||||
// Looping through the committee to select proposer that has enough
|
||||
// effective balance.
|
||||
for i := uint64(0); ; i++ {
|
||||
candidateIndex := firstCommittee[(e+i)%uint64(len(firstCommittee))]
|
||||
candidateIndex, err := ComputeShuffledIndex(i%length, length, seed, true)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
b := append(seed[:], bytesutil.Bytes8(i/32)...)
|
||||
randomByte := hashutil.Hash(b)[i%32]
|
||||
effectiveBal := state.Validators[candidateIndex].EffectiveBalance
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
@@ -31,10 +30,6 @@ func TestIsActiveValidator_OK(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsSlashableValidator_Active(t *testing.T) {
|
||||
if params.BeaconConfig().SlotsPerEpoch != 64 {
|
||||
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
|
||||
}
|
||||
|
||||
activeValidator := ðpb.Validator{
|
||||
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
}
|
||||
@@ -46,10 +41,6 @@ func TestIsSlashableValidator_Active(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsSlashableValidator_BeforeWithdrawable(t *testing.T) {
|
||||
if params.BeaconConfig().SlotsPerEpoch != 64 {
|
||||
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
|
||||
}
|
||||
|
||||
beforeWithdrawableValidator := ðpb.Validator{
|
||||
WithdrawableEpoch: 5,
|
||||
}
|
||||
@@ -61,10 +52,6 @@ func TestIsSlashableValidator_BeforeWithdrawable(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsSlashableValidator_Inactive(t *testing.T) {
|
||||
if params.BeaconConfig().SlotsPerEpoch != 64 {
|
||||
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
|
||||
}
|
||||
|
||||
inactiveValidator := ðpb.Validator{
|
||||
ActivationEpoch: 5,
|
||||
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
@@ -77,10 +64,6 @@ func TestIsSlashableValidator_Inactive(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsSlashableValidator_AfterWithdrawable(t *testing.T) {
|
||||
if params.BeaconConfig().SlotsPerEpoch != 64 {
|
||||
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
|
||||
}
|
||||
|
||||
afterWithdrawableValidator := ðpb.Validator{
|
||||
WithdrawableEpoch: 3,
|
||||
}
|
||||
@@ -92,9 +75,6 @@ func TestIsSlashableValidator_AfterWithdrawable(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsSlashableValidator_SlashedWithdrawalble(t *testing.T) {
|
||||
if params.BeaconConfig().SlotsPerEpoch != 64 {
|
||||
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
|
||||
}
|
||||
slashedValidator := ðpb.Validator{
|
||||
Slashed: true,
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
@@ -108,10 +88,6 @@ func TestIsSlashableValidator_SlashedWithdrawalble(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsSlashableValidator_Slashed(t *testing.T) {
|
||||
if params.BeaconConfig().SlotsPerEpoch != 64 {
|
||||
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
|
||||
}
|
||||
|
||||
slashedValidator2 := ðpb.Validator{
|
||||
Slashed: true,
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
@@ -125,10 +101,6 @@ func TestIsSlashableValidator_Slashed(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsSlashableValidator_InactiveSlashed(t *testing.T) {
|
||||
if params.BeaconConfig().SlotsPerEpoch != 64 {
|
||||
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
|
||||
}
|
||||
|
||||
slashedValidator2 := ðpb.Validator{
|
||||
Slashed: true,
|
||||
ActivationEpoch: 4,
|
||||
@@ -144,11 +116,6 @@ func TestIsSlashableValidator_InactiveSlashed(t *testing.T) {
|
||||
|
||||
func TestBeaconProposerIndex_OK(t *testing.T) {
|
||||
ClearAllCaches()
|
||||
|
||||
if params.BeaconConfig().SlotsPerEpoch != 64 {
|
||||
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
|
||||
}
|
||||
|
||||
c := params.BeaconConfig()
|
||||
c.MinGenesisActiveValidatorCount = 16384
|
||||
params.OverrideBeaconConfig(c)
|
||||
@@ -160,10 +127,9 @@ func TestBeaconProposerIndex_OK(t *testing.T) {
|
||||
}
|
||||
|
||||
state := &pb.BeaconState{
|
||||
Validators: validators,
|
||||
Slot: 0,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
Validators: validators,
|
||||
Slot: 0,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
@@ -172,23 +138,23 @@ func TestBeaconProposerIndex_OK(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
slot: 1,
|
||||
index: 254,
|
||||
index: 505,
|
||||
},
|
||||
{
|
||||
slot: 5,
|
||||
index: 391,
|
||||
index: 798,
|
||||
},
|
||||
{
|
||||
slot: 19,
|
||||
index: 204,
|
||||
index: 1956,
|
||||
},
|
||||
{
|
||||
slot: 30,
|
||||
index: 1051,
|
||||
index: 991,
|
||||
},
|
||||
{
|
||||
slot: 43,
|
||||
index: 1047,
|
||||
index: 1751,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -209,24 +175,10 @@ func TestBeaconProposerIndex_OK(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBeaconProposerIndex_EmptyCommittee(t *testing.T) {
|
||||
ClearAllCaches()
|
||||
beaconState := &pb.BeaconState{
|
||||
Slot: 0,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
}
|
||||
_, err := BeaconProposerIndex(beaconState)
|
||||
expected := fmt.Sprintf("empty first committee at slot %d", 0)
|
||||
if err.Error() != expected {
|
||||
t.Errorf("Unexpected error. got=%v want=%s", err, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDelayedActivationExitEpoch_OK(t *testing.T) {
|
||||
epoch := uint64(9999)
|
||||
got := DelayedActivationExitEpoch(epoch)
|
||||
wanted := epoch + 1 + params.BeaconConfig().ActivationExitDelay
|
||||
wanted := epoch + 1 + params.BeaconConfig().MaxSeedLookhead
|
||||
if wanted != got {
|
||||
t.Errorf("Wanted: %d, received: %d", wanted, got)
|
||||
}
|
||||
@@ -252,12 +204,15 @@ func TestChurnLimit_OK(t *testing.T) {
|
||||
}
|
||||
|
||||
beaconState := &pb.BeaconState{
|
||||
Slot: 1,
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
Slot: 1,
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
}
|
||||
resultChurn, err := ValidatorChurnLimit(beaconState)
|
||||
validatorCount, err := ActiveValidatorCount(beaconState, CurrentEpoch(beaconState))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resultChurn, err := ValidatorChurnLimit(validatorCount)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"skip_slot_cache.go",
|
||||
"state.go",
|
||||
"transition.go",
|
||||
],
|
||||
@@ -24,13 +25,15 @@ go_library(
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/mathutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/traceutil:go_default_library",
|
||||
"//shared/trieutil:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto: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",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@io_opencensus_go//trace:go_default_library",
|
||||
],
|
||||
@@ -40,6 +43,7 @@ go_test(
|
||||
name = "go_default_test",
|
||||
size = "small",
|
||||
srcs = [
|
||||
"skip_slot_cache_test.go",
|
||||
"state_test.go",
|
||||
"transition_test.go",
|
||||
],
|
||||
@@ -50,32 +54,14 @@ go_test(
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/bls:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"//shared/trieutil:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
# Requires ssz=minimal
|
||||
# gazelle:exclude minimal_config_consensus_test.go
|
||||
go_test(
|
||||
name = "go_minimal_test",
|
||||
size = "small",
|
||||
srcs = ["minimal_config_consensus_test.go"],
|
||||
data = glob(["testdata/minimal/**/*.ssz"]),
|
||||
embed = [":go_default_library"],
|
||||
tags = [
|
||||
"manual",
|
||||
"minimal",
|
||||
],
|
||||
deps = [
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@in_gopkg_d4l3k_messagediff_v1//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -8,7 +8,10 @@ go_library(
|
||||
"write_state_to_disk.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/state/interop",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//tools:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"gopkg.in/d4l3k/messagediff.v1"
|
||||
)
|
||||
|
||||
func TestConsensusBugs(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
blockPath string
|
||||
preStatePath string
|
||||
postStatePath string
|
||||
}{
|
||||
{
|
||||
// This scenario produced a consensus issue between ZCLI, and Artemis.
|
||||
// The test expects that running a state transition with 0_block 0_prestate would
|
||||
// output 0_poststate.
|
||||
//
|
||||
// Assert ExecuteStateTransition(ctx, 0_block, 0_prestate) == 0_poststate
|
||||
//
|
||||
// https://github.com/djrtwo/interop-test-cases/tree/master/tests/artemis_16_crosslinks_and_balances
|
||||
name: "ZcliArtemisCrosslinks",
|
||||
blockPath: "testdata/minimal/artemis_crosslink/block.ssz",
|
||||
preStatePath: "testdata/minimal/artemis_crosslink/pre.ssz",
|
||||
postStatePath: "testdata/minimal/artemis_crosslink/post.ssz",
|
||||
},
|
||||
{
|
||||
// This scenario produced a consensus issue when running Prysm with Trinity.
|
||||
// The test expects that running a state transition with 0_block 0_prestate would
|
||||
// output 0_poststate.
|
||||
//
|
||||
// Assert ExecuteStateTransition(ctx, 0_block, 0_prestate) == 0_poststate
|
||||
//
|
||||
// https://github.com/djrtwo/interop-test-cases/tree/master/tests/prysm_16_duplicate_attestation_rewards
|
||||
name: "TrinityPrysmDuplicateRewards",
|
||||
blockPath: "testdata/minimal/duplicate_rewards/block.ssz",
|
||||
preStatePath: "testdata/minimal/duplicate_rewards/pre.ssz",
|
||||
postStatePath: "testdata/minimal/duplicate_rewards/post.ssz",
|
||||
},
|
||||
{
|
||||
// This scenario produced a consensus issue between Trinity, ZCLI, and Lighthouse.
|
||||
// The test expects that running a state transition with 0_block 0_prestate would
|
||||
// output 0_poststate.
|
||||
//
|
||||
// Assert ExecuteStateTransition(ctx, 0_block, 0_prestate) == 0_poststate
|
||||
//
|
||||
// https://github.com/djrtwo/interop-test-cases/tree/master/tests/night_one_16_crosslinks
|
||||
name: "ZcliTrinityLighthouseCrosslinks",
|
||||
blockPath: "testdata/minimal/crosslink_mismatch/block.ssz",
|
||||
preStatePath: "testdata/minimal/crosslink_mismatch/pre.ssz",
|
||||
postStatePath: "testdata/minimal/crosslink_mismatch/post.ssz",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(tt *testing.T) {
|
||||
block := ðpb.BeaconBlock{}
|
||||
pre := &pb.BeaconState{}
|
||||
post := &pb.BeaconState{}
|
||||
|
||||
params.UseMinimalConfig()
|
||||
|
||||
loadSszOrDie(t, test.blockPath, block)
|
||||
loadSszOrDie(t, test.preStatePath, pre)
|
||||
loadSszOrDie(t, test.postStatePath, post)
|
||||
|
||||
result, err := ExecuteStateTransition(context.Background(), pre, block)
|
||||
if err != nil {
|
||||
t.Logf("Could not process state transition %v", err)
|
||||
}
|
||||
if !ssz.DeepEqual(result, post) {
|
||||
diff, _ := messagediff.PrettyDiff(result, post)
|
||||
t.Log(diff)
|
||||
t.Fatal("Resulting state is not equal to expected post state")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func loadSszOrDie(t *testing.T, filepath string, dst interface{}) {
|
||||
b, err := ioutil.ReadFile(filepath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := ssz.Unmarshal(b, dst); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
24
beacon-chain/core/state/skip_slot_cache.go
Normal file
24
beacon-chain/core/state/skip_slot_cache.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
// skipSlotCache exists for the unlikely scenario that is a large gap between the head state and
|
||||
// the current slot. If the beacon chain were ever to be stalled for several epochs, it may be
|
||||
// difficult or impossible to compute the appropriate beacon state for assignments within a
|
||||
// reasonable amount of time.
|
||||
var skipSlotCache, _ = lru.New(8)
|
||||
|
||||
var (
|
||||
skipSlotCacheHit = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "skip_slot_cache_hit",
|
||||
Help: "The total number of cache hits on the skip slot cache.",
|
||||
})
|
||||
skipSlotCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "skip_slot_cache_miss",
|
||||
Help: "The total number of cache misses on the skip slot cache.",
|
||||
})
|
||||
)
|
||||
54
beacon-chain/core/state/skip_slot_cache_test.go
Normal file
54
beacon-chain/core/state/skip_slot_cache_test.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package state_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
)
|
||||
|
||||
func TestSkipSlotCache_OK(t *testing.T) {
|
||||
deps, _, privs := testutil.SetupInitialDeposits(t, params.MinimalSpecConfig().MinGenesisActiveValidatorCount)
|
||||
bState, err := state.GenesisBeaconState(deps, uint64(time.Now().Unix()), testutil.GenerateEth1Data(t, deps))
|
||||
if err != nil {
|
||||
t.Fatalf("Could not generate genesis state: %v", err)
|
||||
}
|
||||
|
||||
originalState := proto.Clone(bState).(*pb.BeaconState)
|
||||
|
||||
blkCfg := testutil.DefaultBlockGenConfig()
|
||||
blkCfg.MaxAttestations = 1
|
||||
blkCfg.MaxDeposits = 0
|
||||
blkCfg.MaxVoluntaryExits = 0
|
||||
blkCfg.MaxProposerSlashings = 0
|
||||
blkCfg.MaxAttesterSlashings = 0
|
||||
|
||||
cfg := featureconfig.Get()
|
||||
cfg.EnableSkipSlotsCache = true
|
||||
featureconfig.Init(cfg)
|
||||
|
||||
// First transition will be with an empty cache, so the cache becomes populated
|
||||
// with the state
|
||||
blk := testutil.GenerateFullBlock(t, bState, privs, blkCfg, originalState.Slot+10)
|
||||
originalState, err = state.ExecuteStateTransition(context.Background(), originalState, blk)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not run state transition: %v", err)
|
||||
}
|
||||
|
||||
bState, err = state.ExecuteStateTransition(context.Background(), bState, blk)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not process state transition: %v", err)
|
||||
}
|
||||
|
||||
if !ssz.DeepEqual(originalState, bState) {
|
||||
t.Fatal("Skipped slots cache leads to different states")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/mathutil"
|
||||
@@ -26,6 +25,7 @@ import (
|
||||
// genesis_time=eth1_timestamp - eth1_timestamp % SECONDS_PER_DAY + 2 * SECONDS_PER_DAY,
|
||||
// eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=len(deposits)),
|
||||
// latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())),
|
||||
// randao_mixes=[eth1_block_hash] * EPOCHS_PER_HISTORICAL_VECTOR, # Seed RANDAO with Eth1 entropy
|
||||
// )
|
||||
//
|
||||
// # Process deposits
|
||||
@@ -52,9 +52,15 @@ import (
|
||||
// state.compact_committees_roots[index] = committee_root
|
||||
// return state
|
||||
func GenesisBeaconState(deposits []*ethpb.Deposit, genesisTime uint64, eth1Data *ethpb.Eth1Data) (*pb.BeaconState, error) {
|
||||
if eth1Data == nil {
|
||||
return nil, errors.New("no eth1data provided for genesis state")
|
||||
}
|
||||
|
||||
randaoMixes := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
|
||||
for i := 0; i < len(randaoMixes); i++ {
|
||||
randaoMixes[i] = make([]byte, 32)
|
||||
h := make([]byte, 32)
|
||||
copy(h, eth1Data.BlockHash)
|
||||
randaoMixes[i] = h
|
||||
}
|
||||
|
||||
zeroHash := params.BeaconConfig().ZeroHash[:]
|
||||
@@ -64,16 +70,6 @@ func GenesisBeaconState(deposits []*ethpb.Deposit, genesisTime uint64, eth1Data
|
||||
activeIndexRoots[i] = zeroHash
|
||||
}
|
||||
|
||||
compactRoots := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector)
|
||||
|
||||
crosslinks := make([]*ethpb.Crosslink, params.BeaconConfig().ShardCount)
|
||||
for i := 0; i < len(crosslinks); i++ {
|
||||
crosslinks[i] = ðpb.Crosslink{
|
||||
ParentRoot: make([]byte, 32),
|
||||
DataRoot: make([]byte, 32),
|
||||
}
|
||||
}
|
||||
|
||||
blockRoots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
|
||||
for i := 0; i < len(blockRoots); i++ {
|
||||
blockRoots[i] = zeroHash
|
||||
@@ -86,10 +82,6 @@ func GenesisBeaconState(deposits []*ethpb.Deposit, genesisTime uint64, eth1Data
|
||||
|
||||
slashings := make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector)
|
||||
|
||||
if eth1Data == nil {
|
||||
return nil, errors.New("no eth1data provided for genesis state")
|
||||
}
|
||||
|
||||
eth1Data.DepositCount = uint64(len(deposits))
|
||||
|
||||
state := &pb.BeaconState{
|
||||
@@ -125,11 +117,6 @@ func GenesisBeaconState(deposits []*ethpb.Deposit, genesisTime uint64, eth1Data
|
||||
Root: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
|
||||
// Recent state.
|
||||
CurrentCrosslinks: crosslinks,
|
||||
PreviousCrosslinks: crosslinks,
|
||||
ActiveIndexRoots: activeIndexRoots,
|
||||
CompactCommitteesRoots: compactRoots,
|
||||
HistoricalRoots: [][]byte{},
|
||||
BlockRoots: blockRoots,
|
||||
StateRoots: stateRoots,
|
||||
@@ -197,23 +184,6 @@ func GenesisBeaconState(deposits []*ethpb.Deposit, genesisTime uint64, eth1Data
|
||||
}
|
||||
}
|
||||
|
||||
// Populate latest_active_index_roots
|
||||
activeIndices, err := helpers.ActiveValidatorIndices(state, 0)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get active validator indices")
|
||||
}
|
||||
genesisActiveIndexRoot, err := ssz.HashTreeRootWithCapacity(activeIndices, params.BeaconConfig().ValidatorRegistryLimit)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not hash tree root active indices")
|
||||
}
|
||||
genesisCompactCommRoot, err := helpers.CompactCommitteesRoot(state, 0)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get compact committee root")
|
||||
}
|
||||
for i := uint64(0); i < params.BeaconConfig().EpochsPerHistoricalVector; i++ {
|
||||
state.ActiveIndexRoots[i] = genesisActiveIndexRoot[:]
|
||||
state.CompactCommitteesRoots[i] = genesisCompactCommRoot[:]
|
||||
}
|
||||
return state, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
@@ -16,10 +15,6 @@ import (
|
||||
)
|
||||
|
||||
func TestGenesisBeaconState_OK(t *testing.T) {
|
||||
if params.BeaconConfig().SlotsPerEpoch != 64 {
|
||||
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
|
||||
}
|
||||
|
||||
genesisEpochNumber := uint64(0)
|
||||
|
||||
if !bytes.Equal(params.BeaconConfig().GenesisForkVersion, []byte{0, 0, 0, 0}) {
|
||||
@@ -36,11 +31,6 @@ func TestGenesisBeaconState_OK(t *testing.T) {
|
||||
}
|
||||
latestRandaoMixesLength := int(params.BeaconConfig().EpochsPerHistoricalVector)
|
||||
|
||||
if params.BeaconConfig().ShardCount != 1024 {
|
||||
t.Error("ShardCount should be 1024 for these tests to pass")
|
||||
}
|
||||
shardCount := int(params.BeaconConfig().ShardCount)
|
||||
|
||||
if params.BeaconConfig().HistoricalRootsLimit != 16777216 {
|
||||
t.Error("HistoricalRootsLimit should be 16777216 for these tests to pass")
|
||||
}
|
||||
@@ -54,11 +44,7 @@ func TestGenesisBeaconState_OK(t *testing.T) {
|
||||
genesisTime := uint64(99999)
|
||||
deposits, _, _ := testutil.SetupInitialDeposits(t, uint64(depositsForChainStart))
|
||||
eth1Data := testutil.GenerateEth1Data(t, deposits)
|
||||
newState, err := state.GenesisBeaconState(
|
||||
deposits,
|
||||
genesisTime,
|
||||
eth1Data,
|
||||
)
|
||||
newState, err := state.GenesisBeaconState(deposits, genesisTime, eth1Data)
|
||||
if err != nil {
|
||||
t.Fatalf("could not execute GenesisBeaconState: %v", err)
|
||||
}
|
||||
@@ -93,7 +79,7 @@ func TestGenesisBeaconState_OK(t *testing.T) {
|
||||
if len(newState.RandaoMixes) != latestRandaoMixesLength {
|
||||
t.Error("Length of RandaoMixes was not correctly initialized")
|
||||
}
|
||||
if !bytes.Equal(newState.RandaoMixes[0], make([]byte, 32)) {
|
||||
if !bytes.Equal(newState.RandaoMixes[0], eth1Data.BlockHash) {
|
||||
t.Error("RandaoMixes was not correctly initialized")
|
||||
}
|
||||
|
||||
@@ -112,12 +98,6 @@ func TestGenesisBeaconState_OK(t *testing.T) {
|
||||
}
|
||||
|
||||
// Recent state checks.
|
||||
if len(newState.CurrentCrosslinks) != shardCount {
|
||||
t.Error("Length of CurrentCrosslinks was not correctly initialized")
|
||||
}
|
||||
if len(newState.PreviousCrosslinks) != shardCount {
|
||||
t.Error("Length of PreviousCrosslinks was not correctly initialized")
|
||||
}
|
||||
if !reflect.DeepEqual(newState.Slashings, make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector)) {
|
||||
t.Error("Slashings was not correctly initialized")
|
||||
}
|
||||
@@ -128,32 +108,11 @@ func TestGenesisBeaconState_OK(t *testing.T) {
|
||||
t.Error("PreviousEpochAttestations was not correctly initialized")
|
||||
}
|
||||
|
||||
activeValidators, _ := helpers.ActiveValidatorIndices(newState, 0)
|
||||
genesisActiveIndexRoot, err := ssz.HashTreeRootWithCapacity(activeValidators, params.BeaconConfig().ValidatorRegistryLimit)
|
||||
if err != nil {
|
||||
t.Errorf("could not hash tree root: %v", err)
|
||||
}
|
||||
if !bytes.Equal(newState.ActiveIndexRoots[0], genesisActiveIndexRoot[:]) {
|
||||
t.Errorf(
|
||||
"Expected index roots to be the tree hash root of active validator indices, received %#x",
|
||||
newState.ActiveIndexRoots[0],
|
||||
)
|
||||
}
|
||||
if !bytes.Equal(newState.ActiveIndexRoots[0], genesisActiveIndexRoot[:]) {
|
||||
t.Errorf(
|
||||
"Expected index roots to be the tree hash root of active validator indices, received %#x",
|
||||
newState.ActiveIndexRoots[0],
|
||||
)
|
||||
}
|
||||
|
||||
zeroHash := params.BeaconConfig().ZeroHash[:]
|
||||
// History root checks.
|
||||
if !bytes.Equal(newState.StateRoots[0], zeroHash) {
|
||||
t.Error("StateRoots was not correctly initialized")
|
||||
}
|
||||
if bytes.Equal(newState.ActiveIndexRoots[0], zeroHash) || bytes.Equal(newState.ActiveIndexRoots[0], []byte{}) {
|
||||
t.Error("ActiveIndexRoots was not correctly initialized")
|
||||
}
|
||||
if !bytes.Equal(newState.BlockRoots[0], zeroHash) {
|
||||
t.Error("BlockRoots was not correctly initialized")
|
||||
}
|
||||
@@ -170,11 +129,11 @@ func TestGenesisBeaconState_OK(t *testing.T) {
|
||||
func TestGenesisState_HashEquality(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
state1, err := state.GenesisBeaconState(deposits, 0, ðpb.Eth1Data{})
|
||||
state1, err := state.GenesisBeaconState(deposits, 0, ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
state2, err := state.GenesisBeaconState(deposits, 0, ðpb.Eth1Data{})
|
||||
state2, err := state.GenesisBeaconState(deposits, 0, ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -19,7 +19,6 @@ import (
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/mathutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/traceutil"
|
||||
@@ -62,7 +61,7 @@ func ExecuteStateTransition(
|
||||
if block != nil {
|
||||
state, err = ProcessBlock(ctx, state, block)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process block")
|
||||
return nil, errors.Wrapf(err, "could not process block in slot %d", block.Slot)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,9 +244,42 @@ func ProcessSlots(ctx context.Context, state *pb.BeaconState, slot uint64) (*pb.
|
||||
traceutil.AnnotateError(span, err)
|
||||
return nil, err
|
||||
}
|
||||
highestSlot := state.Slot
|
||||
var root [32]byte
|
||||
var writeToCache bool
|
||||
var err error
|
||||
|
||||
if featureconfig.Get().EnableSkipSlotsCache {
|
||||
// Restart from cached value, if one exists.
|
||||
root, err = ssz.HashTreeRoot(state)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not HashTreeRoot(state)")
|
||||
}
|
||||
cached, ok := skipSlotCache.Get(root)
|
||||
// if cache key does not exist, we write it to the cache.
|
||||
writeToCache = !ok
|
||||
if ok {
|
||||
// do not write to cache if state with higher slot exists.
|
||||
writeToCache = cached.(*pb.BeaconState).Slot <= slot
|
||||
if cached.(*pb.BeaconState).Slot <= slot {
|
||||
state = proto.Clone(cached.(*pb.BeaconState)).(*pb.BeaconState)
|
||||
highestSlot = state.Slot
|
||||
skipSlotCacheHit.Inc()
|
||||
} else {
|
||||
skipSlotCacheMiss.Inc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for state.Slot < slot {
|
||||
if ctx.Err() != nil {
|
||||
traceutil.AnnotateError(span, ctx.Err())
|
||||
if featureconfig.Get().EnableSkipSlotsCache {
|
||||
// Cache last best value.
|
||||
if highestSlot < state.Slot && writeToCache {
|
||||
skipSlotCache.Add(root, proto.Clone(state).(*pb.BeaconState))
|
||||
}
|
||||
}
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
state, err := ProcessSlot(ctx, state)
|
||||
@@ -272,6 +304,14 @@ func ProcessSlots(ctx context.Context, state *pb.BeaconState, slot uint64) (*pb.
|
||||
}
|
||||
state.Slot++
|
||||
}
|
||||
|
||||
if featureconfig.Get().EnableSkipSlotsCache {
|
||||
// Clone result state so that caches are not mutated.
|
||||
if highestSlot < state.Slot && writeToCache {
|
||||
skipSlotCache.Add(root, proto.Clone(state).(*pb.BeaconState))
|
||||
}
|
||||
}
|
||||
|
||||
return state, nil
|
||||
}
|
||||
|
||||
@@ -404,19 +444,6 @@ func ProcessOperations(
|
||||
return nil, errors.Wrap(err, "could not verify operation lengths")
|
||||
}
|
||||
|
||||
// Verify that there are no duplicate transfers
|
||||
transferSet := make(map[[32]byte]bool)
|
||||
for _, transfer := range body.Transfers {
|
||||
h, err := hashutil.HashProto(transfer)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not hash transfer")
|
||||
}
|
||||
if transferSet[h] {
|
||||
return nil, fmt.Errorf("duplicate transfer: %v", transfer)
|
||||
}
|
||||
transferSet[h] = true
|
||||
}
|
||||
|
||||
state, err := b.ProcessProposerSlashings(ctx, state, body)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process block proposer slashings")
|
||||
@@ -437,10 +464,6 @@ func ProcessOperations(
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process validator exits")
|
||||
}
|
||||
state, err = b.ProcessTransfers(state, body)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process block transfers")
|
||||
}
|
||||
|
||||
return state, nil
|
||||
}
|
||||
@@ -481,19 +504,6 @@ func processOperationsNoVerify(
|
||||
return nil, errors.Wrap(err, "could not verify operation lengths")
|
||||
}
|
||||
|
||||
// Verify that there are no duplicate transfers
|
||||
transferSet := make(map[[32]byte]bool)
|
||||
for _, transfer := range body.Transfers {
|
||||
h, err := hashutil.HashProto(transfer)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not hash transfer")
|
||||
}
|
||||
if transferSet[h] {
|
||||
return nil, fmt.Errorf("duplicate transfer: %v", transfer)
|
||||
}
|
||||
transferSet[h] = true
|
||||
}
|
||||
|
||||
state, err := b.ProcessProposerSlashings(ctx, state, body)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process block proposer slashings")
|
||||
@@ -514,10 +524,6 @@ func processOperationsNoVerify(
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process validator exits")
|
||||
}
|
||||
state, err = b.ProcessTransfers(state, body)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process block transfers")
|
||||
}
|
||||
|
||||
return state, nil
|
||||
}
|
||||
@@ -555,14 +561,6 @@ func verifyOperationLengths(state *pb.BeaconState, body *ethpb.BeaconBlockBody)
|
||||
)
|
||||
}
|
||||
|
||||
if uint64(len(body.Transfers)) > params.BeaconConfig().MaxTransfers {
|
||||
return fmt.Errorf(
|
||||
"number of transfers (%d) in block body exceeds allowed threshold of %d",
|
||||
len(body.Transfers),
|
||||
params.BeaconConfig().MaxTransfers,
|
||||
)
|
||||
}
|
||||
|
||||
if state.Eth1DepositIndex > state.Eth1Data.DepositCount {
|
||||
return fmt.Errorf("expected state.deposit_index %d <= eth1data.deposit_count %d", state.Eth1DepositIndex, state.Eth1Data.DepositCount)
|
||||
}
|
||||
@@ -629,11 +627,6 @@ func ProcessEpoch(ctx context.Context, state *pb.BeaconState) (*pb.BeaconState,
|
||||
return nil, errors.Wrap(err, "could not process justification")
|
||||
}
|
||||
|
||||
state, err = e.ProcessCrosslinks(state)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process crosslink")
|
||||
}
|
||||
|
||||
state, err = e.ProcessRewardsAndPenalties(state)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process rewards and penalties")
|
||||
@@ -674,11 +667,6 @@ func ProcessEpochPrecompute(ctx context.Context, state *pb.BeaconState) (*pb.Bea
|
||||
return nil, errors.Wrap(err, "could not process justification")
|
||||
}
|
||||
|
||||
state, err = e.ProcessCrosslinks(state)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process crosslink")
|
||||
}
|
||||
|
||||
state, err = precompute.ProcessRewardsAndPenaltiesPrecompute(state, bp, vp)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process rewards and penalties")
|
||||
|
||||
@@ -40,7 +40,7 @@ func TestExecuteStateTransition_IncorrectSlot(t *testing.T) {
|
||||
func TestExecuteStateTransition_FullProcess(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -54,7 +54,6 @@ func TestExecuteStateTransition_FullProcess(t *testing.T) {
|
||||
beaconState.Eth1DataVotes = []*ethpb.Eth1Data{eth1Data}
|
||||
|
||||
oldMix := beaconState.RandaoMixes[1]
|
||||
oldStartShard := beaconState.StartShard
|
||||
parentRoot, err := ssz.SigningRoot(beaconState.LatestBlockHeader)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -93,23 +92,19 @@ func TestExecuteStateTransition_FullProcess(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if beaconState.Slot != 64 {
|
||||
if beaconState.Slot != params.BeaconConfig().SlotsPerEpoch {
|
||||
t.Errorf("Unexpected Slot number, expected: 64, received: %d", beaconState.Slot)
|
||||
}
|
||||
|
||||
if bytes.Equal(beaconState.RandaoMixes[1], oldMix) {
|
||||
t.Errorf("Did not expect new and old randao mix to equal, %#x == %#x", beaconState.RandaoMixes[0], oldMix)
|
||||
}
|
||||
|
||||
if beaconState.StartShard == oldStartShard {
|
||||
t.Errorf("Did not expect new and old start shard to equal, %#x == %#x", beaconState.StartShard, oldStartShard)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessBlock_IncorrectProposerSlashing(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 34)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -164,7 +159,7 @@ func TestProcessBlock_IncorrectProposerSlashing(t *testing.T) {
|
||||
|
||||
func TestProcessBlock_IncorrectProcessBlockAttestations(t *testing.T) {
|
||||
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -208,9 +203,6 @@ func TestProcessBlock_IncorrectProcessBlockAttestations(t *testing.T) {
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 0},
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 4,
|
||||
},
|
||||
},
|
||||
CustodyBit_0Indices: []uint64{0, 1},
|
||||
}
|
||||
@@ -222,7 +214,7 @@ func TestProcessBlock_IncorrectProcessBlockAttestations(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
domain = helpers.Domain(beaconState.Fork, currentEpoch, params.BeaconConfig().DomainAttestation)
|
||||
domain = helpers.Domain(beaconState.Fork, currentEpoch, params.BeaconConfig().DomainBeaconAttester)
|
||||
sig0 := privKeys[0].Sign(hashTreeRoot[:], domain)
|
||||
sig1 := privKeys[1].Sign(hashTreeRoot[:], domain)
|
||||
aggregateSig := bls.AggregateSignatures([]*bls.Signature{sig0, sig1})
|
||||
@@ -232,9 +224,6 @@ func TestProcessBlock_IncorrectProcessBlockAttestations(t *testing.T) {
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 1},
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 4,
|
||||
},
|
||||
},
|
||||
CustodyBit_0Indices: []uint64{0, 1},
|
||||
}
|
||||
@@ -260,8 +249,7 @@ func TestProcessBlock_IncorrectProcessBlockAttestations(t *testing.T) {
|
||||
|
||||
att := ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{},
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
},
|
||||
AggregationBits: bitfield.NewBitlist(0),
|
||||
CustodyBits: bitfield.NewBitlist(0),
|
||||
@@ -315,7 +303,7 @@ func TestProcessBlock_IncorrectProcessExits(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
|
||||
deposits, _, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -339,18 +327,14 @@ func TestProcessBlock_IncorrectProcessExits(t *testing.T) {
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 0},
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 4,
|
||||
}},
|
||||
},
|
||||
CustodyBit_0Indices: []uint64{0, 1},
|
||||
},
|
||||
Attestation_2: ðpb.IndexedAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 1},
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 4,
|
||||
}},
|
||||
},
|
||||
CustodyBit_0Indices: []uint64{0, 1},
|
||||
},
|
||||
},
|
||||
@@ -360,19 +344,10 @@ func TestProcessBlock_IncorrectProcessExits(t *testing.T) {
|
||||
blockRoots = append(blockRoots, []byte{byte(i)})
|
||||
}
|
||||
beaconState.BlockRoots = blockRoots
|
||||
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{
|
||||
{
|
||||
DataRoot: []byte{1},
|
||||
},
|
||||
}
|
||||
blockAtt := ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 0},
|
||||
Target: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 0,
|
||||
StartEpoch: 0,
|
||||
},
|
||||
},
|
||||
AggregationBits: bitfield.Bitlist{0xC0, 0xC0, 0xC0, 0xC0, 0x01},
|
||||
CustodyBits: bitfield.Bitlist{0x00, 0x00, 0x00, 0x00, 0x01},
|
||||
@@ -412,29 +387,16 @@ func TestProcessBlock_IncorrectProcessExits(t *testing.T) {
|
||||
},
|
||||
}
|
||||
beaconState.Slot += params.BeaconConfig().MinAttestationInclusionDelay
|
||||
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{
|
||||
{
|
||||
Shard: 0,
|
||||
StartEpoch: 0,
|
||||
},
|
||||
}
|
||||
beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world")
|
||||
beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{}
|
||||
|
||||
encoded, err := ssz.HashTreeRoot(beaconState.CurrentCrosslinks[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block.Body.Attestations[0].Data.Crosslink.ParentRoot = encoded[:]
|
||||
block.Body.Attestations[0].Data.Crosslink.DataRoot = params.BeaconConfig().ZeroHash[:]
|
||||
if _, err := state.ProcessBlock(context.Background(), beaconState, block); err == nil {
|
||||
t.Error("Expected err, received nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessBlock_PassesProcessingConditions(t *testing.T) {
|
||||
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 100)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
deposits, _, privKeys := testutil.SetupInitialDeposits(t, 32)
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -449,19 +411,8 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) {
|
||||
BodyRoot: bodyRoot[:],
|
||||
}
|
||||
beaconState.Slashings = make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector)
|
||||
beaconState.CurrentCrosslinks = []*ethpb.Crosslink{
|
||||
{
|
||||
Shard: 0,
|
||||
StartEpoch: helpers.SlotToEpoch(beaconState.Slot),
|
||||
DataRoot: []byte{1},
|
||||
},
|
||||
}
|
||||
beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world")
|
||||
beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{}
|
||||
encoded, err := ssz.HashTreeRoot(beaconState.CurrentCrosslinks[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
proposerSlashIdx := uint64(3)
|
||||
slotsPerEpoch := params.BeaconConfig().SlotsPerEpoch
|
||||
@@ -506,11 +457,7 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) {
|
||||
att1 := ðpb.IndexedAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 0, Root: []byte{'A'}},
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 4,
|
||||
},
|
||||
},
|
||||
Target: ðpb.Checkpoint{Epoch: 0}},
|
||||
CustodyBit_0Indices: []uint64{0, 1},
|
||||
}
|
||||
dataAndCustodyBit := &pb.AttestationDataAndCustodyBit{
|
||||
@@ -521,7 +468,7 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
domain = helpers.Domain(beaconState.Fork, currentEpoch, params.BeaconConfig().DomainAttestation)
|
||||
domain = helpers.Domain(beaconState.Fork, currentEpoch, params.BeaconConfig().DomainBeaconAttester)
|
||||
sig0 := privKeys[0].Sign(hashTreeRoot[:], domain)
|
||||
sig1 := privKeys[1].Sign(hashTreeRoot[:], domain)
|
||||
aggregateSig := bls.AggregateSignatures([]*bls.Signature{sig0, sig1})
|
||||
@@ -530,11 +477,7 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) {
|
||||
att2 := ðpb.IndexedAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 0, Root: []byte{'B'}},
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 4,
|
||||
},
|
||||
},
|
||||
Target: ðpb.Checkpoint{Epoch: 0}},
|
||||
CustodyBit_0Indices: []uint64{0, 1},
|
||||
}
|
||||
dataAndCustodyBit = &pb.AttestationDataAndCustodyBit{
|
||||
@@ -568,18 +511,12 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) {
|
||||
custodyBits := bitfield.NewBitlist(1)
|
||||
blockAtt := ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Slot: beaconState.Slot - 1,
|
||||
Target: ðpb.Checkpoint{Epoch: helpers.SlotToEpoch(beaconState.Slot)},
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: 0,
|
||||
Root: []byte("hello-world"),
|
||||
},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 0,
|
||||
EndEpoch: 64,
|
||||
DataRoot: params.BeaconConfig().ZeroHash[:],
|
||||
ParentRoot: encoded[:],
|
||||
},
|
||||
},
|
||||
}},
|
||||
AggregationBits: aggBits,
|
||||
CustodyBits: custodyBits,
|
||||
}
|
||||
@@ -674,12 +611,11 @@ func TestProcessEpoch_CantGetTgtAttsPrevEpoch(t *testing.T) {
|
||||
func TestProcessEpoch_CantGetTgtAttsCurrEpoch(t *testing.T) {
|
||||
epoch := uint64(1)
|
||||
|
||||
atts := []*pb.PendingAttestation{{Data: ðpb.AttestationData{Crosslink: ðpb.Crosslink{Shard: 100}}}}
|
||||
atts := []*pb.PendingAttestation{{Data: ðpb.AttestationData{}}}
|
||||
_, err := state.ProcessEpoch(context.Background(), &pb.BeaconState{
|
||||
Slot: epoch * params.BeaconConfig().SlotsPerEpoch,
|
||||
BlockRoots: make([][]byte, 128),
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
CurrentEpochAttestations: atts})
|
||||
if !strings.Contains(err.Error(), "could not get target atts current epoch") {
|
||||
t.Fatal("Did not receive wanted error")
|
||||
@@ -690,22 +626,12 @@ func TestProcessEpoch_CanProcess(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
epoch := uint64(1)
|
||||
|
||||
atts := []*pb.PendingAttestation{{Data: ðpb.AttestationData{Crosslink: ðpb.Crosslink{Shard: 0}, Target: ðpb.Checkpoint{}}}}
|
||||
var crosslinks []*ethpb.Crosslink
|
||||
for i := uint64(0); i < params.BeaconConfig().ShardCount; i++ {
|
||||
crosslinks = append(crosslinks, ðpb.Crosslink{
|
||||
StartEpoch: 0,
|
||||
DataRoot: []byte{'A'},
|
||||
})
|
||||
}
|
||||
atts := []*pb.PendingAttestation{{Data: ðpb.AttestationData{Target: ðpb.Checkpoint{}}}}
|
||||
newState, err := state.ProcessEpoch(context.Background(), &pb.BeaconState{
|
||||
Slot: epoch*params.BeaconConfig().SlotsPerEpoch + 1,
|
||||
BlockRoots: make([][]byte, 128),
|
||||
Slashings: []uint64{0, 1e9, 1e9},
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
CompactCommitteesRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
CurrentCrosslinks: crosslinks,
|
||||
CurrentEpochAttestations: atts,
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{},
|
||||
JustificationBits: bitfield.Bitvector4{0x00},
|
||||
@@ -725,22 +651,13 @@ func TestProcessEpochPrecompute_CanProcess(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
epoch := uint64(1)
|
||||
|
||||
atts := []*pb.PendingAttestation{{Data: ðpb.AttestationData{Crosslink: ðpb.Crosslink{Shard: 0}, Target: ðpb.Checkpoint{}}}}
|
||||
var crosslinks []*ethpb.Crosslink
|
||||
for i := uint64(0); i < params.BeaconConfig().ShardCount; i++ {
|
||||
crosslinks = append(crosslinks, ðpb.Crosslink{
|
||||
StartEpoch: 0,
|
||||
DataRoot: []byte{'A'},
|
||||
})
|
||||
}
|
||||
atts := []*pb.PendingAttestation{{Data: ðpb.AttestationData{Target: ðpb.Checkpoint{}}}}
|
||||
|
||||
newState, err := state.ProcessEpochPrecompute(context.Background(), &pb.BeaconState{
|
||||
Slot: epoch*params.BeaconConfig().SlotsPerEpoch + 1,
|
||||
BlockRoots: make([][]byte, 128),
|
||||
Slashings: []uint64{0, 1e9, 1e9},
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
CompactCommitteesRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
CurrentCrosslinks: crosslinks,
|
||||
CurrentEpochAttestations: atts,
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{},
|
||||
JustificationBits: bitfield.Bitvector4{0x00},
|
||||
@@ -758,9 +675,8 @@ func TestProcessEpochPrecompute_CanProcess(t *testing.T) {
|
||||
|
||||
func TestProcessEpoch_NotPanicOnEmptyActiveValidatorIndices(t *testing.T) {
|
||||
newState := &pb.BeaconState{
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().SlotsPerEpoch),
|
||||
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().SlotsPerEpoch),
|
||||
}
|
||||
|
||||
state.ProcessEpoch(context.Background(), newState)
|
||||
@@ -773,7 +689,7 @@ func BenchmarkProcessEpoch65536Validators(b *testing.B) {
|
||||
epoch := uint64(1)
|
||||
|
||||
validatorCount := params.BeaconConfig().MinGenesisActiveValidatorCount * 4
|
||||
shardCount := validatorCount / params.BeaconConfig().TargetCommitteeSize
|
||||
comitteeCount := validatorCount / params.BeaconConfig().TargetCommitteeSize
|
||||
validators := make([]*ethpb.Validator, validatorCount)
|
||||
balances := make([]uint64, validatorCount)
|
||||
|
||||
@@ -786,44 +702,29 @@ func BenchmarkProcessEpoch65536Validators(b *testing.B) {
|
||||
}
|
||||
|
||||
var atts []*pb.PendingAttestation
|
||||
for i := uint64(0); i < shardCount; i++ {
|
||||
for i := uint64(0); i < comitteeCount; i++ {
|
||||
atts = append(atts, &pb.PendingAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: i,
|
||||
},
|
||||
},
|
||||
Data: ðpb.AttestationData{},
|
||||
AggregationBits: []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
|
||||
InclusionDelay: 1,
|
||||
})
|
||||
}
|
||||
|
||||
var crosslinks []*ethpb.Crosslink
|
||||
for i := uint64(0); i < params.BeaconConfig().ShardCount; i++ {
|
||||
crosslinks = append(crosslinks, ðpb.Crosslink{
|
||||
StartEpoch: 0,
|
||||
DataRoot: []byte{'A'},
|
||||
})
|
||||
}
|
||||
|
||||
s := &pb.BeaconState{
|
||||
Slot: epoch*params.BeaconConfig().SlotsPerEpoch + 1,
|
||||
Validators: validators,
|
||||
Balances: balances,
|
||||
StartShard: 512,
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{},
|
||||
BlockRoots: make([][]byte, 254),
|
||||
Slashings: []uint64{0, 1e9, 0},
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
CurrentCrosslinks: crosslinks,
|
||||
PreviousEpochAttestations: atts,
|
||||
}
|
||||
|
||||
// Precache the shuffled indices
|
||||
for i := uint64(0); i < shardCount; i++ {
|
||||
if _, err := helpers.CrosslinkCommittee(s, 0, i); err != nil {
|
||||
for i := uint64(0); i < comitteeCount; i++ {
|
||||
if _, err := helpers.BeaconCommittee(s, 0, i); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -840,11 +741,9 @@ func BenchmarkProcessEpoch65536Validators(b *testing.B) {
|
||||
func BenchmarkProcessBlk_65536Validators_FullBlock(b *testing.B) {
|
||||
logrus.SetLevel(logrus.PanicLevel)
|
||||
helpers.ClearAllCaches()
|
||||
testConfig := params.BeaconConfig()
|
||||
testConfig.MaxTransfers = 1
|
||||
|
||||
validatorCount := params.BeaconConfig().MinGenesisActiveValidatorCount * 4
|
||||
shardCount := validatorCount / params.BeaconConfig().TargetCommitteeSize
|
||||
committeeCount := validatorCount / params.BeaconConfig().TargetCommitteeSize
|
||||
validators := make([]*ethpb.Validator, validatorCount)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = ðpb.Validator{
|
||||
@@ -864,14 +763,6 @@ func BenchmarkProcessBlk_65536Validators_FullBlock(b *testing.B) {
|
||||
randaoMixes[i] = params.BeaconConfig().ZeroHash[:]
|
||||
}
|
||||
|
||||
var crosslinks []*ethpb.Crosslink
|
||||
for i := uint64(0); i < params.BeaconConfig().ShardCount; i++ {
|
||||
crosslinks = append(crosslinks, ðpb.Crosslink{
|
||||
StartEpoch: 0,
|
||||
DataRoot: []byte{'A'},
|
||||
})
|
||||
}
|
||||
|
||||
s := &pb.BeaconState{
|
||||
Slot: 20,
|
||||
LatestBlockHeader: ðpb.BeaconBlockHeader{},
|
||||
@@ -880,7 +771,6 @@ func BenchmarkProcessBlk_65536Validators_FullBlock(b *testing.B) {
|
||||
Validators: validators,
|
||||
Balances: validatorBalances,
|
||||
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
CurrentJustifiedCheckpoint: ðpb.Checkpoint{
|
||||
Root: []byte("hello-world"),
|
||||
},
|
||||
@@ -888,7 +778,6 @@ func BenchmarkProcessBlk_65536Validators_FullBlock(b *testing.B) {
|
||||
PreviousVersion: []byte{0, 0, 0, 0},
|
||||
CurrentVersion: []byte{0, 0, 0, 0},
|
||||
},
|
||||
CurrentCrosslinks: crosslinks,
|
||||
}
|
||||
|
||||
// Set up proposer slashing object for block
|
||||
@@ -910,19 +799,11 @@ func BenchmarkProcessBlk_65536Validators_FullBlock(b *testing.B) {
|
||||
attesterSlashings := []*ethpb.AttesterSlashing{
|
||||
{
|
||||
Attestation_1: ðpb.IndexedAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 5,
|
||||
},
|
||||
},
|
||||
Data: ðpb.AttestationData{},
|
||||
CustodyBit_0Indices: []uint64{2, 3},
|
||||
},
|
||||
Attestation_2: ðpb.IndexedAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 5,
|
||||
},
|
||||
},
|
||||
Data: ðpb.AttestationData{},
|
||||
CustodyBit_0Indices: []uint64{2, 3},
|
||||
},
|
||||
},
|
||||
@@ -965,40 +846,17 @@ func BenchmarkProcessBlk_65536Validators_FullBlock(b *testing.B) {
|
||||
domain := helpers.Domain(s.Fork, 0, params.BeaconConfig().DomainRandao)
|
||||
epochSignature := priv.Sign(buf, domain)
|
||||
|
||||
// Set up transfer object for block
|
||||
transfers := []*ethpb.Transfer{
|
||||
{
|
||||
Slot: s.Slot,
|
||||
SenderIndex: 3,
|
||||
RecipientIndex: 4,
|
||||
Fee: params.BeaconConfig().MinDepositAmount,
|
||||
Amount: params.BeaconConfig().MinDepositAmount,
|
||||
SenderWithdrawalPublicKey: []byte("A"),
|
||||
},
|
||||
}
|
||||
buf = []byte{params.BeaconConfig().BLSWithdrawalPrefixByte}
|
||||
pubKey := []byte("A")
|
||||
hashed := hashutil.Hash(pubKey)
|
||||
buf = append(buf, hashed[:]...)
|
||||
s.Validators[3].WithdrawalCredentials = buf
|
||||
|
||||
// Set up attestations obj for block.
|
||||
encoded, err := ssz.HashTreeRoot(s.CurrentCrosslinks[0])
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
attestations := make([]*ethpb.Attestation, 128)
|
||||
for i := 0; i < len(attestations); i++ {
|
||||
attestations[i] = ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Root: []byte("hello-world")},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: uint64(i),
|
||||
ParentRoot: encoded[:],
|
||||
DataRoot: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
},
|
||||
Source: ðpb.Checkpoint{Root: []byte("hello-world")}},
|
||||
AggregationBits: bitfield.Bitlist{0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0,
|
||||
0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x01},
|
||||
CustodyBits: bitfield.NewBitlist(0),
|
||||
@@ -1016,13 +874,12 @@ func BenchmarkProcessBlk_65536Validators_FullBlock(b *testing.B) {
|
||||
Attestations: attestations,
|
||||
ProposerSlashings: proposerSlashings,
|
||||
AttesterSlashings: attesterSlashings,
|
||||
Transfers: transfers,
|
||||
},
|
||||
}
|
||||
|
||||
// Precache the shuffled indices
|
||||
for i := uint64(0); i < shardCount; i++ {
|
||||
if _, err := helpers.CrosslinkCommittee(s, 0, i); err != nil {
|
||||
for i := uint64(0); i < committeeCount; i++ {
|
||||
if _, err := helpers.BeaconCommittee(s, 0, i); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1047,7 +904,7 @@ func TestProcessBlk_AttsBasedOnValidatorCount(t *testing.T) {
|
||||
// Default at 256 validators, can raise this number with faster BLS.
|
||||
validatorCount := uint64(256)
|
||||
deposits, _, privKeys := testutil.SetupInitialDeposits(t, validatorCount)
|
||||
s, _ := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
s, _ := state.GenesisBeaconState(deposits, 0, ðpb.Eth1Data{BlockHash: make([]byte, 32)})
|
||||
s.Slot = params.BeaconConfig().SlotsPerEpoch
|
||||
|
||||
bitCount := validatorCount / params.BeaconConfig().SlotsPerEpoch
|
||||
@@ -1056,21 +913,13 @@ func TestProcessBlk_AttsBasedOnValidatorCount(t *testing.T) {
|
||||
for i := uint64(1); i < bitCount; i++ {
|
||||
aggBits.SetBitAt(i, true)
|
||||
}
|
||||
atts := make([]*ethpb.Attestation, 64)
|
||||
crosslinkRoot, _ := ssz.HashTreeRoot(s.CurrentCrosslinks[0])
|
||||
atts := make([]*ethpb.Attestation, 1)
|
||||
|
||||
for i := 0; i < len(atts); i++ {
|
||||
att := ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{Epoch: 0, Root: params.BeaconConfig().ZeroHash[:]},
|
||||
Target: ðpb.Checkpoint{Epoch: 0},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: uint64(i + 960),
|
||||
StartEpoch: 0,
|
||||
ParentRoot: crosslinkRoot[:],
|
||||
DataRoot: params.BeaconConfig().ZeroHash[:],
|
||||
},
|
||||
},
|
||||
Target: ðpb.Checkpoint{Epoch: 0}},
|
||||
AggregationBits: aggBits,
|
||||
CustodyBits: custodyBits,
|
||||
}
|
||||
@@ -1082,7 +931,8 @@ func TestProcessBlk_AttsBasedOnValidatorCount(t *testing.T) {
|
||||
Data: att.Data,
|
||||
CustodyBit: false,
|
||||
}
|
||||
domain := helpers.Domain(s.Fork, 0, params.BeaconConfig().DomainAttestation)
|
||||
|
||||
domain := helpers.Domain(s.Fork, 0, params.BeaconConfig().DomainBeaconAttester)
|
||||
sigs := make([]*bls.Signature, len(attestingIndices))
|
||||
for i, indice := range attestingIndices {
|
||||
hashTreeRoot, err := ssz.HashTreeRoot(dataAndCustodyBit)
|
||||
@@ -1120,10 +970,6 @@ func TestProcessBlk_AttsBasedOnValidatorCount(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCanProcessEpoch_TrueOnEpochs(t *testing.T) {
|
||||
if params.BeaconConfig().SlotsPerEpoch != 64 {
|
||||
t.Errorf("SlotsPerEpoch should be 64 for these tests to pass")
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
slot uint64
|
||||
canProcessEpoch bool
|
||||
@@ -1216,24 +1062,6 @@ func TestProcessOperations_OverMaxAttestations(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessOperations_OverMaxTransfers(t *testing.T) {
|
||||
block := ðpb.BeaconBlock{
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
Transfers: make([]*ethpb.Transfer, params.BeaconConfig().MaxTransfers+1),
|
||||
},
|
||||
}
|
||||
|
||||
want := fmt.Sprintf("number of transfers (%d) in block body exceeds allowed threshold of %d",
|
||||
len(block.Body.Transfers), params.BeaconConfig().MaxTransfers)
|
||||
if _, err := state.ProcessOperations(
|
||||
context.Background(),
|
||||
&pb.BeaconState{},
|
||||
block.Body,
|
||||
); !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("Expected %s, received %v", want, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessOperation_OverMaxVoluntaryExits(t *testing.T) {
|
||||
maxExits := params.BeaconConfig().MaxVoluntaryExits
|
||||
block := ðpb.BeaconBlock{
|
||||
@@ -1274,37 +1102,3 @@ func TestProcessOperations_IncorrectDeposits(t *testing.T) {
|
||||
t.Errorf("Expected %s, received %v", want, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessOperation_DuplicateTransfer(t *testing.T) {
|
||||
testConfig := params.BeaconConfig()
|
||||
testConfig.MaxTransfers = 2
|
||||
transfers := []*ethpb.Transfer{
|
||||
{
|
||||
Amount: 1,
|
||||
},
|
||||
{
|
||||
Amount: 1,
|
||||
},
|
||||
}
|
||||
registry := []*ethpb.Validator{}
|
||||
s := &pb.BeaconState{
|
||||
Validators: registry,
|
||||
Eth1Data: ðpb.Eth1Data{DepositCount: 100},
|
||||
Eth1DepositIndex: 98,
|
||||
}
|
||||
block := ðpb.BeaconBlock{
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
Transfers: transfers,
|
||||
Deposits: []*ethpb.Deposit{{}, {}},
|
||||
},
|
||||
}
|
||||
|
||||
want := "duplicate transfer"
|
||||
if _, err := state.ProcessOperations(
|
||||
context.Background(),
|
||||
s,
|
||||
block.Body,
|
||||
); !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("Expected %s, received %v", want, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ go_library(
|
||||
deps = [
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/mathutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/mathutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
@@ -63,7 +64,11 @@ func InitiateValidatorExit(state *pb.BeaconState, idx uint64) (*pb.BeaconState,
|
||||
exitQueueChurn++
|
||||
}
|
||||
}
|
||||
churn, err := helpers.ValidatorChurnLimit(state)
|
||||
activeValidatorCount, err := helpers.ActiveValidatorCount(state, helpers.CurrentEpoch(state))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get active validator count")
|
||||
}
|
||||
churn, err := helpers.ValidatorChurnLimit(activeValidatorCount)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get churn limit")
|
||||
}
|
||||
@@ -130,12 +135,11 @@ func SlashValidator(state *pb.BeaconState, slashedIdx uint64, whistleBlowerIdx u
|
||||
}
|
||||
|
||||
// ActivatedValidatorIndices determines the indices activated during the current epoch.
|
||||
func ActivatedValidatorIndices(state *pb.BeaconState) []uint64 {
|
||||
currentEpoch := helpers.CurrentEpoch(state)
|
||||
func ActivatedValidatorIndices(epoch uint64, validators []*ethpb.Validator) []uint64 {
|
||||
activations := make([]uint64, 0)
|
||||
delayedActivationEpoch := helpers.DelayedActivationExitEpoch(currentEpoch)
|
||||
for i := 0; i < len(state.Validators); i++ {
|
||||
val := state.Validators[i]
|
||||
delayedActivationEpoch := helpers.DelayedActivationExitEpoch(epoch)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
val := validators[i]
|
||||
if val.ActivationEpoch == delayedActivationEpoch {
|
||||
activations = append(activations, uint64(i))
|
||||
}
|
||||
@@ -144,12 +148,11 @@ func ActivatedValidatorIndices(state *pb.BeaconState) []uint64 {
|
||||
}
|
||||
|
||||
// SlashedValidatorIndices determines the indices slashed during the current epoch.
|
||||
func SlashedValidatorIndices(state *pb.BeaconState) []uint64 {
|
||||
currentEpoch := helpers.CurrentEpoch(state)
|
||||
func SlashedValidatorIndices(epoch uint64, validators []*ethpb.Validator) []uint64 {
|
||||
slashed := make([]uint64, 0)
|
||||
for i := 0; i < len(state.Validators); i++ {
|
||||
val := state.Validators[i]
|
||||
maxWithdrawableEpoch := mathutil.Max(val.WithdrawableEpoch, currentEpoch+params.BeaconConfig().EpochsPerSlashingsVector)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
val := validators[i]
|
||||
maxWithdrawableEpoch := mathutil.Max(val.WithdrawableEpoch, epoch+params.BeaconConfig().EpochsPerSlashingsVector)
|
||||
if val.WithdrawableEpoch == maxWithdrawableEpoch && val.Slashed {
|
||||
slashed = append(slashed, uint64(i))
|
||||
}
|
||||
@@ -158,11 +161,11 @@ func SlashedValidatorIndices(state *pb.BeaconState) []uint64 {
|
||||
}
|
||||
|
||||
// ExitedValidatorIndices determines the indices exited during the current epoch.
|
||||
func ExitedValidatorIndices(state *pb.BeaconState) ([]uint64, error) {
|
||||
func ExitedValidatorIndices(validators []*ethpb.Validator, activeValidatorCount uint64) ([]uint64, error) {
|
||||
exited := make([]uint64, 0)
|
||||
exitEpochs := make([]uint64, 0)
|
||||
for i := 0; i < len(state.Validators); i++ {
|
||||
val := state.Validators[i]
|
||||
for i := 0; i < len(validators); i++ {
|
||||
val := validators[i]
|
||||
if val.ExitEpoch != params.BeaconConfig().FarFutureEpoch {
|
||||
exitEpochs = append(exitEpochs, val.ExitEpoch)
|
||||
}
|
||||
@@ -176,12 +179,12 @@ func ExitedValidatorIndices(state *pb.BeaconState) ([]uint64, error) {
|
||||
|
||||
// We use the exit queue churn to determine if we have passed a churn limit.
|
||||
exitQueueChurn := 0
|
||||
for _, val := range state.Validators {
|
||||
for _, val := range validators {
|
||||
if val.ExitEpoch == exitQueueEpoch {
|
||||
exitQueueChurn++
|
||||
}
|
||||
}
|
||||
churn, err := helpers.ValidatorChurnLimit(state)
|
||||
churn, err := helpers.ValidatorChurnLimit(activeValidatorCount)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get churn limit")
|
||||
}
|
||||
@@ -189,7 +192,7 @@ func ExitedValidatorIndices(state *pb.BeaconState) ([]uint64, error) {
|
||||
exitQueueEpoch++
|
||||
}
|
||||
withdrawableEpoch := exitQueueEpoch + params.BeaconConfig().MinValidatorWithdrawabilityDelay
|
||||
for i, val := range state.Validators {
|
||||
for i, val := range validators {
|
||||
if val.ExitEpoch == exitQueueEpoch && val.WithdrawableEpoch == withdrawableEpoch {
|
||||
exited = append(exited, uint64(i))
|
||||
}
|
||||
|
||||
@@ -113,12 +113,12 @@ func TestSlashValidator_OK(t *testing.T) {
|
||||
}
|
||||
|
||||
bState := &pb.BeaconState{
|
||||
Validators: registry,
|
||||
Slot: 0,
|
||||
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
Balances: balances,
|
||||
Validators: registry,
|
||||
Slot: 0,
|
||||
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
|
||||
Balances: balances,
|
||||
}
|
||||
|
||||
slashedIdx := uint64(2)
|
||||
@@ -213,7 +213,7 @@ func TestActivatedValidatorIndices(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
activatedIndices := ActivatedValidatorIndices(tt.state)
|
||||
activatedIndices := ActivatedValidatorIndices(helpers.CurrentEpoch(tt.state), tt.state.Validators)
|
||||
if !reflect.DeepEqual(tt.wanted, activatedIndices) {
|
||||
t.Errorf("Wanted %v, received %v", tt.wanted, activatedIndices)
|
||||
}
|
||||
@@ -270,7 +270,7 @@ func TestSlashedValidatorIndices(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
slashedIndices := SlashedValidatorIndices(tt.state)
|
||||
slashedIndices := SlashedValidatorIndices(helpers.CurrentEpoch(tt.state), tt.state.Validators)
|
||||
if !reflect.DeepEqual(tt.wanted, slashedIndices) {
|
||||
t.Errorf("Wanted %v, received %v", tt.wanted, slashedIndices)
|
||||
}
|
||||
@@ -328,7 +328,11 @@ func TestExitedValidatorIndices(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
exitedIndices, err := ExitedValidatorIndices(tt.state)
|
||||
activeCount, err := helpers.ActiveValidatorCount(tt.state, helpers.CurrentEpoch(tt.state))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
exitedIndices, err := ExitedValidatorIndices(tt.state.Validators, activeCount)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ type Database interface {
|
||||
SaveBlocks(ctx context.Context, blocks []*ethpb.BeaconBlock) error
|
||||
SaveHeadBlockRoot(ctx context.Context, blockRoot [32]byte) error
|
||||
SaveGenesisBlockRoot(ctx context.Context, blockRoot [32]byte) error
|
||||
IsFinalizedBlock(ctx context.Context, blockRoot [32]byte) bool
|
||||
// Validator related methods.
|
||||
ValidatorLatestVote(ctx context.Context, validatorIdx uint64) (*pb.ValidatorLatestVote, error)
|
||||
HasValidatorLatestVote(ctx context.Context, validatorIdx uint64) bool
|
||||
|
||||
@@ -4,7 +4,10 @@ go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["filter.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/db/filters",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//tools:__subpackages__",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
|
||||
@@ -3,15 +3,13 @@
|
||||
// For example, one can specify a filter query for data by start epoch + end epoch + shard
|
||||
// for attestations, build a filter as follows, and respond to it accordingly:
|
||||
//
|
||||
// f := filters.NewFilter().SetStartEpoch(3).SetEndEpoch(5).SetShard(5)
|
||||
// f := filters.NewFilter().SetStartEpoch(3).SetEndEpoch(5)
|
||||
// for k, v := range f.Filters() {
|
||||
// switch k {
|
||||
// case filters.StartEpoch:
|
||||
// // Verify data matches filter criteria...
|
||||
// case filters.EndEpoch:
|
||||
// // Verify data matches filter criteria...
|
||||
// case filters.Shard:
|
||||
// // Verify data matches filter criteria...
|
||||
// }
|
||||
// }
|
||||
package filters
|
||||
@@ -31,18 +29,16 @@ const (
|
||||
StartEpoch FilterType = 3
|
||||
// EndEpoch is used for range filters of objects by their epoch (inclusive).
|
||||
EndEpoch FilterType = 4
|
||||
// Shard is used for filtering data by shard index.
|
||||
Shard FilterType = 5
|
||||
// HeadBlockRoot defines a filter for the head block root attribute of objects.
|
||||
HeadBlockRoot FilterType = 6
|
||||
HeadBlockRoot FilterType = 5
|
||||
// SourceEpoch defines a filter for the source epoch attribute of objects.
|
||||
SourceEpoch FilterType = 7
|
||||
SourceEpoch FilterType = 6
|
||||
// SourceRoot defines a filter for the source root attribute of objects.
|
||||
SourceRoot FilterType = 8
|
||||
SourceRoot FilterType = 7
|
||||
// TargetEpoch defines a filter for the target epoch attribute of objects.
|
||||
TargetEpoch FilterType = 9
|
||||
TargetEpoch FilterType = 8
|
||||
// TargetRoot defines a filter for the target root attribute of objects.
|
||||
TargetRoot FilterType = 10
|
||||
TargetRoot FilterType = 9
|
||||
)
|
||||
|
||||
// QueryFilter defines a generic interface for type-asserting
|
||||
@@ -125,9 +121,3 @@ func (q *QueryFilter) SetEndEpoch(val uint64) *QueryFilter {
|
||||
q.queries[EndEpoch] = val
|
||||
return q
|
||||
}
|
||||
|
||||
// SetShard enabled filtering by the Shard attribute of an object.
|
||||
func (q *QueryFilter) SetShard(val uint64) *QueryFilter {
|
||||
q.queries[Shard] = val
|
||||
return q
|
||||
}
|
||||
|
||||
@@ -8,11 +8,11 @@ func TestQueryFilter_ChainsCorrectly(t *testing.T) {
|
||||
f := NewFilter().
|
||||
SetStartSlot(2).
|
||||
SetEndSlot(4).
|
||||
SetParentRoot([]byte{3, 4, 5}).
|
||||
SetShard(0)
|
||||
SetParentRoot([]byte{3, 4, 5})
|
||||
|
||||
filterSet := f.Filters()
|
||||
if len(filterSet) != 4 {
|
||||
t.Errorf("Expected 4 filters to have been set, received %d", len(filterSet))
|
||||
if len(filterSet) != 3 {
|
||||
t.Errorf("Expected 3 filters to have been set, received %d", len(filterSet))
|
||||
}
|
||||
for k, v := range filterSet {
|
||||
switch k {
|
||||
@@ -22,8 +22,6 @@ func TestQueryFilter_ChainsCorrectly(t *testing.T) {
|
||||
t.Log(v.(uint64))
|
||||
case ParentRoot:
|
||||
t.Log(v.([]byte))
|
||||
case Shard:
|
||||
t.Log(v.(uint64))
|
||||
default:
|
||||
t.Log("Unknown filter type")
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ go_library(
|
||||
"blocks.go",
|
||||
"checkpoint.go",
|
||||
"deposit_contract.go",
|
||||
"finalized_block_roots.go",
|
||||
"kv.go",
|
||||
"operations.go",
|
||||
"schema.go",
|
||||
@@ -20,6 +21,7 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/db/kv",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = [
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/db/filters:go_default_library",
|
||||
"//proto/beacon/db:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
@@ -49,6 +51,7 @@ go_test(
|
||||
"blocks_test.go",
|
||||
"checkpoint_test.go",
|
||||
"deposit_contract_test.go",
|
||||
"finalized_block_roots_test.go",
|
||||
"kv_test.go",
|
||||
"operations_test.go",
|
||||
"slashings_test.go",
|
||||
@@ -61,6 +64,7 @@ go_test(
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
|
||||
@@ -67,13 +67,6 @@ func TestStore_ArchivedActiveValidatorChanges(t *testing.T) {
|
||||
Epoch: 5,
|
||||
Root: someRoot[:],
|
||||
},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 3,
|
||||
ParentRoot: someRoot[:],
|
||||
StartEpoch: 3,
|
||||
EndEpoch: 4,
|
||||
DataRoot: someRoot[:],
|
||||
},
|
||||
},
|
||||
},
|
||||
Attestation_2: ðpb.IndexedAttestation{
|
||||
@@ -87,13 +80,6 @@ func TestStore_ArchivedActiveValidatorChanges(t *testing.T) {
|
||||
Epoch: 5,
|
||||
Root: someRoot[:],
|
||||
},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 3,
|
||||
ParentRoot: someRoot[:],
|
||||
StartEpoch: 3,
|
||||
EndEpoch: 4,
|
||||
DataRoot: someRoot[:],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -118,9 +104,8 @@ func TestStore_ArchivedCommitteeInfo(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
someSeed := [32]byte{1, 2, 3}
|
||||
info := ðpb.ArchivedCommitteeInfo{
|
||||
Seed: someSeed[:],
|
||||
StartShard: 10,
|
||||
CommitteeCount: 4096,
|
||||
ProposerSeed: someSeed[:],
|
||||
AttesterSeed: someSeed[:],
|
||||
}
|
||||
epoch := uint64(10)
|
||||
if err := db.SaveArchivedCommitteeInfo(ctx, epoch, info); err != nil {
|
||||
|
||||
@@ -19,14 +19,7 @@ func TestStore_AttestationCRUD(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
att := ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 5,
|
||||
ParentRoot: []byte("parent"),
|
||||
StartEpoch: 1,
|
||||
EndEpoch: 2,
|
||||
},
|
||||
},
|
||||
Data: ðpb.AttestationData{Slot: 10},
|
||||
AggregationBits: bitfield.Bitlist{0b00000001, 0b1},
|
||||
CustodyBits: bitfield.NewBitlist(8),
|
||||
}
|
||||
@@ -76,12 +69,7 @@ func TestStore_AttestationsBatchDelete(t *testing.T) {
|
||||
totalAtts[i] = ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
BeaconBlockRoot: []byte("head"),
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: uint64(i),
|
||||
ParentRoot: []byte("parent"),
|
||||
StartEpoch: 1,
|
||||
EndEpoch: 2,
|
||||
},
|
||||
Slot: uint64(i),
|
||||
},
|
||||
AggregationBits: bitfield.Bitlist{0b00000001, 0b1},
|
||||
CustodyBits: bitfield.NewBitlist(8),
|
||||
@@ -116,7 +104,7 @@ func TestStore_AttestationsBatchDelete(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sort.Slice(retrieved, func(i, j int) bool {
|
||||
return retrieved[i].Data.Crosslink.Shard < retrieved[j].Data.Crosslink.Shard
|
||||
return retrieved[i].Data.Slot < retrieved[j].Data.Slot
|
||||
})
|
||||
if !reflect.DeepEqual(retrieved, oddAtts) {
|
||||
t.Errorf("Wanted %v, received %v", oddAtts, retrieved)
|
||||
@@ -130,14 +118,7 @@ func TestStore_BoltDontPanic(t *testing.T) {
|
||||
|
||||
for i := 0; i <= 100; i++ {
|
||||
att := ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 5,
|
||||
ParentRoot: []byte("parent"),
|
||||
StartEpoch: uint64(i + 1),
|
||||
EndEpoch: 2,
|
||||
},
|
||||
},
|
||||
Data: ðpb.AttestationData{Slot: uint64(i)},
|
||||
AggregationBits: bitfield.Bitlist{0b11},
|
||||
}
|
||||
ctx := context.Background()
|
||||
@@ -162,14 +143,7 @@ func TestStore_BoltDontPanic(t *testing.T) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
att := ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 5,
|
||||
ParentRoot: []byte("parent"),
|
||||
StartEpoch: uint64(startEpoch),
|
||||
EndEpoch: 2,
|
||||
},
|
||||
},
|
||||
Data: ðpb.AttestationData{Slot: uint64(startEpoch)},
|
||||
AggregationBits: bitfield.Bitlist{0b11},
|
||||
}
|
||||
ctx := context.Background()
|
||||
@@ -292,14 +266,7 @@ func TestStore_Attestations_FiltersCorrectly(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStore_Attestations_BitfieldLogic(t *testing.T) {
|
||||
commonData := ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 5,
|
||||
ParentRoot: []byte("parent"),
|
||||
StartEpoch: 1,
|
||||
EndEpoch: 2,
|
||||
},
|
||||
}
|
||||
commonData := ðpb.AttestationData{Slot: 10}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@@ -252,6 +252,10 @@ func TestStore_Blocks_FiltersCorrectly(t *testing.T) {
|
||||
filter: filters.NewFilter().SetEndSlot(8),
|
||||
expectedNumBlocks: 5,
|
||||
},
|
||||
{
|
||||
filter: filters.NewFilter().SetStartSlot(5).SetEndSlot(10),
|
||||
expectedNumBlocks: 4,
|
||||
},
|
||||
{
|
||||
// Composite filter criteria.
|
||||
filter: filters.NewFilter().
|
||||
|
||||
@@ -86,6 +86,10 @@ func (k *Store) SaveFinalizedCheckpoint(ctx context.Context, checkpoint *ethpb.C
|
||||
traceutil.AnnotateError(span, errMissingStateForFinalizedCheckpoint)
|
||||
return errMissingStateForFinalizedCheckpoint
|
||||
}
|
||||
return bucket.Put(finalizedCheckpointKey, enc)
|
||||
|
||||
if err := bucket.Put(finalizedCheckpointKey, enc); err != nil {
|
||||
return err
|
||||
}
|
||||
return k.updateFinalizedBlockRoots(ctx, tx, checkpoint)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,8 +5,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
)
|
||||
|
||||
@@ -36,12 +37,32 @@ func TestStore_FinalizedCheckpoint_CanSaveRetrieve(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
ctx := context.Background()
|
||||
root := bytesutil.ToBytes32([]byte{'B'})
|
||||
|
||||
genesis := bytesutil.ToBytes32([]byte{'G', 'E', 'N', 'E', 'S', 'I', 'S'})
|
||||
if err := db.SaveGenesisBlockRoot(ctx, genesis); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
blk := ðpb.BeaconBlock{
|
||||
ParentRoot: genesis[:],
|
||||
Slot: 40,
|
||||
}
|
||||
|
||||
root, err := ssz.SigningRoot(blk)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cp := ðpb.Checkpoint{
|
||||
Epoch: 5,
|
||||
Root: root[:],
|
||||
}
|
||||
|
||||
// a valid chain is required to save finalized checkpoint.
|
||||
if err := db.SaveBlock(ctx, blk); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// a state is required to save checkpoint
|
||||
if err := db.SaveState(ctx, &pb.BeaconState{}, root); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -109,7 +130,7 @@ func TestStore_FinalizedCheckpoint_StateMustExist(t *testing.T) {
|
||||
Root: []byte{'B'},
|
||||
}
|
||||
|
||||
if err := db.SaveFinalizedCheckpoint(ctx, cp); err != errMissingStateForFinalizedCheckpoint {
|
||||
if err := db.SaveFinalizedCheckpoint(ctx, cp); err != errMissingStateForFinalizedCheckpoint {
|
||||
t.Fatalf("wanted err %v, got %v", errMissingStateForFinalizedCheckpoint, err)
|
||||
}
|
||||
}
|
||||
|
||||
174
beacon-chain/db/kv/finalized_block_roots.go
Normal file
174
beacon-chain/db/kv/finalized_block_roots.go
Normal file
@@ -0,0 +1,174 @@
|
||||
package kv
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db/filters"
|
||||
dbpb "github.com/prysmaticlabs/prysm/proto/beacon/db"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/traceutil"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
var previousFinalizedCheckpointKey = []byte("previous-finalized-checkpoint")
|
||||
|
||||
// Blocks from the recent finalized epoch are not part of the finalized and canonical chain in this
|
||||
// index. These containers will be removed on the next update of finalized checkpoint. Note that
|
||||
// these block roots may be considered canonical in the "head view" of the beacon chain, but not so
|
||||
// in this index.
|
||||
var containerFinalizedButNotCanonical = []byte("recent block needs reindexing to determine canonical")
|
||||
|
||||
// The finalized block roots index tracks beacon blocks which are finalized in the canonical chain.
|
||||
// The finalized checkpoint contains the the epoch which was finalized and the highest beacon block
|
||||
// root where block.slot <= start_slot(epoch). As a result, we cannot index the finalized canonical
|
||||
// beacon block chain using the finalized root alone as this would exclude all other blocks in the
|
||||
// finalized epoch from being indexed as "final and canonical".
|
||||
//
|
||||
// The algorithm for building the index works as follows:
|
||||
// - De-index all finalized beacon block roots from previous_finalized_epoch to
|
||||
// new_finalized_epoch. (I.e. delete these roots from the index, to be re-indexed.)
|
||||
// - Build the canonical finalized chain by walking up the ancestry chain from the finalized block
|
||||
// root until a parent is found in the index or the parent is genesis.
|
||||
// - Add all block roots in the database where epoch(block.slot) == checkpoint.epoch.
|
||||
//
|
||||
// This method ensures that all blocks from the current finalized epoch are considered "final" while
|
||||
// maintaining only canonical and finalized blocks older than the current finalized epoch.
|
||||
func (kv *Store) updateFinalizedBlockRoots(ctx context.Context, tx *bolt.Tx, checkpoint *ethpb.Checkpoint) error {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.updateFinalizedBlockRoots")
|
||||
defer span.End()
|
||||
|
||||
bkt := tx.Bucket(finalizedBlockRootsIndexBucket)
|
||||
|
||||
root := checkpoint.Root
|
||||
var previousRoot []byte
|
||||
genesisRoot := tx.Bucket(blocksBucket).Get(genesisBlockRootKey)
|
||||
|
||||
// De-index recent finalized block roots, to be re-indexed.
|
||||
prevousFinalizedCheckpoint := ðpb.Checkpoint{}
|
||||
if b := bkt.Get(previousFinalizedCheckpointKey); b != nil {
|
||||
if err := proto.Unmarshal(b, prevousFinalizedCheckpoint); err != nil {
|
||||
traceutil.AnnotateError(span, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
previousFinalizedCheckpointStartSlot := helpers.StartSlot(prevousFinalizedCheckpoint.Epoch)
|
||||
recentFinalizedCheckpointEndSlot := helpers.StartSlot(checkpoint.Epoch + 1)
|
||||
blockRoots, err := kv.BlockRoots(ctx, filters.NewFilter().SetStartSlot(previousFinalizedCheckpointStartSlot).SetEndSlot(recentFinalizedCheckpointEndSlot))
|
||||
if err != nil {
|
||||
traceutil.AnnotateError(span, err)
|
||||
return err
|
||||
}
|
||||
for _, root := range blockRoots {
|
||||
if err := bkt.Delete(root[:]); err != nil {
|
||||
traceutil.AnnotateError(span, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Walk up the ancestry chain until we reach a block root present in the finalized block roots
|
||||
// index bucket or genesis block root.
|
||||
for {
|
||||
if bytes.Equal(root, genesisRoot) {
|
||||
break
|
||||
}
|
||||
|
||||
block, err := kv.Block(ctx, bytesutil.ToBytes32(root))
|
||||
if err != nil {
|
||||
traceutil.AnnotateError(span, err)
|
||||
return err
|
||||
}
|
||||
if block == nil {
|
||||
err := fmt.Errorf("missing block in database: block root=%#x", root)
|
||||
traceutil.AnnotateError(span, err)
|
||||
return err
|
||||
}
|
||||
|
||||
container := &dbpb.FinalizedBlockRootContainer{
|
||||
ParentRoot: block.ParentRoot,
|
||||
ChildRoot: previousRoot,
|
||||
}
|
||||
|
||||
enc, err := proto.Marshal(container)
|
||||
if err != nil {
|
||||
traceutil.AnnotateError(span, err)
|
||||
return err
|
||||
}
|
||||
if err := bkt.Put(root, enc); err != nil {
|
||||
traceutil.AnnotateError(span, err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Found parent, loop exit condition.
|
||||
if parentBytes := bkt.Get(block.ParentRoot); parentBytes != nil {
|
||||
parent := &dbpb.FinalizedBlockRootContainer{}
|
||||
if err := proto.Unmarshal(parentBytes, parent); err != nil {
|
||||
traceutil.AnnotateError(span, err)
|
||||
return err
|
||||
}
|
||||
parent.ChildRoot = root
|
||||
enc, err := proto.Marshal(parent)
|
||||
if err != nil {
|
||||
traceutil.AnnotateError(span, err)
|
||||
return err
|
||||
}
|
||||
if err := bkt.Put(block.ParentRoot, enc); err != nil {
|
||||
traceutil.AnnotateError(span, err)
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
previousRoot = root
|
||||
root = block.ParentRoot
|
||||
}
|
||||
|
||||
// Upsert blocks from the current finalized epoch.
|
||||
roots, err := kv.BlockRoots(ctx, filters.NewFilter().SetStartSlot(helpers.StartSlot(checkpoint.Epoch)).SetEndSlot(helpers.StartSlot(checkpoint.Epoch+1)))
|
||||
if err != nil {
|
||||
traceutil.AnnotateError(span, err)
|
||||
return err
|
||||
}
|
||||
for _, root := range roots {
|
||||
root := root[:]
|
||||
if bytes.Equal(root, checkpoint.Root) || bkt.Get(root) != nil {
|
||||
continue
|
||||
}
|
||||
if err := bkt.Put(root, containerFinalizedButNotCanonical); err != nil {
|
||||
traceutil.AnnotateError(span, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Update previous checkpoint
|
||||
enc, err := proto.Marshal(checkpoint)
|
||||
if err != nil {
|
||||
traceutil.AnnotateError(span, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return bkt.Put(previousFinalizedCheckpointKey, enc)
|
||||
}
|
||||
|
||||
// IsFinalizedBlock returns true if the block root is present in the finalized block root index.
|
||||
// A beacon block root contained exists in this index if it is considered finalized and canonical.
|
||||
// Note: beacon blocks from the latest finalized epoch return true, whether or not they are
|
||||
// considered canonical in the "head view" of the beacon node.
|
||||
func (kv *Store) IsFinalizedBlock(ctx context.Context, blockRoot [32]byte) bool {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.IsFinalizedBlock")
|
||||
defer span.End()
|
||||
|
||||
var exists bool
|
||||
err := kv.db.View(func(tx *bolt.Tx) error {
|
||||
exists = tx.Bucket(finalizedBlockRootsIndexBucket).Get(blockRoot[:]) != nil
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
traceutil.AnnotateError(span, err)
|
||||
}
|
||||
return exists
|
||||
}
|
||||
179
beacon-chain/db/kv/finalized_block_roots_test.go
Normal file
179
beacon-chain/db/kv/finalized_block_roots_test.go
Normal file
@@ -0,0 +1,179 @@
|
||||
package kv
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
var genesisBlockRoot = bytesutil.ToBytes32([]byte{'G', 'E', 'N', 'E', 'S', 'I', 'S'})
|
||||
|
||||
func TestStore_IsFinalizedBlock(t *testing.T) {
|
||||
slotsPerEpoch := int(params.BeaconConfig().SlotsPerEpoch)
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
ctx := context.Background()
|
||||
|
||||
if err := db.SaveGenesisBlockRoot(ctx, genesisBlockRoot); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
blks := makeBlocks(t, 0, slotsPerEpoch*3, genesisBlockRoot)
|
||||
if err := db.SaveBlocks(ctx, blks); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
root, err := ssz.SigningRoot(blks[slotsPerEpoch])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cp := ðpb.Checkpoint{
|
||||
Epoch: 1,
|
||||
Root: root[:],
|
||||
}
|
||||
|
||||
// a state is required to save checkpoint
|
||||
if err := db.SaveState(ctx, &pb.BeaconState{}, root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := db.SaveFinalizedCheckpoint(ctx, cp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// All blocks up to slotsPerEpoch*2 should be in the finalized index.
|
||||
for i := 0; i < slotsPerEpoch*2; i++ {
|
||||
root, err := ssz.SigningRoot(blks[i])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !db.IsFinalizedBlock(ctx, root) {
|
||||
t.Errorf("Block at index %d was not considered finalized in the index", i)
|
||||
}
|
||||
}
|
||||
for i := slotsPerEpoch * 3; i < len(blks); i++ {
|
||||
root, err := ssz.SigningRoot(blks[i])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if db.IsFinalizedBlock(ctx, root) {
|
||||
t.Errorf("Block at index %d was considered finalized in the index, but should not have", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This test scenario is to test a specific edge case where the finalized block root is not part of
|
||||
// the finalized and canonical chain.
|
||||
//
|
||||
// Example:
|
||||
// 0 1 2 3 4 5 6 slot
|
||||
// a <- b <-- d <- e <- f <- g roots
|
||||
// ^- c
|
||||
// Imagine that epochs are 2 slots and that epoch 1, 2, and 3 are finalized. Checkpoint roots would
|
||||
// be c, e, and g. In this scenario, c was a finalized checkpoint root but no block built upon it so
|
||||
// it should not be considered "final and canonical" in the view at slot 6.
|
||||
func TestStore_IsFinalized_ForkEdgeCase(t *testing.T) {
|
||||
slotsPerEpoch := int(params.BeaconConfig().SlotsPerEpoch)
|
||||
blocks0 := makeBlocks(t, slotsPerEpoch*0, slotsPerEpoch, genesisBlockRoot)
|
||||
blocks1 := append(
|
||||
makeBlocks(t, slotsPerEpoch*1, 1, bytesutil.ToBytes32(sszRootOrDie(t, blocks0[len(blocks0)-1]))), // No block builds off of the first block in epoch.
|
||||
makeBlocks(t, slotsPerEpoch*1+1, slotsPerEpoch-1, bytesutil.ToBytes32(sszRootOrDie(t, blocks0[len(blocks0)-1])))...,
|
||||
)
|
||||
blocks2 := makeBlocks(t, slotsPerEpoch*2, slotsPerEpoch, bytesutil.ToBytes32(sszRootOrDie(t, blocks1[len(blocks1)-1])))
|
||||
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
ctx := context.Background()
|
||||
|
||||
if err := db.SaveGenesisBlockRoot(ctx, genesisBlockRoot); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.SaveBlocks(ctx, blocks0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.SaveBlocks(ctx, blocks1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.SaveBlocks(ctx, blocks2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// First checkpoint
|
||||
checkpoint1 := ðpb.Checkpoint{
|
||||
Root: sszRootOrDie(t, blocks1[0]),
|
||||
Epoch: 1,
|
||||
}
|
||||
// A state is required to save checkpoint
|
||||
if err := db.SaveState(ctx, &pb.BeaconState{}, bytesutil.ToBytes32(checkpoint1.Root)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.SaveFinalizedCheckpoint(ctx, checkpoint1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// All blocks in blocks0 and blocks1 should be finalized and canonical.
|
||||
for i, block := range append(blocks0, blocks1...) {
|
||||
root := sszRootOrDie(t, block)
|
||||
if !db.IsFinalizedBlock(ctx, bytesutil.ToBytes32(root)) {
|
||||
t.Errorf("%d - Expected block %#x to be finalized", i, root)
|
||||
}
|
||||
}
|
||||
|
||||
// Second checkpoint
|
||||
checkpoint2 := ðpb.Checkpoint{
|
||||
Root: sszRootOrDie(t, blocks2[0]),
|
||||
Epoch: 2,
|
||||
}
|
||||
// A state is required to save checkpoint
|
||||
if err := db.SaveState(ctx, &pb.BeaconState{}, bytesutil.ToBytes32(checkpoint2.Root)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.SaveFinalizedCheckpoint(ctx, checkpoint2); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
// All blocks in blocks0 and blocks2 should be finalized and canonical.
|
||||
for i, block := range append(blocks0, blocks2...) {
|
||||
root := sszRootOrDie(t, block)
|
||||
if !db.IsFinalizedBlock(ctx, bytesutil.ToBytes32(root)) {
|
||||
t.Errorf("%d - Expected block %#x to be finalized", i, root)
|
||||
}
|
||||
}
|
||||
// All blocks in blocks1 should be finalized and canonical, except blocks1[0].
|
||||
for i, block := range blocks1 {
|
||||
root := sszRootOrDie(t, block)
|
||||
if db.IsFinalizedBlock(ctx, bytesutil.ToBytes32(root)) == (i == 0) {
|
||||
t.Errorf("Expected db.IsFinalizedBlock(ctx, blocks1[%d]) to be %v", i, i != 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sszRootOrDie(t *testing.T, block *ethpb.BeaconBlock) []byte {
|
||||
root, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return root[:]
|
||||
}
|
||||
|
||||
func makeBlocks(t *testing.T, i, n int, previousRoot [32]byte) []*ethpb.BeaconBlock {
|
||||
blocks := make([]*ethpb.BeaconBlock, n)
|
||||
for j := i; j < n+i; j++ {
|
||||
parentRoot := make([]byte, 32)
|
||||
copy(parentRoot, previousRoot[:])
|
||||
blocks[j-i] = ðpb.BeaconBlock{
|
||||
Slot: uint64(j + 1),
|
||||
ParentRoot: parentRoot,
|
||||
}
|
||||
var err error
|
||||
previousRoot, err = ssz.SigningRoot(blocks[j-i])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
return blocks
|
||||
}
|
||||
@@ -79,6 +79,7 @@ func NewKVStore(dirPath string) (*Store, error) {
|
||||
attestationTargetEpochIndicesBucket,
|
||||
blockSlotIndicesBucket,
|
||||
blockParentRootIndicesBucket,
|
||||
finalizedBlockRootsIndexBucket,
|
||||
)
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -29,6 +29,7 @@ var (
|
||||
attestationSourceEpochIndicesBucket = []byte("attestation-source-epoch-indices")
|
||||
attestationTargetRootIndicesBucket = []byte("attestation-target-root-indices")
|
||||
attestationTargetEpochIndicesBucket = []byte("attestation-target-epoch-indices")
|
||||
finalizedBlockRootsIndexBucket = []byte("finalized-block-roots-index")
|
||||
|
||||
// Specific item keys.
|
||||
headBlockRootKey = []byte("head-root")
|
||||
|
||||
@@ -56,17 +56,13 @@ func TestStore_AttesterSlashing_CRUD(t *testing.T) {
|
||||
Attestation_1: ðpb.IndexedAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
BeaconBlockRoot: make([]byte, 32),
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 5,
|
||||
},
|
||||
Slot: 5,
|
||||
},
|
||||
},
|
||||
Attestation_2: ðpb.IndexedAttestation{
|
||||
Data: ðpb.AttestationData{
|
||||
BeaconBlockRoot: make([]byte, 32),
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 7,
|
||||
},
|
||||
Slot: 7,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user