Merge branch 'devel'
This commit is contained in:
commit
b574799088
|
@ -9,7 +9,6 @@ init: # Scripts called at the very beginning
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
- NimBinaries
|
- NimBinaries
|
||||||
- p2pdCache
|
|
||||||
- jsonTestsCache
|
- jsonTestsCache
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
|
@ -36,8 +35,8 @@ build_script:
|
||||||
|
|
||||||
test_script:
|
test_script:
|
||||||
# the "go-checks" target fails in AppVeyor, for some reason; easier to disable than to debug
|
# the "go-checks" target fails in AppVeyor, for some reason; easier to disable than to debug
|
||||||
- mingw32-make -j2 ARCH_OVERRIDE=%PLATFORM% DISABLE_GO_CHECKS=1 P2PD_CACHE=p2pdCache LOG_LEVEL=TRACE
|
- mingw32-make -j2 ARCH_OVERRIDE=%PLATFORM% DISABLE_GO_CHECKS=1 LOG_LEVEL=TRACE
|
||||||
- mingw32-make -j2 ARCH_OVERRIDE=%PLATFORM% DISABLE_GO_CHECKS=1 P2PD_CACHE=p2pdCache LOG_LEVEL=TRACE NIMFLAGS="-d:NETWORK_TYPE=libp2p -d:testnet_servers_image"
|
- mingw32-make -j2 ARCH_OVERRIDE=%PLATFORM% DISABLE_GO_CHECKS=1 LOG_LEVEL=TRACE NIMFLAGS="-d:testnet_servers_image"
|
||||||
- mingw32-make -j2 ARCH_OVERRIDE=%PLATFORM% DISABLE_TEST_FIXTURES_SCRIPT=1 DISABLE_GO_CHECKS=1 test
|
- mingw32-make -j2 ARCH_OVERRIDE=%PLATFORM% DISABLE_TEST_FIXTURES_SCRIPT=1 DISABLE_GO_CHECKS=1 test
|
||||||
|
|
||||||
deploy: off
|
deploy: off
|
||||||
|
|
|
@ -136,6 +136,8 @@
|
||||||
[submodule "vendor/nim-sqlite3-abi"]
|
[submodule "vendor/nim-sqlite3-abi"]
|
||||||
path = vendor/nim-sqlite3-abi
|
path = vendor/nim-sqlite3-abi
|
||||||
url = https://github.com/arnetheduck/nim-sqlite3-abi.git
|
url = https://github.com/arnetheduck/nim-sqlite3-abi.git
|
||||||
|
ignore = dirty
|
||||||
|
branch = master
|
||||||
[submodule "vendor/nim-testutils"]
|
[submodule "vendor/nim-testutils"]
|
||||||
path = vendor/nim-testutils
|
path = vendor/nim-testutils
|
||||||
url = https://github.com/status-im/nim-testutils.git
|
url = https://github.com/status-im/nim-testutils.git
|
||||||
|
|
|
@ -50,6 +50,6 @@ script:
|
||||||
# Building Nim-1.0.4 takes up to 10 minutes on Travis - the time limit after which jobs are cancelled for having no output
|
# Building Nim-1.0.4 takes up to 10 minutes on Travis - the time limit after which jobs are cancelled for having no output
|
||||||
- make -j${NPROC} NIMFLAGS="--parallelBuild:${NPROC}" V=1 update # to allow a newer Nim version to be detected
|
- make -j${NPROC} NIMFLAGS="--parallelBuild:${NPROC}" V=1 update # to allow a newer Nim version to be detected
|
||||||
- make -j${NPROC} NIMFLAGS="--parallelBuild:${NPROC}" LOG_LEVEL=TRACE
|
- make -j${NPROC} NIMFLAGS="--parallelBuild:${NPROC}" LOG_LEVEL=TRACE
|
||||||
- make -j${NPROC} NIMFLAGS="--parallelBuild:${NPROC} -d:NETWORK_TYPE=libp2p -d:testnet_servers_image" LOG_LEVEL=TRACE
|
- make -j${NPROC} NIMFLAGS="--parallelBuild:${NPROC} -d:testnet_servers_image" LOG_LEVEL=TRACE
|
||||||
- make -j${NPROC} NIMFLAGS="--parallelBuild:${NPROC}" DISABLE_TEST_FIXTURES_SCRIPT=1 test
|
- make -j${NPROC} NIMFLAGS="--parallelBuild:${NPROC}" DISABLE_TEST_FIXTURES_SCRIPT=1 test
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,14 @@ OK: 2/2 Fail: 0/2 Skip: 0/2
|
||||||
+ Attestation topics OK
|
+ Attestation topics OK
|
||||||
```
|
```
|
||||||
OK: 1/1 Fail: 0/1 Skip: 0/1
|
OK: 1/1 Fail: 0/1 Skip: 0/1
|
||||||
## Official - 0.11.0 - constants & config [Preset: mainnet]
|
## Interop
|
||||||
|
```diff
|
||||||
|
+ Interop genesis OK
|
||||||
|
+ Interop signatures OK
|
||||||
|
+ Mocked start private key OK
|
||||||
|
```
|
||||||
|
OK: 3/3 Fail: 0/3 Skip: 0/3
|
||||||
|
## Official - 0.11.1 - constants & config [Preset: mainnet]
|
||||||
```diff
|
```diff
|
||||||
+ BASE_REWARD_FACTOR 64 [Preset: mainnet] OK
|
+ BASE_REWARD_FACTOR 64 [Preset: mainnet] OK
|
||||||
+ BLS_WITHDRAWAL_PREFIX "0x00" [Preset: mainnet] OK
|
+ BLS_WITHDRAWAL_PREFIX "0x00" [Preset: mainnet] OK
|
||||||
|
@ -80,7 +87,7 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
|
||||||
+ EPOCHS_PER_SLASHINGS_VECTOR 8192 [Preset: mainnet] OK
|
+ EPOCHS_PER_SLASHINGS_VECTOR 8192 [Preset: mainnet] OK
|
||||||
+ ETH1_FOLLOW_DISTANCE 1024 [Preset: mainnet] OK
|
+ ETH1_FOLLOW_DISTANCE 1024 [Preset: mainnet] OK
|
||||||
+ GASPRICE_ADJUSTMENT_COEFFICIENT 8 [Preset: mainnet] OK
|
+ GASPRICE_ADJUSTMENT_COEFFICIENT 8 [Preset: mainnet] OK
|
||||||
+ GENESIS_FORK_VERSION "0x00000000" [Preset: mainnet] OK
|
- GENESIS_FORK_VERSION "0x00000000" [Preset: mainnet] Fail
|
||||||
+ HISTORICAL_ROOTS_LIMIT 16777216 [Preset: mainnet] OK
|
+ HISTORICAL_ROOTS_LIMIT 16777216 [Preset: mainnet] OK
|
||||||
+ HYSTERESIS_DOWNWARD_MULTIPLIER 1 [Preset: mainnet] OK
|
+ HYSTERESIS_DOWNWARD_MULTIPLIER 1 [Preset: mainnet] OK
|
||||||
+ HYSTERESIS_QUOTIENT 4 [Preset: mainnet] OK
|
+ HYSTERESIS_QUOTIENT 4 [Preset: mainnet] OK
|
||||||
|
@ -141,7 +148,7 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
|
||||||
+ VALIDATOR_REGISTRY_LIMIT 1099511627776 [Preset: mainnet] OK
|
+ VALIDATOR_REGISTRY_LIMIT 1099511627776 [Preset: mainnet] OK
|
||||||
+ WHISTLEBLOWER_REWARD_QUOTIENT 512 [Preset: mainnet] OK
|
+ WHISTLEBLOWER_REWARD_QUOTIENT 512 [Preset: mainnet] OK
|
||||||
```
|
```
|
||||||
OK: 85/87 Fail: 2/87 Skip: 0/87
|
OK: 84/87 Fail: 3/87 Skip: 0/87
|
||||||
## PeerPool testing suite
|
## PeerPool testing suite
|
||||||
```diff
|
```diff
|
||||||
+ Access peers by key test OK
|
+ Access peers by key test OK
|
||||||
|
@ -222,4 +229,4 @@ OK: 4/4 Fail: 0/4 Skip: 0/4
|
||||||
OK: 8/8 Fail: 0/8 Skip: 0/8
|
OK: 8/8 Fail: 0/8 Skip: 0/8
|
||||||
|
|
||||||
---TOTAL---
|
---TOTAL---
|
||||||
OK: 142/144 Fail: 2/144 Skip: 0/144
|
OK: 144/147 Fail: 3/147 Skip: 0/147
|
||||||
|
|
|
@ -78,7 +78,14 @@ OK: 2/2 Fail: 0/2 Skip: 0/2
|
||||||
+ Attestation topics OK
|
+ Attestation topics OK
|
||||||
```
|
```
|
||||||
OK: 1/1 Fail: 0/1 Skip: 0/1
|
OK: 1/1 Fail: 0/1 Skip: 0/1
|
||||||
## Official - 0.11.0 - constants & config [Preset: minimal]
|
## Interop
|
||||||
|
```diff
|
||||||
|
+ Interop genesis OK
|
||||||
|
+ Interop signatures OK
|
||||||
|
+ Mocked start private key OK
|
||||||
|
```
|
||||||
|
OK: 3/3 Fail: 0/3 Skip: 0/3
|
||||||
|
## Official - 0.11.1 - constants & config [Preset: minimal]
|
||||||
```diff
|
```diff
|
||||||
+ BASE_REWARD_FACTOR 64 [Preset: minimal] OK
|
+ BASE_REWARD_FACTOR 64 [Preset: minimal] OK
|
||||||
+ BLS_WITHDRAWAL_PREFIX "0x00" [Preset: minimal] OK
|
+ BLS_WITHDRAWAL_PREFIX "0x00" [Preset: minimal] OK
|
||||||
|
@ -107,7 +114,7 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
|
||||||
+ EPOCHS_PER_SLASHINGS_VECTOR 64 [Preset: minimal] OK
|
+ EPOCHS_PER_SLASHINGS_VECTOR 64 [Preset: minimal] OK
|
||||||
+ ETH1_FOLLOW_DISTANCE 16 [Preset: minimal] OK
|
+ ETH1_FOLLOW_DISTANCE 16 [Preset: minimal] OK
|
||||||
+ GASPRICE_ADJUSTMENT_COEFFICIENT 8 [Preset: minimal] OK
|
+ GASPRICE_ADJUSTMENT_COEFFICIENT 8 [Preset: minimal] OK
|
||||||
+ GENESIS_FORK_VERSION "0x00000001" [Preset: minimal] OK
|
- GENESIS_FORK_VERSION "0x00000001" [Preset: minimal] Fail
|
||||||
+ HISTORICAL_ROOTS_LIMIT 16777216 [Preset: minimal] OK
|
+ HISTORICAL_ROOTS_LIMIT 16777216 [Preset: minimal] OK
|
||||||
+ HYSTERESIS_DOWNWARD_MULTIPLIER 1 [Preset: minimal] OK
|
+ HYSTERESIS_DOWNWARD_MULTIPLIER 1 [Preset: minimal] OK
|
||||||
+ HYSTERESIS_QUOTIENT 4 [Preset: minimal] OK
|
+ HYSTERESIS_QUOTIENT 4 [Preset: minimal] OK
|
||||||
|
@ -168,7 +175,7 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
|
||||||
+ VALIDATOR_REGISTRY_LIMIT 1099511627776 [Preset: minimal] OK
|
+ VALIDATOR_REGISTRY_LIMIT 1099511627776 [Preset: minimal] OK
|
||||||
+ WHISTLEBLOWER_REWARD_QUOTIENT 512 [Preset: minimal] OK
|
+ WHISTLEBLOWER_REWARD_QUOTIENT 512 [Preset: minimal] OK
|
||||||
```
|
```
|
||||||
OK: 85/87 Fail: 2/87 Skip: 0/87
|
OK: 84/87 Fail: 3/87 Skip: 0/87
|
||||||
## PeerPool testing suite
|
## PeerPool testing suite
|
||||||
```diff
|
```diff
|
||||||
+ Access peers by key test OK
|
+ Access peers by key test OK
|
||||||
|
@ -249,4 +256,4 @@ OK: 4/4 Fail: 0/4 Skip: 0/4
|
||||||
OK: 8/8 Fail: 0/8 Skip: 0/8
|
OK: 8/8 Fail: 0/8 Skip: 0/8
|
||||||
|
|
||||||
---TOTAL---
|
---TOTAL---
|
||||||
OK: 157/159 Fail: 2/159 Skip: 0/159
|
OK: 159/162 Fail: 3/162 Skip: 0/162
|
||||||
|
|
|
@ -45,6 +45,7 @@ OK: 3/3 Fail: 0/3 Skip: 0/3
|
||||||
+ [Invalid] after_epoch_slots OK
|
+ [Invalid] after_epoch_slots OK
|
||||||
+ [Invalid] bad_source_root OK
|
+ [Invalid] bad_source_root OK
|
||||||
+ [Invalid] before_inclusion_delay OK
|
+ [Invalid] before_inclusion_delay OK
|
||||||
|
+ [Invalid] empty_aggregation_bits OK
|
||||||
+ [Invalid] future_target_epoch OK
|
+ [Invalid] future_target_epoch OK
|
||||||
+ [Invalid] invalid_attestation_signature OK
|
+ [Invalid] invalid_attestation_signature OK
|
||||||
+ [Invalid] invalid_current_source_root OK
|
+ [Invalid] invalid_current_source_root OK
|
||||||
|
@ -58,7 +59,6 @@ OK: 3/3 Fail: 0/3 Skip: 0/3
|
||||||
+ [Invalid] too_many_aggregation_bits OK
|
+ [Invalid] too_many_aggregation_bits OK
|
||||||
+ [Invalid] wrong_index_for_committee_signature OK
|
+ [Invalid] wrong_index_for_committee_signature OK
|
||||||
+ [Invalid] wrong_index_for_slot OK
|
+ [Invalid] wrong_index_for_slot OK
|
||||||
+ [Valid] empty_aggregation_bits OK
|
|
||||||
+ [Valid] success OK
|
+ [Valid] success OK
|
||||||
+ [Valid] success_multi_proposer_index_iterations OK
|
+ [Valid] success_multi_proposer_index_iterations OK
|
||||||
+ [Valid] success_previous_epoch OK
|
+ [Valid] success_previous_epoch OK
|
||||||
|
|
|
@ -45,6 +45,7 @@ OK: 3/3 Fail: 0/3 Skip: 0/3
|
||||||
+ [Invalid] after_epoch_slots OK
|
+ [Invalid] after_epoch_slots OK
|
||||||
+ [Invalid] bad_source_root OK
|
+ [Invalid] bad_source_root OK
|
||||||
+ [Invalid] before_inclusion_delay OK
|
+ [Invalid] before_inclusion_delay OK
|
||||||
|
+ [Invalid] empty_aggregation_bits OK
|
||||||
+ [Invalid] future_target_epoch OK
|
+ [Invalid] future_target_epoch OK
|
||||||
+ [Invalid] invalid_attestation_signature OK
|
+ [Invalid] invalid_attestation_signature OK
|
||||||
+ [Invalid] invalid_current_source_root OK
|
+ [Invalid] invalid_current_source_root OK
|
||||||
|
@ -58,7 +59,6 @@ OK: 3/3 Fail: 0/3 Skip: 0/3
|
||||||
+ [Invalid] too_many_aggregation_bits OK
|
+ [Invalid] too_many_aggregation_bits OK
|
||||||
+ [Invalid] wrong_index_for_committee_signature OK
|
+ [Invalid] wrong_index_for_committee_signature OK
|
||||||
+ [Invalid] wrong_index_for_slot OK
|
+ [Invalid] wrong_index_for_slot OK
|
||||||
+ [Valid] empty_aggregation_bits OK
|
|
||||||
+ [Valid] success OK
|
+ [Valid] success OK
|
||||||
+ [Valid] success_multi_proposer_index_iterations OK
|
+ [Valid] success_multi_proposer_index_iterations OK
|
||||||
+ [Valid] success_previous_epoch OK
|
+ [Valid] success_previous_epoch OK
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
FixtureSSZConsensus-mainnet
|
FixtureSSZConsensus-mainnet
|
||||||
===
|
===
|
||||||
## Official - 0.11.0 - SSZ consensus objects [Preset: mainnet]
|
## Official - 0.11.1 - SSZ consensus objects [Preset: mainnet]
|
||||||
```diff
|
```diff
|
||||||
+ Testing AggregateAndProof OK
|
+ Testing AggregateAndProof OK
|
||||||
+ Testing Attestation OK
|
+ Testing Attestation OK
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
FixtureSSZConsensus-minimal
|
FixtureSSZConsensus-minimal
|
||||||
===
|
===
|
||||||
## Official - 0.11.0 - SSZ consensus objects [Preset: minimal]
|
## Official - 0.11.1 - SSZ consensus objects [Preset: minimal]
|
||||||
```diff
|
```diff
|
||||||
+ Testing AggregateAndProof OK
|
+ Testing AggregateAndProof OK
|
||||||
+ Testing Attestation OK
|
+ Testing Attestation OK
|
||||||
|
|
|
@ -22,7 +22,7 @@ def runStages() {
|
||||||
"tools": {
|
"tools": {
|
||||||
stage("Tools") {
|
stage("Tools") {
|
||||||
sh "make -j${env.NPROC}"
|
sh "make -j${env.NPROC}"
|
||||||
sh "make -j${env.NPROC} LOG_LEVEL=TRACE NIMFLAGS='-d:NETWORK_TYPE=libp2p -d:testnet_servers_image'"
|
sh "make -j${env.NPROC} LOG_LEVEL=TRACE NIMFLAGS='-d:testnet_servers_image'"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"test suite": {
|
"test suite": {
|
||||||
|
@ -31,8 +31,8 @@ def runStages() {
|
||||||
}
|
}
|
||||||
if ("${NODE_NAME}" ==~ /linux.*/) {
|
if ("${NODE_NAME}" ==~ /linux.*/) {
|
||||||
stage("testnet finalization") {
|
stage("testnet finalization") {
|
||||||
sh "./scripts/launch_local_testnet.sh --testnet 0 --nodes 4 --disable-htop -- --verify-finalization --stop-at-epoch=5"
|
sh "./scripts/launch_local_testnet.sh --testnet 0 --nodes 4 --log-level INFO --disable-htop -- --verify-finalization --stop-at-epoch=5"
|
||||||
sh "./scripts/launch_local_testnet.sh --testnet 1 --nodes 4 --disable-htop -- --verify-finalization --stop-at-epoch=5"
|
sh "./scripts/launch_local_testnet.sh --testnet 1 --nodes 4 --log-level INFO --disable-htop -- --verify-finalization --stop-at-epoch=5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
18
Makefile
18
Makefile
|
@ -37,7 +37,6 @@ TOOLS_CSV := $(subst $(SPACE),$(COMMA),$(TOOLS))
|
||||||
build-system-checks \
|
build-system-checks \
|
||||||
deps \
|
deps \
|
||||||
update \
|
update \
|
||||||
p2pd \
|
|
||||||
test \
|
test \
|
||||||
$(TOOLS) \
|
$(TOOLS) \
|
||||||
clean_eth2_network_simulation_files \
|
clean_eth2_network_simulation_files \
|
||||||
|
@ -59,12 +58,11 @@ endif
|
||||||
# must be included after the default target
|
# must be included after the default target
|
||||||
-include $(BUILD_SYSTEM_DIR)/makefiles/targets.mk
|
-include $(BUILD_SYSTEM_DIR)/makefiles/targets.mk
|
||||||
|
|
||||||
# "--import" can't be added to config.nims, for some reason
|
# "--define:release" implies "--stacktrace:off" and it cannot be added to config.nims
|
||||||
# "--define:release" implies "--stacktrace:off" and it cannot be added to config.nims either
|
|
||||||
ifeq ($(USE_LIBBACKTRACE), 0)
|
ifeq ($(USE_LIBBACKTRACE), 0)
|
||||||
NIM_PARAMS := $(NIM_PARAMS) -d:debug -d:disable_libbacktrace
|
NIM_PARAMS := $(NIM_PARAMS) -d:debug -d:disable_libbacktrace
|
||||||
else
|
else
|
||||||
NIM_PARAMS := $(NIM_PARAMS) -d:release --import:libbacktrace
|
NIM_PARAMS := $(NIM_PARAMS) -d:release
|
||||||
endif
|
endif
|
||||||
|
|
||||||
#- the Windows build fails on Azure Pipelines if we have Unicode symbols copy/pasted here,
|
#- the Windows build fails on Azure Pipelines if we have Unicode symbols copy/pasted here,
|
||||||
|
@ -80,7 +78,7 @@ build-system-checks:
|
||||||
}; \
|
}; \
|
||||||
exit 0
|
exit 0
|
||||||
|
|
||||||
deps: | deps-common beacon_chain.nims p2pd
|
deps: | deps-common beacon_chain.nims
|
||||||
ifneq ($(USE_LIBBACKTRACE), 0)
|
ifneq ($(USE_LIBBACKTRACE), 0)
|
||||||
deps: | libbacktrace
|
deps: | libbacktrace
|
||||||
endif
|
endif
|
||||||
|
@ -96,13 +94,7 @@ beacon_chain.nims:
|
||||||
|
|
||||||
# nim-libbacktrace
|
# nim-libbacktrace
|
||||||
libbacktrace:
|
libbacktrace:
|
||||||
+ $(MAKE) -C vendor/nim-libbacktrace BUILD_CXX_LIB=0
|
+ $(MAKE) -C vendor/nim-libbacktrace BUILD_CXX_LIB=0 $(HANDLE_OUTPUT)
|
||||||
|
|
||||||
P2PD_CACHE :=
|
|
||||||
p2pd: | go-checks
|
|
||||||
BUILD_MSG="$(BUILD_MSG) $@" \
|
|
||||||
V=$(V) \
|
|
||||||
$(ENV_SCRIPT) $(BUILD_SYSTEM_DIR)/scripts/build_p2pd.sh "$(P2PD_CACHE)"
|
|
||||||
|
|
||||||
# Windows 10 with WSL enabled, but no distro installed, fails if "../../nimble.sh" is executed directly
|
# Windows 10 with WSL enabled, but no distro installed, fails if "../../nimble.sh" is executed directly
|
||||||
# in a Makefile recipe but works when prefixing it with `bash`. No idea how the PATH is overridden.
|
# in a Makefile recipe but works when prefixing it with `bash`. No idea how the PATH is overridden.
|
||||||
|
@ -121,7 +113,7 @@ $(TOOLS): | build deps
|
||||||
clean_eth2_network_simulation_files:
|
clean_eth2_network_simulation_files:
|
||||||
rm -rf tests/simulation/{data,validators}
|
rm -rf tests/simulation/{data,validators}
|
||||||
|
|
||||||
eth2_network_simulation: | build deps p2pd clean_eth2_network_simulation_files process_dashboard
|
eth2_network_simulation: | build deps clean_eth2_network_simulation_files process_dashboard
|
||||||
+ GIT_ROOT="$$PWD" NIMFLAGS="$(NIMFLAGS)" LOG_LEVEL="$(LOG_LEVEL)" tests/simulation/start.sh
|
+ GIT_ROOT="$$PWD" NIMFLAGS="$(NIMFLAGS)" LOG_LEVEL="$(LOG_LEVEL)" tests/simulation/start.sh
|
||||||
|
|
||||||
clean-testnet0:
|
clean-testnet0:
|
||||||
|
|
34
README.md
34
README.md
|
@ -48,9 +48,8 @@ You can check where the beacon chain fits in the Ethereum ecosystem our Two-Poin
|
||||||
|
|
||||||
At the moment, Nimbus has to be built from source.
|
At the moment, Nimbus has to be built from source.
|
||||||
|
|
||||||
Nimbus has 4 external dependencies:
|
Nimbus has the following external dependencies:
|
||||||
|
|
||||||
* Go 1.12 (for compiling libp2p daemon - being phased out)
|
|
||||||
* Developer tools (C compiler, Make, Bash, Git)
|
* Developer tools (C compiler, Make, Bash, Git)
|
||||||
* PCRE
|
* PCRE
|
||||||
|
|
||||||
|
@ -61,13 +60,13 @@ Nim is not an external dependency, Nimbus will build its own local copy.
|
||||||
On common Linux distributions the dependencies can be installed with:
|
On common Linux distributions the dependencies can be installed with:
|
||||||
```sh
|
```sh
|
||||||
# Debian and Ubuntu
|
# Debian and Ubuntu
|
||||||
sudo apt-get install build-essential git golang-go libpcre3-dev
|
sudo apt-get install build-essential git libpcre3-dev
|
||||||
|
|
||||||
# Fedora
|
# Fedora
|
||||||
dnf install @development-tools go pcre
|
dnf install @development-tools pcre
|
||||||
|
|
||||||
# Archlinux, using an AUR manager for pcre-static
|
# Archlinux, using an AUR manager for pcre-static
|
||||||
yourAURmanager -S base-devel go pcre-static
|
yourAURmanager -S base-devel pcre-static
|
||||||
```
|
```
|
||||||
|
|
||||||
### MacOS
|
### MacOS
|
||||||
|
@ -75,17 +74,14 @@ yourAURmanager -S base-devel go pcre-static
|
||||||
Assuming you use [Homebrew](https://brew.sh/) to manage packages
|
Assuming you use [Homebrew](https://brew.sh/) to manage packages
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
brew install go pcre
|
brew install pcre
|
||||||
```
|
```
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
* install [Go](https://golang.org/doc/install#windows)
|
|
||||||
You can install the developer tools by following the instruction in our [Windows dev environment section](#windows-dev-environment).
|
You can install the developer tools by following the instruction in our [Windows dev environment section](#windows-dev-environment).
|
||||||
It also provides a downloading script for prebuilt PCRE.
|
It also provides a downloading script for prebuilt PCRE.
|
||||||
|
|
||||||
If you choose to install Go from source, both Go and Nimbus requires the same initial steps of installing Mingw.
|
|
||||||
|
|
||||||
### Android
|
### Android
|
||||||
|
|
||||||
* Install the [Termux](https://termux.com) app from FDroid or the Google Play store
|
* Install the [Termux](https://termux.com) app from FDroid or the Google Play store
|
||||||
|
@ -95,7 +91,7 @@ Note, the Ubuntu PRoot is known to contain all Nimbus prerequisites compiled on
|
||||||
*Assuming Ubuntu PRoot is used*
|
*Assuming Ubuntu PRoot is used*
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
apt install build-essential git golang-go libpcre3-dev
|
apt install build-essential git libpcre3-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
## For users
|
## For users
|
||||||
|
@ -295,24 +291,8 @@ sudo reboot
|
||||||
# Install prerequisites
|
# Install prerequisites
|
||||||
sudo apt-get install git libgflags-dev libsnappy-dev libpcre3-dev
|
sudo apt-get install git libgflags-dev libsnappy-dev libpcre3-dev
|
||||||
|
|
||||||
mkdir status
|
|
||||||
cd status
|
|
||||||
|
|
||||||
# Install Go at least 1.12 (Buster only includes up to 1.11)
|
|
||||||
# Raspbian is 32-bit, so the package is go1.XX.X.linux-armv6l.tar.gz (and not arm64)
|
|
||||||
curl -O https://storage.googleapis.com/golang/go1.13.3.linux-armv6l.tar.gz
|
|
||||||
sudo tar -C /usr/local -xzf go1.13.3.linux-armv6l.tar.gz
|
|
||||||
|
|
||||||
echo '# Go install' >> ~/.profile
|
|
||||||
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.profile
|
|
||||||
|
|
||||||
# Reload the environment variable changes
|
|
||||||
source ~/.profile
|
|
||||||
|
|
||||||
git clone https://github.com/status-im/nim-beacon-chain.git
|
|
||||||
|
|
||||||
cd nim-beacon-chain
|
|
||||||
# Then you can follow instructions for Linux.
|
# Then you can follow instructions for Linux.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Makefile tips and tricks for developers
|
### Makefile tips and tricks for developers
|
||||||
|
|
|
@ -14,15 +14,9 @@ jobs:
|
||||||
- task: CacheBeta@1
|
- task: CacheBeta@1
|
||||||
displayName: 'cache Nim binaries'
|
displayName: 'cache Nim binaries'
|
||||||
inputs:
|
inputs:
|
||||||
key: NimBinaries | $(Agent.OS) | $(PLATFORM) | "$(Build.SourceBranchName)" | "v2"
|
key: NimBinaries | $(Agent.OS) | $(PLATFORM) | "$(Build.SourceBranchName)" | "v4"
|
||||||
path: NimBinaries
|
path: NimBinaries
|
||||||
|
|
||||||
- task: CacheBeta@1
|
|
||||||
displayName: 'cache p2pd binaries'
|
|
||||||
inputs:
|
|
||||||
key: p2pdCache | $(Agent.OS) | $(PLATFORM) | "$(Build.SourceBranchName)"
|
|
||||||
path: p2pdCache
|
|
||||||
|
|
||||||
- task: CacheBeta@1
|
- task: CacheBeta@1
|
||||||
displayName: 'cache official test fixtures'
|
displayName: 'cache official test fixtures'
|
||||||
inputs:
|
inputs:
|
||||||
|
@ -71,8 +65,10 @@ jobs:
|
||||||
scripts/setup_official_tests.sh jsonTestsCache
|
scripts/setup_official_tests.sh jsonTestsCache
|
||||||
mingw32-make -j2 ARCH_OVERRIDE=${PLATFORM} CI_CACHE=NimBinaries update
|
mingw32-make -j2 ARCH_OVERRIDE=${PLATFORM} CI_CACHE=NimBinaries update
|
||||||
mingw32-make -j2 ARCH_OVERRIDE=${PLATFORM} fetch-dlls
|
mingw32-make -j2 ARCH_OVERRIDE=${PLATFORM} fetch-dlls
|
||||||
mingw32-make -j2 ARCH_OVERRIDE=${PLATFORM} P2PD_CACHE=p2pdCache LOG_LEVEL=TRACE
|
mingw32-make -j2 ARCH_OVERRIDE=${PLATFORM} LOG_LEVEL=TRACE
|
||||||
mingw32-make -j2 ARCH_OVERRIDE=${PLATFORM} P2PD_CACHE=p2pdCache LOG_LEVEL=TRACE NIMFLAGS="-d:NETWORK_TYPE=libp2p -d:testnet_servers_image"
|
mingw32-make -j2 ARCH_OVERRIDE=${PLATFORM} LOG_LEVEL=TRACE NIMFLAGS="-d:testnet_servers_image"
|
||||||
file build/beacon_node
|
file build/beacon_node
|
||||||
|
# fail fast
|
||||||
|
export NIMTEST_ABORT_ON_ERROR=1
|
||||||
mingw32-make -j2 ARCH_OVERRIDE=${PLATFORM} DISABLE_TEST_FIXTURES_SCRIPT=1 test
|
mingw32-make -j2 ARCH_OVERRIDE=${PLATFORM} DISABLE_TEST_FIXTURES_SCRIPT=1 test
|
||||||
displayName: 'build and test'
|
displayName: 'build and test'
|
||||||
|
|
|
@ -5,20 +5,12 @@
|
||||||
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
|
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
|
||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
# Have an an aggregated aggregation ready for broadcast at
|
|
||||||
# SECONDS_PER_SLOT * 2 / 3, i.e. 2/3 through relevant slot
|
|
||||||
# intervals.
|
|
||||||
#
|
|
||||||
# The other part is arguably part of attestation pool -- the validation's
|
# The other part is arguably part of attestation pool -- the validation's
|
||||||
# something that should be happing on receipt, not aggregation per se. In
|
# something that should be happing on receipt, not aggregation per se. In
|
||||||
# that part, check that messages conform -- so, check for each type
|
# that part, check that messages conform -- so, check for each type
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/p2p-interface.md#topics-and-messages
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/p2p-interface.md#topics-and-messages
|
||||||
# specifies. So by the time this calls attestation pool, all validation's
|
# specifies. So by the time this calls attestation pool, all validation's
|
||||||
# already done.
|
# already done.
|
||||||
#
|
|
||||||
# Finally, some of the filtering's libp2p stuff. Consistency checks between
|
|
||||||
# topic/message types and GOSSIP_MAX_SIZE -- mostly doesn't belong here, so
|
|
||||||
# while TODO, isn't TODO for this module.
|
|
||||||
|
|
||||||
import
|
import
|
||||||
options,
|
options,
|
||||||
|
@ -26,15 +18,7 @@ import
|
||||||
state_transition_block],
|
state_transition_block],
|
||||||
./attestation_pool, ./beacon_node_types, ./ssz
|
./attestation_pool, ./beacon_node_types, ./ssz
|
||||||
|
|
||||||
# TODO gossipsub validation lives somewhere, maybe here
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#aggregation-selection
|
||||||
# TODO add tests, especially for validation
|
|
||||||
# https://github.com/status-im/nim-beacon-chain/issues/122#issuecomment-562479965
|
|
||||||
|
|
||||||
const
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/p2p-interface.md#configuration
|
|
||||||
ATTESTATION_PROPAGATION_SLOT_RANGE = 32
|
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/validator.md#aggregation-selection
|
|
||||||
func is_aggregator(state: BeaconState, slot: Slot, index: uint64,
|
func is_aggregator(state: BeaconState, slot: Slot, index: uint64,
|
||||||
slot_signature: ValidatorSig): bool =
|
slot_signature: ValidatorSig): bool =
|
||||||
# TODO index is a CommitteeIndex, aka uint64
|
# TODO index is a CommitteeIndex, aka uint64
|
||||||
|
@ -47,34 +31,49 @@ func is_aggregator(state: BeaconState, slot: Slot, index: uint64,
|
||||||
|
|
||||||
proc aggregate_attestations*(
|
proc aggregate_attestations*(
|
||||||
pool: AttestationPool, state: BeaconState, index: uint64,
|
pool: AttestationPool, state: BeaconState, index: uint64,
|
||||||
privkey: ValidatorPrivKey): Option[AggregateAndProof] =
|
privkey: ValidatorPrivKey, trailing_distance: uint64): Option[AggregateAndProof] =
|
||||||
# TODO alias CommitteeIndex to actual type then convert various uint64's here
|
# TODO alias CommitteeIndex to actual type then convert various uint64's here
|
||||||
|
|
||||||
let
|
doAssert state.slot >= trailing_distance
|
||||||
slot = state.slot - 2
|
|
||||||
slot_signature = get_slot_signature(state.fork, slot, privkey)
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/p2p-interface.md#configuration
|
||||||
|
doAssert trailing_distance <= ATTESTATION_PROPAGATION_SLOT_RANGE
|
||||||
|
|
||||||
|
let
|
||||||
|
slot = state.slot - trailing_distance
|
||||||
|
slot_signature = get_slot_signature(
|
||||||
|
state.fork, state.genesis_validators_root, slot, privkey)
|
||||||
|
|
||||||
if slot < 0:
|
|
||||||
return none(AggregateAndProof)
|
|
||||||
doAssert slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= state.slot
|
doAssert slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= state.slot
|
||||||
doAssert state.slot >= slot
|
doAssert state.slot >= slot
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/validator.md#aggregation-selection
|
# TODO performance issue for future, via get_active_validator_indices(...)
|
||||||
|
doAssert index < get_committee_count_at_slot(state, slot)
|
||||||
|
|
||||||
|
# TODO for testing purposes, refactor this into the condition check
|
||||||
|
# and just calculation
|
||||||
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#aggregation-selection
|
||||||
if not is_aggregator(state, slot, index, slot_signature):
|
if not is_aggregator(state, slot, index, slot_signature):
|
||||||
return none(AggregateAndProof)
|
return none(AggregateAndProof)
|
||||||
|
|
||||||
let attestation_data =
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#attestation-data
|
||||||
makeAttestationData(state, slot, index, get_block_root_at_slot(state, slot))
|
# describes how to construct an attestation, which applies for makeAttestationData(...)
|
||||||
|
# TODO this won't actually match anything
|
||||||
|
let attestation_data = AttestationData(
|
||||||
|
slot: slot,
|
||||||
|
index: index,
|
||||||
|
beacon_block_root: get_block_root_at_slot(state, slot))
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/validator.md#construct-aggregate
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#construct-aggregate
|
||||||
for attestation in getAttestationsForBlock(pool, state, slot):
|
# TODO once EV goes in w/ refactoring of getAttestationsForBlock, pull out the getSlot version and use
|
||||||
|
# it. This is incorrect.
|
||||||
|
for attestation in getAttestationsForBlock(pool, state):
|
||||||
|
# getAttestationsForBlock(...) already aggregates
|
||||||
if attestation.data == attestation_data:
|
if attestation.data == attestation_data:
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/validator.md#aggregateandproof
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#aggregateandproof
|
||||||
return some(AggregateAndProof(
|
return some(AggregateAndProof(
|
||||||
aggregator_index: index,
|
aggregator_index: index,
|
||||||
aggregate: attestation,
|
aggregate: attestation,
|
||||||
selection_proof: slot_signature))
|
selection_proof: slot_signature))
|
||||||
|
|
||||||
# TODO in catch-up mode, we could get here, so probably shouldn't assert
|
|
||||||
doAssert false
|
|
||||||
none(AggregateAndProof)
|
none(AggregateAndProof)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import
|
import
|
||||||
deques, sequtils, tables,
|
deques, sequtils, tables, options,
|
||||||
chronicles, stew/[bitseqs, byteutils], json_serialization/std/sets,
|
chronicles, stew/[bitseqs, byteutils], json_serialization/std/sets,
|
||||||
./spec/[beaconstate, datatypes, crypto, digest, helpers, validator],
|
./spec/[beaconstate, datatypes, crypto, digest, helpers, validator],
|
||||||
./extras, ./ssz, ./block_pool, ./beacon_node_types
|
./extras, ./ssz, ./block_pool, ./beacon_node_types
|
||||||
|
@ -35,6 +35,7 @@ proc combine*(tgt: var Attestation, src: Attestation, flags: UpdateFlags) =
|
||||||
else:
|
else:
|
||||||
trace "Ignoring overlapping attestations"
|
trace "Ignoring overlapping attestations"
|
||||||
|
|
||||||
|
# TODO remove/merge with p2p-interface validation
|
||||||
proc validate(
|
proc validate(
|
||||||
state: BeaconState, attestation: Attestation): bool =
|
state: BeaconState, attestation: Attestation): bool =
|
||||||
# TODO what constitutes a valid attestation when it's about to be added to
|
# TODO what constitutes a valid attestation when it's about to be added to
|
||||||
|
@ -265,26 +266,20 @@ proc add*(pool: var AttestationPool, attestation: Attestation) =
|
||||||
|
|
||||||
pool.addResolved(blck, attestation)
|
pool.addResolved(blck, attestation)
|
||||||
|
|
||||||
proc getAttestationsForBlock*(
|
proc getAttestationsForSlot(pool: AttestationPool, newBlockSlot: Slot):
|
||||||
pool: AttestationPool, state: BeaconState): seq[Attestation] =
|
Option[SlotData] =
|
||||||
## Retrieve attestations that may be added to a new block at the slot of the
|
|
||||||
## given state
|
|
||||||
logScope: pcs = "retrieve_attestation"
|
|
||||||
|
|
||||||
let newBlockSlot = state.slot
|
|
||||||
if newBlockSlot < (GENESIS_SLOT + MIN_ATTESTATION_INCLUSION_DELAY):
|
if newBlockSlot < (GENESIS_SLOT + MIN_ATTESTATION_INCLUSION_DELAY):
|
||||||
debug "Too early for attestations",
|
debug "Too early for attestations",
|
||||||
newBlockSlot = shortLog(newBlockSlot),
|
newBlockSlot = shortLog(newBlockSlot),
|
||||||
cat = "query"
|
cat = "query"
|
||||||
return
|
return none(SlotData)
|
||||||
|
|
||||||
if pool.slots.len == 0: # startingSlot not set yet!
|
if pool.slots.len == 0: # startingSlot not set yet!
|
||||||
info "No attestations found (pool empty)",
|
info "No attestations found (pool empty)",
|
||||||
newBlockSlot = shortLog(newBlockSlot),
|
newBlockSlot = shortLog(newBlockSlot),
|
||||||
cat = "query"
|
cat = "query"
|
||||||
return
|
return none(SlotData)
|
||||||
|
|
||||||
var cache = get_empty_per_epoch_cache()
|
|
||||||
let
|
let
|
||||||
# TODO in theory we could include attestations from other slots also, but
|
# TODO in theory we could include attestations from other slots also, but
|
||||||
# we're currently not tracking which attestations have already been included
|
# we're currently not tracking which attestations have already been included
|
||||||
|
@ -300,15 +295,32 @@ proc getAttestationsForBlock*(
|
||||||
startingSlot = shortLog(pool.startingSlot),
|
startingSlot = shortLog(pool.startingSlot),
|
||||||
endingSlot = shortLog(pool.startingSlot + pool.slots.len.uint64),
|
endingSlot = shortLog(pool.startingSlot + pool.slots.len.uint64),
|
||||||
cat = "query"
|
cat = "query"
|
||||||
return
|
return none(SlotData)
|
||||||
|
|
||||||
|
let slotDequeIdx = int(attestationSlot - pool.startingSlot)
|
||||||
|
some(pool.slots[slotDequeIdx])
|
||||||
|
|
||||||
|
proc getAttestationsForBlock*(
|
||||||
|
pool: AttestationPool, state: BeaconState): seq[Attestation] =
|
||||||
|
## Retrieve attestations that may be added to a new block at the slot of the
|
||||||
|
## given state
|
||||||
|
logScope: pcs = "retrieve_attestation"
|
||||||
|
|
||||||
|
# TODO this shouldn't really need state -- it's to recheck/validate, but that
|
||||||
|
# should be refactored
|
||||||
let
|
let
|
||||||
slotDequeIdx = int(attestationSlot - pool.startingSlot)
|
newBlockSlot = state.slot
|
||||||
slotData = pool.slots[slotDequeIdx]
|
maybeSlotData = getAttestationsForSlot(pool, newBlockSlot)
|
||||||
|
|
||||||
|
if maybeSlotData.isNone:
|
||||||
|
# Logging done in getAttestationsForSlot(...)
|
||||||
|
return
|
||||||
|
let slotData = maybeSlotData.get
|
||||||
|
|
||||||
|
var cache = get_empty_per_epoch_cache()
|
||||||
for a in slotData.attestations:
|
for a in slotData.attestations:
|
||||||
var
|
var
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/validator.md#construct-attestation
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#construct-attestation
|
||||||
attestation = Attestation(
|
attestation = Attestation(
|
||||||
aggregation_bits: a.validations[0].aggregation_bits,
|
aggregation_bits: a.validations[0].aggregation_bits,
|
||||||
data: a.data,
|
data: a.data,
|
||||||
|
@ -438,3 +450,80 @@ proc selectHead*(pool: AttestationPool): BlockRef =
|
||||||
lmdGhost(pool, pool.blockPool.justifiedState.data.data, justifiedHead.blck)
|
lmdGhost(pool, pool.blockPool.justifiedState.data.data, justifiedHead.blck)
|
||||||
|
|
||||||
newHead
|
newHead
|
||||||
|
|
||||||
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/p2p-interface.md#attestation-subnets
|
||||||
|
proc isValidAttestation*(
|
||||||
|
pool: AttestationPool, attestation: Attestation, current_slot: Slot,
|
||||||
|
topicCommitteeIndex: uint64, flags: UpdateFlags): bool =
|
||||||
|
# The attestation's committee index (attestation.data.index) is for the
|
||||||
|
# correct subnet.
|
||||||
|
if attestation.data.index != topicCommitteeIndex:
|
||||||
|
debug "isValidAttestation: attestation's committee index not for the correct subnet",
|
||||||
|
topicCommitteeIndex = topicCommitteeIndex,
|
||||||
|
attestation_data_index = attestation.data.index
|
||||||
|
return false
|
||||||
|
|
||||||
|
if not (attestation.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE >=
|
||||||
|
current_slot and current_slot >= attestation.data.slot):
|
||||||
|
debug "isValidAttestation: attestation.data.slot not within ATTESTATION_PROPAGATION_SLOT_RANGE"
|
||||||
|
return false
|
||||||
|
|
||||||
|
# The attestation is unaggregated -- that is, it has exactly one
|
||||||
|
# participating validator (len([bit for bit in attestation.aggregation_bits
|
||||||
|
# if bit == 0b1]) == 1).
|
||||||
|
# TODO a cleverer algorithm, along the lines of countOnes() in nim-stew
|
||||||
|
# But that belongs in nim-stew, since it'd break abstraction layers, to
|
||||||
|
# use details of its representation from nim-beacon-chain.
|
||||||
|
var onesCount = 0
|
||||||
|
for aggregation_bit in attestation.aggregation_bits:
|
||||||
|
if not aggregation_bit:
|
||||||
|
continue
|
||||||
|
onesCount += 1
|
||||||
|
if onesCount > 1:
|
||||||
|
debug "isValidAttestation: attestation has too many aggregation bits",
|
||||||
|
aggregation_bits = attestation.aggregation_bits
|
||||||
|
return false
|
||||||
|
if onesCount != 1:
|
||||||
|
debug "isValidAttestation: attestation has too few aggregation bits"
|
||||||
|
return false
|
||||||
|
|
||||||
|
# The attestation is the first valid attestation received for the
|
||||||
|
# participating validator for the slot, attestation.data.slot.
|
||||||
|
let maybeSlotData = getAttestationsForSlot(pool, attestation.data.slot)
|
||||||
|
if maybeSlotData.isSome:
|
||||||
|
for attestationEntry in maybeSlotData.get.attestations:
|
||||||
|
if attestation.data != attestationEntry.data:
|
||||||
|
continue
|
||||||
|
# Attestations might be aggregated eagerly or lazily; allow for both.
|
||||||
|
for validation in attestationEntry.validations:
|
||||||
|
if attestation.aggregation_bits.isSubsetOf(validation.aggregation_bits):
|
||||||
|
debug "isValidAttestation: attestation already exists at slot",
|
||||||
|
attestation_data_slot = attestation.data.slot,
|
||||||
|
attestation_aggregation_bits = attestation.aggregation_bits,
|
||||||
|
attestation_pool_validation = validation.aggregation_bits
|
||||||
|
return false
|
||||||
|
|
||||||
|
# The block being voted for (attestation.data.beacon_block_root) passes
|
||||||
|
# validation.
|
||||||
|
# We rely on the block pool to have been validated, so check for the
|
||||||
|
# existence of the block in the pool.
|
||||||
|
# TODO: consider a "slush pool" of attestations whose blocks have not yet
|
||||||
|
# propagated - i.e. imagine that attestations are smaller than blocks and
|
||||||
|
# therefore propagate faster, thus reordering their arrival in some nodes
|
||||||
|
if pool.blockPool.get(attestation.data.beacon_block_root).isNone():
|
||||||
|
debug "isValidAttestation: block doesn't exist in block pool",
|
||||||
|
attestation_data_beacon_block_root = attestation.data.beacon_block_root
|
||||||
|
return false
|
||||||
|
|
||||||
|
# The signature of attestation is valid.
|
||||||
|
# TODO need to know above which validator anyway, and this is too general
|
||||||
|
# as it supports aggregated attestations (which this can't be)
|
||||||
|
var cache = get_empty_per_epoch_cache()
|
||||||
|
if not is_valid_indexed_attestation(
|
||||||
|
pool.blockPool.headState.data.data,
|
||||||
|
get_indexed_attestation(
|
||||||
|
pool.blockPool.headState.data.data, attestation, cache), {}):
|
||||||
|
debug "isValidAttestation: signature verification failed"
|
||||||
|
return false
|
||||||
|
|
||||||
|
true
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import
|
import
|
||||||
# Standard library
|
# Standard library
|
||||||
os, tables, random, strutils, times, sequtils,
|
os, tables, random, strutils, times,
|
||||||
|
|
||||||
# Nimble packages
|
# Nimble packages
|
||||||
stew/[objects, bitseqs, byteutils], stew/shims/macros,
|
stew/[objects, bitseqs, byteutils], stew/shims/macros,
|
||||||
|
@ -12,10 +12,11 @@ import
|
||||||
# Local modules
|
# Local modules
|
||||||
spec/[datatypes, digest, crypto, beaconstate, helpers, validator, network,
|
spec/[datatypes, digest, crypto, beaconstate, helpers, validator, network,
|
||||||
state_transition_block], spec/presets/custom,
|
state_transition_block], spec/presets/custom,
|
||||||
conf, time, state_transition, beacon_chain_db, validator_pool, extras,
|
conf, time, beacon_chain_db, validator_pool, extras,
|
||||||
attestation_pool, block_pool, eth2_network, eth2_discovery,
|
attestation_pool, block_pool, eth2_network, eth2_discovery,
|
||||||
beacon_node_types, mainchain_monitor, version, ssz, ssz/dynamic_navigator,
|
beacon_node_types, mainchain_monitor, version, ssz, ssz/dynamic_navigator,
|
||||||
sync_protocol, request_manager, validator_keygen, interop, statusbar
|
sync_protocol, request_manager, validator_keygen, interop, statusbar,
|
||||||
|
attestation_aggregation
|
||||||
|
|
||||||
const
|
const
|
||||||
genesisFile = "genesis.ssz"
|
genesisFile = "genesis.ssz"
|
||||||
|
@ -56,8 +57,6 @@ type
|
||||||
forkVersion: array[4, byte]
|
forkVersion: array[4, byte]
|
||||||
netKeys: KeyPair
|
netKeys: KeyPair
|
||||||
requestManager: RequestManager
|
requestManager: RequestManager
|
||||||
bootstrapNodes: seq[ENode]
|
|
||||||
bootstrapEnrs: seq[enr.Record]
|
|
||||||
db: BeaconChainDB
|
db: BeaconChainDB
|
||||||
config: BeaconNodeConf
|
config: BeaconNodeConf
|
||||||
attachedValidators: ValidatorPool
|
attachedValidators: ValidatorPool
|
||||||
|
@ -134,7 +133,6 @@ proc getStateFromSnapshot(conf: BeaconNodeConf, state: var BeaconState): bool =
|
||||||
proc init*(T: type BeaconNode, conf: BeaconNodeConf): Future[BeaconNode] {.async.} =
|
proc init*(T: type BeaconNode, conf: BeaconNodeConf): Future[BeaconNode] {.async.} =
|
||||||
let
|
let
|
||||||
netKeys = getPersistentNetKeys(conf)
|
netKeys = getPersistentNetKeys(conf)
|
||||||
ourPubKey = netKeys.pubkey.skkey
|
|
||||||
nickname = if conf.nodeName == "auto": shortForm(netKeys)
|
nickname = if conf.nodeName == "auto": shortForm(netKeys)
|
||||||
else: conf.nodeName
|
else: conf.nodeName
|
||||||
db = BeaconChainDB.init(kvStore SqliteStoreRef.init(conf.databaseDir))
|
db = BeaconChainDB.init(kvStore SqliteStoreRef.init(conf.databaseDir))
|
||||||
|
@ -188,25 +186,7 @@ proc init*(T: type BeaconNode, conf: BeaconNodeConf): Future[BeaconNode] {.async
|
||||||
# monitor
|
# monitor
|
||||||
mainchainMonitor.start()
|
mainchainMonitor.start()
|
||||||
|
|
||||||
var bootNodes: seq[ENode]
|
let network = await createEth2Node(conf)
|
||||||
var bootEnrs: seq[enr.Record]
|
|
||||||
for node in conf.bootstrapNodes: addBootstrapNode(node, bootNodes, bootEnrs, ourPubKey)
|
|
||||||
loadBootstrapFile(string conf.bootstrapNodesFile, bootNodes, bootEnrs, ourPubKey)
|
|
||||||
|
|
||||||
when networkBackend == libp2pDaemon:
|
|
||||||
for enr in bootEnrs:
|
|
||||||
let enode = toENode(enr)
|
|
||||||
if enode.isOk:
|
|
||||||
bootNodes.add enode.value
|
|
||||||
|
|
||||||
let persistentBootstrapFile = conf.dataDir / "bootstrap_nodes.txt"
|
|
||||||
if fileExists(persistentBootstrapFile):
|
|
||||||
loadBootstrapFile(persistentBootstrapFile, bootNodes, bootEnrs, ourPubKey)
|
|
||||||
|
|
||||||
let
|
|
||||||
network = await createEth2Node(conf, bootNodes)
|
|
||||||
addressFile = string(conf.dataDir) / "beacon_node.address"
|
|
||||||
network.saveConnectionAddressFile(addressFile)
|
|
||||||
|
|
||||||
let rpcServer = if conf.rpcEnabled:
|
let rpcServer = if conf.rpcEnabled:
|
||||||
RpcServer.init(conf.rpcAddress, conf.rpcPort)
|
RpcServer.init(conf.rpcAddress, conf.rpcPort)
|
||||||
|
@ -219,8 +199,6 @@ proc init*(T: type BeaconNode, conf: BeaconNodeConf): Future[BeaconNode] {.async
|
||||||
forkVersion: blockPool.headState.data.data.fork.current_version,
|
forkVersion: blockPool.headState.data.data.fork.current_version,
|
||||||
netKeys: netKeys,
|
netKeys: netKeys,
|
||||||
requestManager: RequestManager.init(network),
|
requestManager: RequestManager.init(network),
|
||||||
bootstrapNodes: bootNodes,
|
|
||||||
bootstrapEnrs: bootEnrs,
|
|
||||||
db: db,
|
db: db,
|
||||||
config: conf,
|
config: conf,
|
||||||
attachedValidators: ValidatorPool.init(),
|
attachedValidators: ValidatorPool.init(),
|
||||||
|
@ -258,13 +236,10 @@ proc init*(T: type BeaconNode, conf: BeaconNodeConf): Future[BeaconNode] {.async
|
||||||
return res
|
return res
|
||||||
|
|
||||||
proc connectToNetwork(node: BeaconNode) {.async.} =
|
proc connectToNetwork(node: BeaconNode) {.async.} =
|
||||||
if node.bootstrapNodes.len > 0:
|
await node.network.connectToNetwork()
|
||||||
info "Connecting to bootstrap nodes", bootstrapNodes = node.bootstrapNodes
|
|
||||||
else:
|
|
||||||
info "Waiting for connections"
|
|
||||||
|
|
||||||
await node.network.connectToNetwork(node.bootstrapNodes,
|
let addressFile = node.config.dataDir / "beacon_node.address"
|
||||||
node.bootstrapEnrs)
|
writeFile(addressFile, node.network.announcedENR.toURI)
|
||||||
|
|
||||||
template findIt(s: openarray, predicate: untyped): int =
|
template findIt(s: openarray, predicate: untyped): int =
|
||||||
var res = -1
|
var res = -1
|
||||||
|
@ -341,14 +316,15 @@ proc updateHead(node: BeaconNode): BlockRef =
|
||||||
|
|
||||||
proc sendAttestation(node: BeaconNode,
|
proc sendAttestation(node: BeaconNode,
|
||||||
fork: Fork,
|
fork: Fork,
|
||||||
|
genesis_validators_root: Eth2Digest,
|
||||||
validator: AttachedValidator,
|
validator: AttachedValidator,
|
||||||
attestationData: AttestationData,
|
attestationData: AttestationData,
|
||||||
committeeLen: int,
|
committeeLen: int,
|
||||||
indexInCommittee: int) {.async.} =
|
indexInCommittee: int) {.async.} =
|
||||||
logScope: pcs = "send_attestation"
|
logScope: pcs = "send_attestation"
|
||||||
|
|
||||||
let
|
let validatorSignature = await validator.signAttestation(attestationData,
|
||||||
validatorSignature = await validator.signAttestation(attestationData, fork)
|
fork, genesis_validators_root)
|
||||||
|
|
||||||
var aggregationBits = CommitteeValidatorsBits.init(committeeLen)
|
var aggregationBits = CommitteeValidatorsBits.init(committeeLen)
|
||||||
aggregationBits.setBit indexInCommittee
|
aggregationBits.setBit indexInCommittee
|
||||||
|
@ -359,7 +335,7 @@ proc sendAttestation(node: BeaconNode,
|
||||||
aggregation_bits: aggregationBits
|
aggregation_bits: aggregationBits
|
||||||
)
|
)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/validator.md#broadcast-attestation
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#broadcast-attestation
|
||||||
node.network.broadcast(
|
node.network.broadcast(
|
||||||
getAttestationTopic(attestationData.index), attestation)
|
getAttestationTopic(attestationData.index), attestation)
|
||||||
|
|
||||||
|
@ -409,7 +385,7 @@ proc proposeBlock(node: BeaconNode,
|
||||||
let message = makeBeaconBlock(
|
let message = makeBeaconBlock(
|
||||||
state,
|
state,
|
||||||
head.root,
|
head.root,
|
||||||
validator.genRandaoReveal(state.fork, slot),
|
validator.genRandaoReveal(state.fork, state.genesis_validators_root, slot),
|
||||||
eth1data,
|
eth1data,
|
||||||
Eth2Digest(),
|
Eth2Digest(),
|
||||||
node.attestationPool.getAttestationsForBlock(state),
|
node.attestationPool.getAttestationsForBlock(state),
|
||||||
|
@ -425,8 +401,8 @@ proc proposeBlock(node: BeaconNode,
|
||||||
let blockRoot = hash_tree_root(newBlock.message)
|
let blockRoot = hash_tree_root(newBlock.message)
|
||||||
|
|
||||||
# Careful, state no longer valid after here because of the await..
|
# Careful, state no longer valid after here because of the await..
|
||||||
newBlock.signature =
|
newBlock.signature = await validator.signBlockProposal(
|
||||||
await validator.signBlockProposal(state.fork, slot, blockRoot)
|
state.fork, state.genesis_validators_root, slot, blockRoot)
|
||||||
|
|
||||||
(blockRoot, newBlock)
|
(blockRoot, newBlock)
|
||||||
|
|
||||||
|
@ -581,7 +557,8 @@ proc handleAttestations(node: BeaconNode, head: BlockRef, slot: Slot) =
|
||||||
|
|
||||||
for a in attestations:
|
for a in attestations:
|
||||||
traceAsyncErrors sendAttestation(
|
traceAsyncErrors sendAttestation(
|
||||||
node, state.fork, a.validator, a.data, a.committeeLen, a.indexInCommittee)
|
node, state.fork, state.genesis_validators_root, a.validator, a.data,
|
||||||
|
a.committeeLen, a.indexInCommittee)
|
||||||
|
|
||||||
proc handleProposal(node: BeaconNode, head: BlockRef, slot: Slot):
|
proc handleProposal(node: BeaconNode, head: BlockRef, slot: Slot):
|
||||||
Future[BlockRef] {.async.} =
|
Future[BlockRef] {.async.} =
|
||||||
|
@ -625,6 +602,45 @@ proc verifyFinalization(node: BeaconNode, slot: Slot) =
|
||||||
node.blockPool.finalizedHead.blck.slot.compute_epoch_at_slot()
|
node.blockPool.finalizedHead.blck.slot.compute_epoch_at_slot()
|
||||||
doAssert finalizedEpoch + 2 == epoch
|
doAssert finalizedEpoch + 2 == epoch
|
||||||
|
|
||||||
|
proc broadcastAggregatedAttestations(
|
||||||
|
node: BeaconNode, state: auto, head: var auto, slot: Slot,
|
||||||
|
trailing_distance: uint64) =
|
||||||
|
# The index is via a
|
||||||
|
# locally attested validator. Unlike in handleAttestations(...) there's a
|
||||||
|
# single one at most per slot (because that's how aggregation attestation
|
||||||
|
# works), so the machinery that has to handle looping across, basically a
|
||||||
|
# set of locally attached validators is in principle not necessary, but a
|
||||||
|
# way to organize this. Then the private key for that validator should be
|
||||||
|
# the corresponding one -- whatver they are, they match.
|
||||||
|
|
||||||
|
let
|
||||||
|
bs = BlockSlot(blck: head, slot: slot)
|
||||||
|
committees_per_slot = get_committee_count_at_slot(state, slot)
|
||||||
|
var cache = get_empty_per_epoch_cache()
|
||||||
|
for committee_index in 0'u64..<committees_per_slot:
|
||||||
|
let
|
||||||
|
committee = get_beacon_committee(state, slot, committee_index, cache)
|
||||||
|
|
||||||
|
for index_in_committee, validatorIdx in committee:
|
||||||
|
let validator = node.getAttachedValidator(state, validatorIdx)
|
||||||
|
if validator != nil:
|
||||||
|
# This is slightly strange/inverted control flow, since really it's
|
||||||
|
# going to happen once per slot, but this is the best way to get at
|
||||||
|
# the validator index and private key pair. TODO verify it only has
|
||||||
|
# one isSome() with test.
|
||||||
|
let option_aggregateandproof =
|
||||||
|
aggregate_attestations(node.attestationPool, state,
|
||||||
|
committee_index,
|
||||||
|
# TODO https://github.com/status-im/nim-beacon-chain/issues/545
|
||||||
|
# this assumes in-process private keys
|
||||||
|
validator.privKey,
|
||||||
|
trailing_distance)
|
||||||
|
|
||||||
|
# Don't broadcast when, e.g., this node isn't an aggregator
|
||||||
|
if option_aggregateandproof.isSome:
|
||||||
|
node.network.broadcast(
|
||||||
|
topicAggregateAndProof, option_aggregateandproof.get)
|
||||||
|
|
||||||
proc onSlotStart(node: BeaconNode, lastSlot, scheduledSlot: Slot) {.gcsafe, async.} =
|
proc onSlotStart(node: BeaconNode, lastSlot, scheduledSlot: Slot) {.gcsafe, async.} =
|
||||||
## Called at the beginning of a slot - usually every slot, but sometimes might
|
## Called at the beginning of a slot - usually every slot, but sometimes might
|
||||||
## skip a few in case we're running late.
|
## skip a few in case we're running late.
|
||||||
|
@ -720,7 +736,7 @@ proc onSlotStart(node: BeaconNode, lastSlot, scheduledSlot: Slot) {.gcsafe, asyn
|
||||||
|
|
||||||
var head = node.updateHead()
|
var head = node.updateHead()
|
||||||
|
|
||||||
# TODO is the slot of the clock or the head block more interestion? provide
|
# TODO is the slot of the clock or the head block more interesting? provide
|
||||||
# rationale in comment
|
# rationale in comment
|
||||||
beacon_head_slot.set slot.int64
|
beacon_head_slot.set slot.int64
|
||||||
|
|
||||||
|
@ -785,11 +801,12 @@ proc onSlotStart(node: BeaconNode, lastSlot, scheduledSlot: Slot) {.gcsafe, asyn
|
||||||
# with any clock discrepancies once only, at the start of slot timer
|
# with any clock discrepancies once only, at the start of slot timer
|
||||||
# processing..
|
# processing..
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.4/specs/validator/0_beacon-chain-validator.md#attesting
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#attesting
|
||||||
# A validator should create and broadcast the attestation to the
|
# A validator should create and broadcast the attestation to the associated
|
||||||
# associated attestation subnet one-third of the way through the slot
|
# attestation subnet when either (a) the validator has received a valid
|
||||||
# during which the validator is assigned―that is, SECONDS_PER_SLOT / 3
|
# block from the expected block proposer for the assigned slot or
|
||||||
# seconds after the start of slot.
|
# (b) one-third of the slot has transpired (`SECONDS_PER_SLOT / 3` seconds
|
||||||
|
# after the start of slot) -- whichever comes first.
|
||||||
let
|
let
|
||||||
attestationStart = node.beaconClock.fromNow(slot)
|
attestationStart = node.beaconClock.fromNow(slot)
|
||||||
thirdSlot = seconds(int64(SECONDS_PER_SLOT)) div 3
|
thirdSlot = seconds(int64(SECONDS_PER_SLOT)) div 3
|
||||||
|
@ -811,6 +828,25 @@ proc onSlotStart(node: BeaconNode, lastSlot, scheduledSlot: Slot) {.gcsafe, asyn
|
||||||
|
|
||||||
handleAttestations(node, head, slot)
|
handleAttestations(node, head, slot)
|
||||||
|
|
||||||
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#broadcast-aggregate
|
||||||
|
# If the validator is selected to aggregate (is_aggregator), then they
|
||||||
|
# broadcast their best aggregate as a SignedAggregateAndProof to the global
|
||||||
|
# aggregate channel (beacon_aggregate_and_proof) two-thirds of the way
|
||||||
|
# through the slot-that is, SECONDS_PER_SLOT * 2 / 3 seconds after the start
|
||||||
|
# of slot.
|
||||||
|
if slot > 2:
|
||||||
|
const TRAILING_DISTANCE = 1
|
||||||
|
let aggregationSlot = slot - TRAILING_DISTANCE
|
||||||
|
var aggregationHead = getAncestorAt(head, aggregationSlot)
|
||||||
|
|
||||||
|
let bs = BlockSlot(blck: aggregationHead, slot: aggregationSlot)
|
||||||
|
node.blockPool.withState(node.blockPool.tmpState, bs):
|
||||||
|
let twoThirdsSlot =
|
||||||
|
toBeaconTime(slot, seconds(2*int64(SECONDS_PER_SLOT)) div 3)
|
||||||
|
addTimer(saturate(node.beaconClock.fromNow(twoThirdsSlot))) do (p: pointer):
|
||||||
|
broadcastAggregatedAttestations(
|
||||||
|
node, state, aggregationHead, aggregationSlot, TRAILING_DISTANCE)
|
||||||
|
|
||||||
# TODO ... and beacon clock might jump here also. sigh.
|
# TODO ... and beacon clock might jump here also. sigh.
|
||||||
let
|
let
|
||||||
nextSlotStart = saturate(node.beaconClock.fromNow(nextSlot))
|
nextSlotStart = saturate(node.beaconClock.fromNow(nextSlot))
|
||||||
|
@ -924,20 +960,11 @@ proc installBeaconApiHandlers(rpcServer: RpcServer, node: BeaconNode) =
|
||||||
return StringOfJson("null")
|
return StringOfJson("null")
|
||||||
|
|
||||||
rpcServer.rpc("getNetworkPeerId") do () -> string:
|
rpcServer.rpc("getNetworkPeerId") do () -> string:
|
||||||
when networkBackend != libp2p:
|
return $publicKey(node.network)
|
||||||
if true:
|
|
||||||
raise newException(CatchableError, "Unsupported operation")
|
|
||||||
return ""
|
|
||||||
else:
|
|
||||||
return $publicKey(node.network)
|
|
||||||
|
|
||||||
rpcServer.rpc("getNetworkPeers") do () -> seq[string]:
|
rpcServer.rpc("getNetworkPeers") do () -> seq[string]:
|
||||||
when networkBackend != libp2p:
|
for peerId, peer in node.network.peerPool:
|
||||||
if true:
|
result.add $peerId
|
||||||
raise newException(CatchableError, "Unsupported operation")
|
|
||||||
else:
|
|
||||||
for peerId, peer in node.network.peerPool:
|
|
||||||
result.add $peerId
|
|
||||||
|
|
||||||
rpcServer.rpc("getNetworkEnr") do () -> string:
|
rpcServer.rpc("getNetworkEnr") do () -> string:
|
||||||
when networkBackend == libp2p:
|
when networkBackend == libp2p:
|
||||||
|
@ -970,14 +997,31 @@ proc run*(node: BeaconNode) =
|
||||||
|
|
||||||
waitFor node.network.subscribe(topicBeaconBlocks) do (signedBlock: SignedBeaconBlock):
|
waitFor node.network.subscribe(topicBeaconBlocks) do (signedBlock: SignedBeaconBlock):
|
||||||
onBeaconBlock(node, signedBlock)
|
onBeaconBlock(node, signedBlock)
|
||||||
|
do (signedBlock: SignedBeaconBlock) -> bool:
|
||||||
|
let (afterGenesis, slot) = node.beaconClock.now.toSlot()
|
||||||
|
if not afterGenesis:
|
||||||
|
return false
|
||||||
|
node.blockPool.isValidBeaconBlock(signedBlock, slot, {})
|
||||||
|
|
||||||
waitFor allFutures(mapIt(
|
proc attestationHandler(attestation: Attestation) =
|
||||||
0'u64 ..< ATTESTATION_SUBNET_COUNT.uint64,
|
# Avoid double-counting attestation-topic attestations on shared codepath
|
||||||
node.network.subscribe(getAttestationTopic(it)) do (attestation: Attestation):
|
# when they're reflected through beacon blocks
|
||||||
# Avoid double-counting attestation-topic attestations on shared codepath
|
beacon_attestations_received.inc()
|
||||||
# when they're reflected through beacon blocks
|
node.onAttestation(attestation)
|
||||||
beacon_attestations_received.inc()
|
|
||||||
node.onAttestation(attestation)))
|
var attestationSubscriptions: seq[Future[void]] = @[]
|
||||||
|
for it in 0'u64 ..< ATTESTATION_SUBNET_COUNT.uint64:
|
||||||
|
closureScope:
|
||||||
|
let ci = it
|
||||||
|
attestationSubscriptions.add(node.network.subscribe(
|
||||||
|
getAttestationTopic(ci), attestationHandler,
|
||||||
|
proc(attestation: Attestation): bool =
|
||||||
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/p2p-interface.md#attestation-subnets
|
||||||
|
let (afterGenesis, slot) = node.beaconClock.now().toSlot()
|
||||||
|
if not afterGenesis:
|
||||||
|
return false
|
||||||
|
node.attestationPool.isValidAttestation(attestation, slot, ci, {})))
|
||||||
|
waitFor allFutures(attestationSubscriptions)
|
||||||
|
|
||||||
let
|
let
|
||||||
t = node.beaconClock.now().toSlot()
|
t = node.beaconClock.now().toSlot()
|
||||||
|
@ -1172,10 +1216,6 @@ when isMainModule:
|
||||||
proc (logLevel: LogLevel, msg: LogOutputStr) {.gcsafe.} =
|
proc (logLevel: LogLevel, msg: LogOutputStr) {.gcsafe.} =
|
||||||
stdout.write(msg)
|
stdout.write(msg)
|
||||||
|
|
||||||
debug "Launching beacon node",
|
|
||||||
version = fullVersionStr,
|
|
||||||
cmdParams = commandLineParams(), config
|
|
||||||
|
|
||||||
randomize()
|
randomize()
|
||||||
|
|
||||||
if config.logLevel != LogLevel.NONE:
|
if config.logLevel != LogLevel.NONE:
|
||||||
|
@ -1231,15 +1271,12 @@ when isMainModule:
|
||||||
if bootstrapFile.len > 0:
|
if bootstrapFile.len > 0:
|
||||||
let
|
let
|
||||||
networkKeys = getPersistentNetKeys(config)
|
networkKeys = getPersistentNetKeys(config)
|
||||||
bootstrapAddress = enode.Address(
|
|
||||||
ip: config.bootstrapAddress,
|
|
||||||
tcpPort: config.bootstrapPort,
|
|
||||||
udpPort: config.bootstrapPort)
|
|
||||||
|
|
||||||
bootstrapEnr = enr.Record.init(
|
bootstrapEnr = enr.Record.init(
|
||||||
1, # sequence number
|
1, # sequence number
|
||||||
networkKeys.seckey.asEthKey,
|
networkKeys.seckey.asEthKey,
|
||||||
bootstrapAddress)
|
some(config.bootstrapAddress),
|
||||||
|
config.bootstrapPort,
|
||||||
|
config.bootstrapPort)
|
||||||
|
|
||||||
writeFile(bootstrapFile, bootstrapEnr.toURI)
|
writeFile(bootstrapFile, bootstrapEnr.toURI)
|
||||||
echo "Wrote ", bootstrapFile
|
echo "Wrote ", bootstrapFile
|
||||||
|
@ -1261,6 +1298,11 @@ when isMainModule:
|
||||||
reportFailureFor keyFile.string
|
reportFailureFor keyFile.string
|
||||||
|
|
||||||
of noCommand:
|
of noCommand:
|
||||||
|
debug "Launching beacon node",
|
||||||
|
version = fullVersionStr,
|
||||||
|
cmdParams = commandLineParams(),
|
||||||
|
config
|
||||||
|
|
||||||
createPidFile(config.dataDir.string / "beacon_node.pid")
|
createPidFile(config.dataDir.string / "beacon_node.pid")
|
||||||
|
|
||||||
var node = waitFor BeaconNode.init(config)
|
var node = waitFor BeaconNode.init(config)
|
||||||
|
|
|
@ -136,7 +136,10 @@ type
|
||||||
|
|
||||||
inAdd*: bool
|
inAdd*: bool
|
||||||
|
|
||||||
headState*: StateData ## State given by the head block
|
headState*: StateData ## \
|
||||||
|
## State given by the head block; only update in `updateHead`, not anywhere
|
||||||
|
## else via `withState`
|
||||||
|
|
||||||
justifiedState*: StateData ## Latest justified state, as seen from the head
|
justifiedState*: StateData ## Latest justified state, as seen from the head
|
||||||
|
|
||||||
tmpState*: StateData ## Scratchpad - may be any state
|
tmpState*: StateData ## Scratchpad - may be any state
|
||||||
|
|
|
@ -943,7 +943,7 @@ proc getProposer*(pool: BlockPool, head: BlockRef, slot: Slot): Option[Validator
|
||||||
pool.withState(pool.tmpState, head.atSlot(slot)):
|
pool.withState(pool.tmpState, head.atSlot(slot)):
|
||||||
var cache = get_empty_per_epoch_cache()
|
var cache = get_empty_per_epoch_cache()
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/validator.md#validator-assignments
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#validator-assignments
|
||||||
let proposerIdx = get_beacon_proposer_index(state, cache)
|
let proposerIdx = get_beacon_proposer_index(state, cache)
|
||||||
if proposerIdx.isNone:
|
if proposerIdx.isNone:
|
||||||
warn "Missing proposer index",
|
warn "Missing proposer index",
|
||||||
|
@ -956,3 +956,131 @@ proc getProposer*(pool: BlockPool, head: BlockRef, slot: Slot): Option[Validator
|
||||||
return
|
return
|
||||||
|
|
||||||
return some(state.validators[proposerIdx.get()].pubkey)
|
return some(state.validators[proposerIdx.get()].pubkey)
|
||||||
|
|
||||||
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/p2p-interface.md#global-topics
|
||||||
|
proc isValidBeaconBlock*(pool: BlockPool,
|
||||||
|
signed_beacon_block: SignedBeaconBlock, current_slot: Slot,
|
||||||
|
flags: UpdateFlags): bool =
|
||||||
|
# In general, checks are ordered from cheap to expensive. Especially, crypto
|
||||||
|
# verification could be quite a bit more expensive than the rest. This is an
|
||||||
|
# externally easy-to-invoke function by tossing network packets at the node.
|
||||||
|
|
||||||
|
# The block is not from a future slot
|
||||||
|
# TODO allow `MAXIMUM_GOSSIP_CLOCK_DISPARITY` leniency, especially towards
|
||||||
|
# seemingly future slots.
|
||||||
|
if not (signed_beacon_block.message.slot <= current_slot):
|
||||||
|
debug "isValidBeaconBlock: block is from a future slot",
|
||||||
|
signed_beacon_block_message_slot = signed_beacon_block.message.slot,
|
||||||
|
current_slot = current_slot
|
||||||
|
return false
|
||||||
|
|
||||||
|
# The block is from a slot greater than the latest finalized slot (with a
|
||||||
|
# MAXIMUM_GOSSIP_CLOCK_DISPARITY allowance) -- i.e. validate that
|
||||||
|
# signed_beacon_block.message.slot >
|
||||||
|
# compute_start_slot_at_epoch(state.finalized_checkpoint.epoch)
|
||||||
|
if not (signed_beacon_block.message.slot > pool.finalizedHead.slot):
|
||||||
|
debug "isValidBeaconBlock: block is not from a slot greater than the latest finalized slot"
|
||||||
|
return false
|
||||||
|
|
||||||
|
# The proposer signature, signed_beacon_block.signature, is valid with
|
||||||
|
# respect to the proposer_index pubkey.
|
||||||
|
|
||||||
|
# TODO resolve following two checks' robustness and remove this early exit.
|
||||||
|
const alwaysTrue = true
|
||||||
|
if alwaysTrue:
|
||||||
|
return true
|
||||||
|
|
||||||
|
# TODO because this check depends on the proposer aspect, and see the comment
|
||||||
|
# there for that issue, the fallout is this check isn't reliable anymore.
|
||||||
|
# The block is the first block with valid signature received for the proposer
|
||||||
|
# for the slot, signed_beacon_block.message.slot.
|
||||||
|
#
|
||||||
|
# While this condition is similar to the proposer slashing condition at
|
||||||
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#proposer-slashing
|
||||||
|
# it's not identical, and this check does not address slashing:
|
||||||
|
#
|
||||||
|
# (1) The beacon blocks must be conflicting, i.e. different, for the same
|
||||||
|
# slot and proposer. This check also catches identical blocks.
|
||||||
|
#
|
||||||
|
# (2) By this point in the function, it's not been checked whether they're
|
||||||
|
# signed yet. As in general, expensive checks should be deferred, this
|
||||||
|
# would add complexity not directly relevant this function.
|
||||||
|
#
|
||||||
|
# (3) As evidenced by point (1), the similarity in the validation condition
|
||||||
|
# and slashing condition, while not coincidental, aren't similar enough
|
||||||
|
# to combine, as one or the other might drift.
|
||||||
|
#
|
||||||
|
# (4) Furthermore, this function, as much as possible, simply returns a yes
|
||||||
|
# or no answer, without modifying other state for p2p network interface
|
||||||
|
# validation. Complicating this interface, for the sake of sharing only
|
||||||
|
# couple lines of code, wouldn't be worthwhile.
|
||||||
|
#
|
||||||
|
# TODO might check unresolved/orphaned blocks too, and this might not see all
|
||||||
|
# blocks at a given slot (though, in theory, those get checked elsewhere), or
|
||||||
|
# adding metrics that count how often these conditions occur.
|
||||||
|
let slotBlockRef =
|
||||||
|
getBlockByPreciseSlot(pool, signed_beacon_block.message.slot)
|
||||||
|
if (not slotBlockRef.isNil) and
|
||||||
|
pool.get(slotBlockRef).data.message.proposer_index ==
|
||||||
|
signed_beacon_block.message.proposer_index:
|
||||||
|
debug "isValidBeaconBlock: block isn't first block with valid signature received for the proposer",
|
||||||
|
signed_beacon_block_message_slot = signed_beacon_block.message.slot,
|
||||||
|
blckRef = getBlockByPreciseSlot(pool, signed_beacon_block.message.slot)
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
# If this block doesn't have a parent we know about, we can't/don't really
|
||||||
|
# trace it back to a known-good state/checkpoint to verify its prevenance;
|
||||||
|
# while one could getOrResolve to queue up searching for missing parent it
|
||||||
|
# might not be the best place. As much as feasible, this function aims for
|
||||||
|
# answering yes/no, not queuing other action or otherwise altering state.
|
||||||
|
let parent_ref = pool.getRef(signed_beacon_block.message.parent_root)
|
||||||
|
if parent_ref.isNil:
|
||||||
|
# TODO find where incorrect block's being produced at/around epoch 20,
|
||||||
|
# nim-beacon-chain commit 708ac80daef5e05e01d4fc84576f8692adc256a3, at
|
||||||
|
# 2020-04-02, running `make eth2_network_simulation`, or, alternately,
|
||||||
|
# why correctly produced ancestor block isn't found. By appearances, a
|
||||||
|
# chain is being forked, probably by node 0, as nodes 1/2/3 die first,
|
||||||
|
# then node 0 only dies eventually then nodes 1/2/3 are not around, to
|
||||||
|
# help it in turn finalize. So node 0 is probably culprit, around/near
|
||||||
|
# the end of epoch 19, in its block proposal(s). BlockPool.add() later
|
||||||
|
# discovers this same missing parent. The missing step here is that we
|
||||||
|
# need to be able to receive this block and store it in unresolved but
|
||||||
|
# without passing it on to other nodes (which is what EV actually does
|
||||||
|
# specify). The other BeaconBlock validation conditions cannot change,
|
||||||
|
# just because later blocks fill in gaps, but this one can. My read of
|
||||||
|
# the intent here is that only nodes which know about the parentage of
|
||||||
|
# a block should pass it on. That doesn't mean we shouldn't process it
|
||||||
|
# though, just not rebroadcast it.
|
||||||
|
# Debug output: isValidBeaconBlock: incorrectly skipping BLS validation when parent block unknown topics="blkpool" tid=2111475 file=block_pool.nim:1040 current_epoch=22 current_slot=133 parent_root=72b5b0f1 pool_head_slot=131 pool_head_state_root=48e9f4b8 proposed_block_slot=133 proposed_block_state_root=ed7b1ddd proposer_index=42 node=3
|
||||||
|
# So it's missing a head update, probably, at slot 132.
|
||||||
|
debug "isValidBeaconBlock: incorrectly skipping BLS validation when parent block unknown",
|
||||||
|
current_slot = current_slot,
|
||||||
|
current_epoch = compute_epoch_at_slot(current_slot),
|
||||||
|
parent_root = signed_beacon_block.message.parent_root,
|
||||||
|
proposed_block_slot = signed_beacon_block.message.slot,
|
||||||
|
proposer_index = signed_beacon_block.message.proposer_index,
|
||||||
|
proposed_block_state_root = signed_beacon_block.message.state_root,
|
||||||
|
pool_head_slot = pool.headState.data.data.slot,
|
||||||
|
pool_head_state_root = pool.headState.data.root
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
let bs =
|
||||||
|
BlockSlot(blck: parent_ref, slot: pool.get(parent_ref).data.message.slot)
|
||||||
|
pool.withState(pool.tmpState, bs):
|
||||||
|
let
|
||||||
|
blockRoot = hash_tree_root(signed_beacon_block.message)
|
||||||
|
domain = get_domain(pool.headState.data.data, DOMAIN_BEACON_PROPOSER,
|
||||||
|
compute_epoch_at_slot(signed_beacon_block.message.slot))
|
||||||
|
signing_root = compute_signing_root(blockRoot, domain)
|
||||||
|
proposer_index = signed_beacon_block.message.proposer_index
|
||||||
|
|
||||||
|
if proposer_index >= pool.headState.data.data.validators.len.uint64:
|
||||||
|
return false
|
||||||
|
if not blsVerify(pool.headState.data.data.validators[proposer_index].pubkey,
|
||||||
|
signing_root.data, signed_beacon_block.signature):
|
||||||
|
debug "isValidBeaconBlock: block failed signature verification"
|
||||||
|
return false
|
||||||
|
|
||||||
|
true
|
||||||
|
|
|
@ -67,7 +67,7 @@ proc main() {.async.} =
|
||||||
let cfg = CliConfig.load()
|
let cfg = CliConfig.load()
|
||||||
let web3 = await newWeb3(cfg.depositWeb3Url)
|
let web3 = await newWeb3(cfg.depositWeb3Url)
|
||||||
if cfg.privateKey.len != 0:
|
if cfg.privateKey.len != 0:
|
||||||
web3.privateKey = initPrivateKey(cfg.privateKey)
|
web3.privateKey = PrivateKey.fromHex(cfg.privateKey)[]
|
||||||
else:
|
else:
|
||||||
let accounts = await web3.provider.eth_accounts()
|
let accounts = await web3.provider.eth_accounts()
|
||||||
doAssert(accounts.len > 0)
|
doAssert(accounts.len > 0)
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
|
# TODO Cannot use push here becaise it gets applied to PeerID.init (!)
|
||||||
|
# probably because it's a generic proc...
|
||||||
|
# {.push raises: [Defect].}
|
||||||
|
|
||||||
import
|
import
|
||||||
os, net, strutils, strformat, parseutils,
|
os, net, strutils, strformat, parseutils,
|
||||||
chronicles, stew/[result, objects], eth/keys, eth/trie/db, eth/p2p/enode,
|
chronicles, stew/[results, objects], eth/keys, eth/trie/db, eth/p2p/enode,
|
||||||
eth/p2p/discoveryv5/[enr, protocol, discovery_db, types],
|
eth/p2p/discoveryv5/[enr, protocol, discovery_db, types],
|
||||||
libp2p/[multiaddress, peer],
|
libp2p/[multiaddress, peer],
|
||||||
libp2p/crypto/crypto as libp2pCrypto,
|
libp2p/crypto/crypto as libp2pCrypto,
|
||||||
|
@ -12,26 +16,13 @@ type
|
||||||
PublicKey = keys.PublicKey
|
PublicKey = keys.PublicKey
|
||||||
|
|
||||||
export
|
export
|
||||||
Eth2DiscoveryProtocol, open, close, result
|
Eth2DiscoveryProtocol, open, start, close, results
|
||||||
|
|
||||||
proc new*(T: type Eth2DiscoveryProtocol,
|
|
||||||
conf: BeaconNodeConf,
|
|
||||||
ip: IpAddress, rawPrivKeyBytes: openarray[byte]): T =
|
|
||||||
# TODO
|
|
||||||
# Implement more configuration options:
|
|
||||||
# * for setting up a specific key
|
|
||||||
# * for using a persistent database
|
|
||||||
var
|
|
||||||
pk = initPrivateKey(rawPrivKeyBytes)
|
|
||||||
db = DiscoveryDB.init(newMemoryDB())
|
|
||||||
|
|
||||||
newProtocol(pk, db, ip, conf.tcpPort, conf.udpPort)
|
|
||||||
|
|
||||||
proc toENode*(a: MultiAddress): Result[ENode, cstring] =
|
|
||||||
if not IPFS.match(a):
|
|
||||||
return err "Unsupported MultiAddress"
|
|
||||||
|
|
||||||
|
proc toENode*(a: MultiAddress): Result[ENode, cstring] {.raises: [Defect].} =
|
||||||
try:
|
try:
|
||||||
|
if not IPFS.match(a):
|
||||||
|
return err "Unsupported MultiAddress"
|
||||||
|
|
||||||
# TODO. This code is quite messy with so much string handling.
|
# TODO. This code is quite messy with so much string handling.
|
||||||
# MultiAddress can offer a more type-safe API?
|
# MultiAddress can offer a more type-safe API?
|
||||||
var
|
var
|
||||||
|
@ -58,7 +49,7 @@ proc toENode*(a: MultiAddress): Result[ENode, cstring] =
|
||||||
var pubkey: libp2pCrypto.PublicKey
|
var pubkey: libp2pCrypto.PublicKey
|
||||||
if peerId.extractPublicKey(pubkey):
|
if peerId.extractPublicKey(pubkey):
|
||||||
if pubkey.scheme == Secp256k1:
|
if pubkey.scheme == Secp256k1:
|
||||||
return ok ENode(pubkey: pubkey.skkey,
|
return ok ENode(pubkey: PublicKey(pubkey.skkey),
|
||||||
address: Address(ip: ipAddress,
|
address: Address(ip: ipAddress,
|
||||||
tcpPort: Port tcpPort,
|
tcpPort: Port tcpPort,
|
||||||
udpPort: Port udpPort))
|
udpPort: Port udpPort))
|
||||||
|
@ -66,15 +57,20 @@ proc toENode*(a: MultiAddress): Result[ENode, cstring] =
|
||||||
except CatchableError:
|
except CatchableError:
|
||||||
# This will reach the error exit path below
|
# This will reach the error exit path below
|
||||||
discard
|
discard
|
||||||
|
except Exception as e:
|
||||||
|
# TODO:
|
||||||
|
# nim-libp2p/libp2p/multiaddress.nim(616, 40) Error: can raise an unlisted exception: Exception
|
||||||
|
if e of Defect:
|
||||||
|
raise (ref Defect)(e)
|
||||||
|
|
||||||
return err "Invalid MultiAddress"
|
return err "Invalid MultiAddress"
|
||||||
|
|
||||||
proc toMultiAddressStr*(enode: ENode): string =
|
proc toMultiAddressStr*(enode: ENode): string =
|
||||||
var peerId = PeerID.init(libp2pCrypto.PublicKey(scheme: Secp256k1,
|
var peerId = PeerID.init(libp2pCrypto.PublicKey(
|
||||||
skkey: enode.pubkey))
|
scheme: Secp256k1, skkey: SkPublicKey(enode.pubkey)))
|
||||||
&"/ip4/{enode.address.ip}/tcp/{enode.address.tcpPort}/p2p/{peerId.pretty}"
|
&"/ip4/{enode.address.ip}/tcp/{enode.address.tcpPort}/p2p/{peerId.pretty}"
|
||||||
|
|
||||||
proc toENode*(enrRec: enr.Record): Result[ENode, cstring] =
|
proc toENode*(enrRec: enr.Record): Result[ENode, cstring] {.raises: [Defect].} =
|
||||||
try:
|
try:
|
||||||
# TODO: handle IPv6
|
# TODO: handle IPv6
|
||||||
let ipBytes = enrRec.get("ip", seq[byte])
|
let ipBytes = enrRec.get("ip", seq[byte])
|
||||||
|
@ -85,10 +81,10 @@ proc toENode*(enrRec: enr.Record): Result[ENode, cstring] =
|
||||||
address_v4: toArray(4, ipBytes))
|
address_v4: toArray(4, ipBytes))
|
||||||
tcpPort = Port enrRec.get("tcp", uint16)
|
tcpPort = Port enrRec.get("tcp", uint16)
|
||||||
udpPort = Port enrRec.get("udp", uint16)
|
udpPort = Port enrRec.get("udp", uint16)
|
||||||
var pubKey: keys.PublicKey
|
let pubkey = enrRec.get(PublicKey)
|
||||||
if not enrRec.get(pubKey):
|
if pubkey.isNone:
|
||||||
return err "Failed to read public key from ENR record"
|
return err "Failed to read public key from ENR record"
|
||||||
return ok ENode(pubkey: pubkey,
|
return ok ENode(pubkey: pubkey.get(),
|
||||||
address: Address(ip: ip,
|
address: Address(ip: ip,
|
||||||
tcpPort: tcpPort,
|
tcpPort: tcpPort,
|
||||||
udpPort: udpPort))
|
udpPort: udpPort))
|
||||||
|
@ -165,3 +161,27 @@ proc loadBootstrapFile*(bootstrapFile: string,
|
||||||
error "Unknown bootstrap file format", ext
|
error "Unknown bootstrap file format", ext
|
||||||
quit 1
|
quit 1
|
||||||
|
|
||||||
|
proc new*(T: type Eth2DiscoveryProtocol,
|
||||||
|
conf: BeaconNodeConf,
|
||||||
|
ip: Option[IpAddress], tcpPort, udpPort: Port,
|
||||||
|
rawPrivKeyBytes: openarray[byte]): T =
|
||||||
|
# TODO
|
||||||
|
# Implement more configuration options:
|
||||||
|
# * for setting up a specific key
|
||||||
|
# * for using a persistent database
|
||||||
|
var
|
||||||
|
pk = PrivateKey.fromRaw(rawPrivKeyBytes).tryGet()
|
||||||
|
ourPubKey = pk.toPublicKey().tryGet()
|
||||||
|
db = DiscoveryDB.init(newMemoryDB())
|
||||||
|
|
||||||
|
var bootNodes: seq[ENode]
|
||||||
|
var bootEnrs: seq[enr.Record]
|
||||||
|
for node in conf.bootstrapNodes:
|
||||||
|
addBootstrapNode(node, bootNodes, bootEnrs, ourPubKey)
|
||||||
|
loadBootstrapFile(string conf.bootstrapNodesFile, bootNodes, bootEnrs, ourPubKey)
|
||||||
|
|
||||||
|
let persistentBootstrapFile = conf.dataDir / "bootstrap_nodes.txt"
|
||||||
|
if fileExists(persistentBootstrapFile):
|
||||||
|
loadBootstrapFile(persistentBootstrapFile, bootNodes, bootEnrs, ourPubKey)
|
||||||
|
|
||||||
|
newProtocol(pk, db, ip, tcpPort, udpPort, bootEnrs)
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -51,7 +51,7 @@ func makeDeposit*(
|
||||||
withdrawal_credentials: makeWithdrawalCredentials(pubkey)))
|
withdrawal_credentials: makeWithdrawalCredentials(pubkey)))
|
||||||
|
|
||||||
if skipBLSValidation notin flags:
|
if skipBLSValidation notin flags:
|
||||||
let domain = compute_domain(DOMAIN_DEPOSIT)
|
let domain = compute_domain(DOMAIN_DEPOSIT, GENESIS_FORK_VERSION)
|
||||||
let signing_root = compute_signing_root(ret.getDepositMessage, domain)
|
let signing_root = compute_signing_root(ret.getDepositMessage, domain)
|
||||||
|
|
||||||
ret.data.signature = bls_sign(privkey, signing_root.data)
|
ret.data.signature = bls_sign(privkey, signing_root.data)
|
||||||
|
|
|
@ -1,423 +0,0 @@
|
||||||
import
|
|
||||||
algorithm, typetraits, net as stdNet,
|
|
||||||
stew/[varints,base58], stew/shims/[macros, tables], chronos, chronicles,
|
|
||||||
stint, faststreams/output_stream, serialization,
|
|
||||||
json_serialization/std/[net, options],
|
|
||||||
eth/[keys, async_utils], eth/p2p/[enode, p2p_protocol_dsl],
|
|
||||||
eth/p2p/discoveryv5/[enr, node],
|
|
||||||
# TODO: create simpler to use libp2p modules that use re-exports
|
|
||||||
libp2p/[switch, multistream, connection,
|
|
||||||
multiaddress, peerinfo, peer,
|
|
||||||
crypto/crypto, protocols/identify, protocols/protocol],
|
|
||||||
libp2p/muxers/mplex/[mplex, types],
|
|
||||||
libp2p/protocols/secure/[secure, secio],
|
|
||||||
libp2p/protocols/pubsub/[pubsub, floodsub],
|
|
||||||
libp2p/transports/[transport, tcptransport],
|
|
||||||
libp2p_json_serialization, eth2_discovery, conf, ssz,
|
|
||||||
peer_pool
|
|
||||||
|
|
||||||
import
|
|
||||||
eth/p2p/discoveryv5/protocol as discv5_protocol
|
|
||||||
|
|
||||||
export
|
|
||||||
p2pProtocol, libp2p_json_serialization, ssz
|
|
||||||
|
|
||||||
type
|
|
||||||
P2PStream = Connection
|
|
||||||
|
|
||||||
# TODO Is this really needed?
|
|
||||||
Eth2Node* = ref object of RootObj
|
|
||||||
switch*: Switch
|
|
||||||
discovery*: Eth2DiscoveryProtocol
|
|
||||||
wantedPeers*: int
|
|
||||||
peerPool*: PeerPool[Peer, PeerID]
|
|
||||||
protocolStates*: seq[RootRef]
|
|
||||||
libp2pTransportLoops*: seq[Future[void]]
|
|
||||||
|
|
||||||
EthereumNode = Eth2Node # needed for the definitions in p2p_backends_helpers
|
|
||||||
|
|
||||||
Peer* = ref object
|
|
||||||
network*: Eth2Node
|
|
||||||
info*: PeerInfo
|
|
||||||
wasDialed*: bool
|
|
||||||
discoveryId*: Eth2DiscoveryId
|
|
||||||
connectionState*: ConnectionState
|
|
||||||
protocolStates*: seq[RootRef]
|
|
||||||
maxInactivityAllowed*: Duration
|
|
||||||
score*: int
|
|
||||||
|
|
||||||
ConnectionState* = enum
|
|
||||||
None,
|
|
||||||
Connecting,
|
|
||||||
Connected,
|
|
||||||
Disconnecting,
|
|
||||||
Disconnected
|
|
||||||
|
|
||||||
UntypedResponder = object
|
|
||||||
peer*: Peer
|
|
||||||
stream*: P2PStream
|
|
||||||
|
|
||||||
Responder*[MsgType] = distinct UntypedResponder
|
|
||||||
|
|
||||||
MessageInfo* = object
|
|
||||||
name*: string
|
|
||||||
|
|
||||||
# Private fields:
|
|
||||||
libp2pCodecName: string
|
|
||||||
protocolMounter*: MounterProc
|
|
||||||
printer*: MessageContentPrinter
|
|
||||||
nextMsgResolver*: NextMsgResolver
|
|
||||||
|
|
||||||
ProtocolInfoObj* = object
|
|
||||||
name*: string
|
|
||||||
messages*: seq[MessageInfo]
|
|
||||||
index*: int # the position of the protocol in the
|
|
||||||
# ordered list of supported protocols
|
|
||||||
|
|
||||||
# Private fields:
|
|
||||||
peerStateInitializer*: PeerStateInitializer
|
|
||||||
networkStateInitializer*: NetworkStateInitializer
|
|
||||||
handshake*: HandshakeStep
|
|
||||||
disconnectHandler*: DisconnectionHandler
|
|
||||||
|
|
||||||
ProtocolInfo* = ptr ProtocolInfoObj
|
|
||||||
|
|
||||||
PeerStateInitializer* = proc(peer: Peer): RootRef {.gcsafe.}
|
|
||||||
NetworkStateInitializer* = proc(network: EthereumNode): RootRef {.gcsafe.}
|
|
||||||
HandshakeStep* = proc(peer: Peer, stream: P2PStream): Future[void] {.gcsafe.}
|
|
||||||
DisconnectionHandler* = proc(peer: Peer): Future[void] {.gcsafe.}
|
|
||||||
ThunkProc* = LPProtoHandler
|
|
||||||
MounterProc* = proc(network: Eth2Node) {.gcsafe.}
|
|
||||||
MessageContentPrinter* = proc(msg: pointer): string {.gcsafe.}
|
|
||||||
NextMsgResolver* = proc(msgData: SszReader, future: FutureBase) {.gcsafe.}
|
|
||||||
|
|
||||||
DisconnectionReason* = enum
|
|
||||||
ClientShutDown
|
|
||||||
IrrelevantNetwork
|
|
||||||
FaultOrError
|
|
||||||
|
|
||||||
PeerDisconnected* = object of CatchableError
|
|
||||||
reason*: DisconnectionReason
|
|
||||||
|
|
||||||
TransmissionError* = object of CatchableError
|
|
||||||
|
|
||||||
const
|
|
||||||
TCP = net.Protocol.IPPROTO_TCP
|
|
||||||
|
|
||||||
template `$`*(peer: Peer): string = id(peer.info)
|
|
||||||
chronicles.formatIt(Peer): $it
|
|
||||||
|
|
||||||
template remote*(peer: Peer): untyped =
|
|
||||||
peer.info.peerId
|
|
||||||
|
|
||||||
# TODO: This exists only as a compatibility layer between the daemon
|
|
||||||
# APIs and the native LibP2P ones. It won't be necessary once the
|
|
||||||
# daemon is removed.
|
|
||||||
#
|
|
||||||
template writeAllBytes(stream: P2PStream, bytes: seq[byte]): untyped =
|
|
||||||
write(stream, bytes)
|
|
||||||
|
|
||||||
template openStream(node: Eth2Node, peer: Peer, protocolId: string): untyped =
|
|
||||||
dial(node.switch, peer.info, protocolId)
|
|
||||||
|
|
||||||
proc peer(stream: P2PStream): PeerID =
|
|
||||||
# TODO: Can this be `nil`?
|
|
||||||
stream.peerInfo.peerId
|
|
||||||
#
|
|
||||||
# End of compatibility layer
|
|
||||||
|
|
||||||
proc init*(T: type Peer, network: Eth2Node, info: PeerInfo): Peer {.gcsafe.}
|
|
||||||
|
|
||||||
proc getPeer*(node: Eth2Node, peerInfo: PeerInfo): Peer {.gcsafe.} =
|
|
||||||
let peerId = peerInfo.peerId
|
|
||||||
result = node.peerPool.getOrDefault(peerId)
|
|
||||||
if result == nil:
|
|
||||||
result = Peer.init(node, peerInfo)
|
|
||||||
|
|
||||||
proc peerFromStream(network: Eth2Node, stream: P2PStream): Peer {.gcsafe.} =
|
|
||||||
# TODO: Can this be `nil`?
|
|
||||||
return network.getPeer(stream.peerInfo)
|
|
||||||
|
|
||||||
proc getKey*(peer: Peer): PeerID {.inline.} =
|
|
||||||
result = peer.info.peerId
|
|
||||||
|
|
||||||
proc getFuture*(peer: Peer): Future[void] {.inline.} =
|
|
||||||
result = peer.info.lifeFuture()
|
|
||||||
|
|
||||||
proc `<`*(a, b: Peer): bool =
|
|
||||||
result = `<`(a.score, b.score)
|
|
||||||
|
|
||||||
proc disconnect*(peer: Peer, reason: DisconnectionReason,
|
|
||||||
notifyOtherPeer = false) {.async.} =
|
|
||||||
# TODO: How should we notify the other peer?
|
|
||||||
if peer.connectionState notin {Disconnecting, Disconnected}:
|
|
||||||
peer.connectionState = Disconnecting
|
|
||||||
await peer.network.switch.disconnect(peer.info)
|
|
||||||
peer.connectionState = Disconnected
|
|
||||||
peer.network.peerPool.release(peer)
|
|
||||||
peer.info.close()
|
|
||||||
|
|
||||||
proc safeClose(stream: P2PStream) {.async.} =
|
|
||||||
if not stream.closed:
|
|
||||||
await close(stream)
|
|
||||||
|
|
||||||
proc handleIncomingPeer*(peer: Peer)
|
|
||||||
|
|
||||||
include eth/p2p/p2p_backends_helpers
|
|
||||||
include eth/p2p/p2p_tracing
|
|
||||||
include libp2p_backends_common
|
|
||||||
|
|
||||||
proc handleOutgoingPeer*(peer: Peer): Future[void] {.async.} =
|
|
||||||
let network = peer.network
|
|
||||||
|
|
||||||
proc onPeerClosed(udata: pointer) {.gcsafe.} =
|
|
||||||
debug "Peer (outgoing) lost", peer = $peer.info
|
|
||||||
libp2p_peers.set int64(len(network.peerPool))
|
|
||||||
|
|
||||||
let res = await network.peerPool.addOutgoingPeer(peer)
|
|
||||||
if res:
|
|
||||||
debug "Peer (outgoing) has been added to PeerPool", peer = $peer.info
|
|
||||||
peer.getFuture().addCallback(onPeerClosed)
|
|
||||||
libp2p_peers.set int64(len(network.peerPool))
|
|
||||||
|
|
||||||
proc handleIncomingPeer*(peer: Peer) =
|
|
||||||
let network = peer.network
|
|
||||||
|
|
||||||
proc onPeerClosed(udata: pointer) {.gcsafe.} =
|
|
||||||
debug "Peer (incoming) lost", peer = $peer.info
|
|
||||||
libp2p_peers.set int64(len(network.peerPool))
|
|
||||||
|
|
||||||
let res = network.peerPool.addIncomingPeerNoWait(peer)
|
|
||||||
if res:
|
|
||||||
debug "Peer (incoming) has been added to PeerPool", peer = $peer.info
|
|
||||||
peer.getFuture().addCallback(onPeerClosed)
|
|
||||||
libp2p_peers.set int64(len(network.peerPool))
|
|
||||||
|
|
||||||
proc toPeerInfo*(r: enr.TypedRecord): PeerInfo =
|
|
||||||
if r.secp256k1.isSome:
|
|
||||||
var pubKey: keys.PublicKey
|
|
||||||
if recoverPublicKey(r.secp256k1.get, pubKey) != EthKeysStatus.Success:
|
|
||||||
return # TODO
|
|
||||||
|
|
||||||
let peerId = PeerID.init crypto.PublicKey(scheme: Secp256k1, skkey: pubKey)
|
|
||||||
var addresses = newSeq[MultiAddress]()
|
|
||||||
|
|
||||||
if r.ip.isSome and r.tcp.isSome:
|
|
||||||
let ip = IpAddress(family: IpAddressFamily.IPv4,
|
|
||||||
address_v4: r.ip.get)
|
|
||||||
addresses.add MultiAddress.init(ip, TCP, Port r.tcp.get)
|
|
||||||
|
|
||||||
if r.ip6.isSome:
|
|
||||||
let ip = IpAddress(family: IpAddressFamily.IPv6,
|
|
||||||
address_v6: r.ip6.get)
|
|
||||||
if r.tcp6.isSome:
|
|
||||||
addresses.add MultiAddress.init(ip, TCP, Port r.tcp6.get)
|
|
||||||
elif r.tcp.isSome:
|
|
||||||
addresses.add MultiAddress.init(ip, TCP, Port r.tcp.get)
|
|
||||||
else:
|
|
||||||
discard
|
|
||||||
|
|
||||||
if addresses.len > 0:
|
|
||||||
return PeerInfo.init(peerId, addresses)
|
|
||||||
|
|
||||||
proc toPeerInfo(r: Option[enr.TypedRecord]): PeerInfo =
|
|
||||||
if r.isSome:
|
|
||||||
return r.get.toPeerInfo
|
|
||||||
|
|
||||||
proc dialPeer*(node: Eth2Node, peerInfo: PeerInfo) {.async.} =
|
|
||||||
logScope: peer = $peerInfo
|
|
||||||
|
|
||||||
debug "Connecting to peer"
|
|
||||||
await node.switch.connect(peerInfo)
|
|
||||||
var peer = node.getPeer(peerInfo)
|
|
||||||
peer.wasDialed = true
|
|
||||||
|
|
||||||
debug "Initializing connection"
|
|
||||||
await initializeConnection(peer)
|
|
||||||
|
|
||||||
inc libp2p_successful_dials
|
|
||||||
debug "Network handshakes completed"
|
|
||||||
|
|
||||||
await handleOutgoingPeer(peer)
|
|
||||||
|
|
||||||
proc runDiscoveryLoop*(node: Eth2Node) {.async.} =
|
|
||||||
debug "Starting discovery loop"
|
|
||||||
|
|
||||||
while true:
|
|
||||||
let currentPeerCount = node.peerPool.len
|
|
||||||
if currentPeerCount < node.wantedPeers:
|
|
||||||
try:
|
|
||||||
let discoveredPeers =
|
|
||||||
node.discovery.randomNodes(node.wantedPeers - currentPeerCount)
|
|
||||||
debug "Discovered peers", peer = $discoveredPeers
|
|
||||||
for peer in discoveredPeers:
|
|
||||||
try:
|
|
||||||
let peerInfo = peer.record.toTypedRecord.toPeerInfo
|
|
||||||
if peerInfo != nil and peerInfo.id notin node.switch.connections:
|
|
||||||
# TODO do this in parallel
|
|
||||||
await node.dialPeer(peerInfo)
|
|
||||||
except CatchableError as err:
|
|
||||||
debug "Failed to connect to peer", peer = $peer, err = err.msg
|
|
||||||
except CatchableError as err:
|
|
||||||
debug "Failure in discovery", err = err.msg
|
|
||||||
|
|
||||||
await sleepAsync seconds(1)
|
|
||||||
|
|
||||||
proc init*(T: type Eth2Node, conf: BeaconNodeConf,
|
|
||||||
switch: Switch, ip: IpAddress, privKey: keys.PrivateKey): T =
|
|
||||||
new result
|
|
||||||
result.switch = switch
|
|
||||||
result.discovery = Eth2DiscoveryProtocol.new(conf, ip, privKey.data)
|
|
||||||
result.wantedPeers = conf.maxPeers
|
|
||||||
result.peerPool = newPeerPool[Peer, PeerID](maxPeers = conf.maxPeers)
|
|
||||||
|
|
||||||
newSeq result.protocolStates, allProtocols.len
|
|
||||||
for proto in allProtocols:
|
|
||||||
if proto.networkStateInitializer != nil:
|
|
||||||
result.protocolStates[proto.index] = proto.networkStateInitializer(result)
|
|
||||||
|
|
||||||
for msg in proto.messages:
|
|
||||||
if msg.protocolMounter != nil:
|
|
||||||
msg.protocolMounter result
|
|
||||||
|
|
||||||
template publicKey*(node: Eth2Node): keys.PublicKey =
|
|
||||||
node.discovery.privKey.getPublicKey
|
|
||||||
|
|
||||||
template addKnownPeer*(node: Eth2Node, peer: ENode|enr.Record) =
|
|
||||||
node.discovery.addNode peer
|
|
||||||
|
|
||||||
proc start*(node: Eth2Node) {.async.} =
|
|
||||||
node.discovery.open()
|
|
||||||
node.libp2pTransportLoops = await node.switch.start()
|
|
||||||
traceAsyncErrors node.runDiscoveryLoop()
|
|
||||||
|
|
||||||
proc init*(T: type Peer, network: Eth2Node, info: PeerInfo): Peer =
|
|
||||||
new result
|
|
||||||
result.info = info
|
|
||||||
result.network = network
|
|
||||||
result.connectionState = Connected
|
|
||||||
result.maxInactivityAllowed = 15.minutes # TODO: Read this from the config
|
|
||||||
newSeq result.protocolStates, allProtocols.len
|
|
||||||
for i in 0 ..< allProtocols.len:
|
|
||||||
let proto = allProtocols[i]
|
|
||||||
if proto.peerStateInitializer != nil:
|
|
||||||
result.protocolStates[i] = proto.peerStateInitializer(result)
|
|
||||||
|
|
||||||
proc registerMsg(protocol: ProtocolInfo,
|
|
||||||
name: string,
|
|
||||||
mounter: MounterProc,
|
|
||||||
libp2pCodecName: string,
|
|
||||||
printer: MessageContentPrinter) =
|
|
||||||
protocol.messages.add MessageInfo(name: name,
|
|
||||||
protocolMounter: mounter,
|
|
||||||
libp2pCodecName: libp2pCodecName,
|
|
||||||
printer: printer)
|
|
||||||
|
|
||||||
proc p2pProtocolBackendImpl*(p: P2PProtocol): Backend =
|
|
||||||
var
|
|
||||||
Format = ident "SSZ"
|
|
||||||
Responder = bindSym "Responder"
|
|
||||||
P2PStream = bindSym "P2PStream"
|
|
||||||
OutputStream = bindSym "OutputStream"
|
|
||||||
Peer = bindSym "Peer"
|
|
||||||
Eth2Node = bindSym "Eth2Node"
|
|
||||||
messagePrinter = bindSym "messagePrinter"
|
|
||||||
milliseconds = bindSym "milliseconds"
|
|
||||||
registerMsg = bindSym "registerMsg"
|
|
||||||
initProtocol = bindSym "initProtocol"
|
|
||||||
bindSymOp = bindSym "bindSym"
|
|
||||||
errVar = ident "err"
|
|
||||||
msgVar = ident "msg"
|
|
||||||
msgBytesVar = ident "msgBytes"
|
|
||||||
networkVar = ident "network"
|
|
||||||
await = ident "await"
|
|
||||||
callUserHandler = ident "callUserHandler"
|
|
||||||
|
|
||||||
p.useRequestIds = false
|
|
||||||
p.useSingleRecordInlining = true
|
|
||||||
|
|
||||||
new result
|
|
||||||
|
|
||||||
result.PeerType = Peer
|
|
||||||
result.NetworkType = Eth2Node
|
|
||||||
result.registerProtocol = bindSym "registerProtocol"
|
|
||||||
result.setEventHandlers = bindSym "setEventHandlers"
|
|
||||||
result.SerializationFormat = Format
|
|
||||||
result.ResponderType = Responder
|
|
||||||
|
|
||||||
result.afterProtocolInit = proc (p: P2PProtocol) =
|
|
||||||
p.onPeerConnected.params.add newIdentDefs(streamVar, P2PStream)
|
|
||||||
|
|
||||||
result.implementMsg = proc (msg: Message) =
|
|
||||||
let
|
|
||||||
protocol = msg.protocol
|
|
||||||
msgName = $msg.ident
|
|
||||||
msgNameLit = newLit msgName
|
|
||||||
MsgRecName = msg.recName
|
|
||||||
MsgStrongRecName = msg.strongRecName
|
|
||||||
codecNameLit = getRequestProtoName(msg.procDef)
|
|
||||||
|
|
||||||
if msg.procDef.body.kind != nnkEmpty and msg.kind == msgRequest:
|
|
||||||
# Request procs need an extra param - the stream where the response
|
|
||||||
# should be written:
|
|
||||||
msg.userHandler.params.insert(2, newIdentDefs(streamVar, P2PStream))
|
|
||||||
msg.initResponderCall.add streamVar
|
|
||||||
|
|
||||||
##
|
|
||||||
## Implement the Thunk:
|
|
||||||
##
|
|
||||||
## The protocol handlers in nim-libp2p receive only a `P2PStream`
|
|
||||||
## parameter and there is no way to access the wider context (such
|
|
||||||
## as the current `Switch`). In our handlers, we may need to list all
|
|
||||||
## peers in the current network, so we must keep a reference to the
|
|
||||||
## network object in the closure environment of the installed handlers.
|
|
||||||
##
|
|
||||||
## For this reason, we define a `protocol mounter` proc that will
|
|
||||||
## initialize the network object by creating handlers bound to the
|
|
||||||
## specific network.
|
|
||||||
##
|
|
||||||
let
|
|
||||||
protocolMounterName = ident(msgName & "_mounter")
|
|
||||||
userHandlerCall = msg.genUserHandlerCall(msgVar, [peerVar, streamVar])
|
|
||||||
|
|
||||||
var mounter: NimNode
|
|
||||||
if msg.userHandler != nil:
|
|
||||||
protocol.outRecvProcs.add quote do:
|
|
||||||
template `callUserHandler`(`peerVar`: `Peer`,
|
|
||||||
`streamVar`: `P2PStream`,
|
|
||||||
`msgVar`: `MsgRecName`): untyped =
|
|
||||||
`userHandlerCall`
|
|
||||||
|
|
||||||
proc `protocolMounterName`(`networkVar`: `Eth2Node`) =
|
|
||||||
proc thunk(`streamVar`: `P2PStream`,
|
|
||||||
proto: string): Future[void] {.gcsafe.} =
|
|
||||||
return handleIncomingStream(`networkVar`, `streamVar`,
|
|
||||||
`MsgStrongRecName`, `Format`)
|
|
||||||
|
|
||||||
mount `networkVar`.switch,
|
|
||||||
LPProtocol(codec: `codecNameLit`, handler: thunk)
|
|
||||||
|
|
||||||
mounter = protocolMounterName
|
|
||||||
else:
|
|
||||||
mounter = newNilLit()
|
|
||||||
|
|
||||||
##
|
|
||||||
## Implement Senders and Handshake
|
|
||||||
##
|
|
||||||
if msg.kind == msgHandshake:
|
|
||||||
macros.error "Handshake messages are not supported in LibP2P protocols"
|
|
||||||
else:
|
|
||||||
var sendProc = msg.createSendProc()
|
|
||||||
implementSendProcBody sendProc
|
|
||||||
|
|
||||||
protocol.outProcRegistrations.add(
|
|
||||||
newCall(registerMsg,
|
|
||||||
protocol.protocolInfoVar,
|
|
||||||
msgNameLit,
|
|
||||||
mounter,
|
|
||||||
codecNameLit,
|
|
||||||
newTree(nnkBracketExpr, messagePrinter, MsgRecName)))
|
|
||||||
|
|
||||||
result.implementProtocolInit = proc (p: P2PProtocol): NimNode =
|
|
||||||
return newCall(initProtocol, newLit(p.name), p.peerInit, p.netInit)
|
|
||||||
|
|
|
@ -1,407 +0,0 @@
|
||||||
import
|
|
||||||
metrics
|
|
||||||
|
|
||||||
type
|
|
||||||
ResponseCode* = enum
|
|
||||||
Success
|
|
||||||
InvalidRequest
|
|
||||||
ServerError
|
|
||||||
|
|
||||||
Bytes = seq[byte]
|
|
||||||
|
|
||||||
const
|
|
||||||
defaultIncomingReqTimeout = 5000
|
|
||||||
HandshakeTimeout = FaultOrError
|
|
||||||
|
|
||||||
# Spec constants
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/dev/specs/networking/p2p-interface.md#eth-20-network-interaction-domains
|
|
||||||
REQ_RESP_MAX_SIZE* = 1 * 1024 * 1024 # bytes
|
|
||||||
GOSSIP_MAX_SIZE* = 1 * 1024 * 1024 # bytes
|
|
||||||
TTFB_TIMEOUT* = 5.seconds
|
|
||||||
RESP_TIMEOUT* = 10.seconds
|
|
||||||
|
|
||||||
readTimeoutErrorMsg = "Exceeded read timeout for a request"
|
|
||||||
|
|
||||||
logScope:
|
|
||||||
topics = "libp2p"
|
|
||||||
|
|
||||||
declarePublicGauge libp2p_successful_dials,
|
|
||||||
"Number of successfully dialed peers"
|
|
||||||
|
|
||||||
declarePublicGauge libp2p_peers,
|
|
||||||
"Number of active libp2p peers"
|
|
||||||
|
|
||||||
template libp2pProtocol*(name: string, version: int) {.pragma.}
|
|
||||||
|
|
||||||
proc getRequestProtoName(fn: NimNode): NimNode =
|
|
||||||
# `getCustomPragmaVal` doesn't work yet on regular nnkProcDef nodes
|
|
||||||
# (TODO: file as an issue)
|
|
||||||
|
|
||||||
let pragmas = fn.pragma
|
|
||||||
if pragmas.kind == nnkPragma and pragmas.len > 0:
|
|
||||||
for pragma in pragmas:
|
|
||||||
if pragma.len > 0 and $pragma[0] == "libp2pProtocol":
|
|
||||||
let protoName = $(pragma[1])
|
|
||||||
let protoVer = $(pragma[2].intVal)
|
|
||||||
return newLit("/eth2/beacon_chain/req/" & protoName & "/" & protoVer & "/ssz")
|
|
||||||
|
|
||||||
return newLit("")
|
|
||||||
|
|
||||||
template raisePeerDisconnected(msg: string, r: DisconnectionReason) =
|
|
||||||
var e = newException(PeerDisconnected, msg)
|
|
||||||
e.reason = r
|
|
||||||
raise e
|
|
||||||
|
|
||||||
proc disconnectAndRaise(peer: Peer,
|
|
||||||
reason: DisconnectionReason,
|
|
||||||
msg: string) {.async.} =
|
|
||||||
let r = reason
|
|
||||||
await peer.disconnect(r)
|
|
||||||
raisePeerDisconnected(msg, r)
|
|
||||||
|
|
||||||
proc readChunk(stream: P2PStream,
|
|
||||||
MsgType: type,
|
|
||||||
withResponseCode: bool,
|
|
||||||
deadline: Future[void]): Future[Option[MsgType]] {.gcsafe.}
|
|
||||||
|
|
||||||
proc readSizePrefix(stream: P2PStream,
|
|
||||||
deadline: Future[void]): Future[int] {.async.} =
|
|
||||||
trace "about to read msg size prefix"
|
|
||||||
var parser: VarintParser[uint64, ProtoBuf]
|
|
||||||
while true:
|
|
||||||
var nextByte: byte
|
|
||||||
var readNextByte = stream.readExactly(addr nextByte, 1)
|
|
||||||
await readNextByte or deadline
|
|
||||||
if not readNextByte.finished:
|
|
||||||
trace "size prefix byte not received in time"
|
|
||||||
return -1
|
|
||||||
case parser.feedByte(nextByte)
|
|
||||||
of Done:
|
|
||||||
let res = parser.getResult
|
|
||||||
if res > uint64(REQ_RESP_MAX_SIZE):
|
|
||||||
trace "size prefix outside of range", res
|
|
||||||
return -1
|
|
||||||
else:
|
|
||||||
trace "got size prefix", res
|
|
||||||
return int(res)
|
|
||||||
of Overflow:
|
|
||||||
trace "size prefix overflow"
|
|
||||||
return -1
|
|
||||||
of Incomplete:
|
|
||||||
continue
|
|
||||||
|
|
||||||
proc readMsgBytes(stream: P2PStream,
|
|
||||||
withResponseCode: bool,
|
|
||||||
deadline: Future[void]): Future[Bytes] {.async.} =
|
|
||||||
trace "about to read message bytes", withResponseCode
|
|
||||||
|
|
||||||
try:
|
|
||||||
if withResponseCode:
|
|
||||||
var responseCode: byte
|
|
||||||
trace "about to read response code"
|
|
||||||
var readResponseCode = stream.readExactly(addr responseCode, 1)
|
|
||||||
await readResponseCode or deadline
|
|
||||||
|
|
||||||
if not readResponseCode.finished:
|
|
||||||
trace "response code not received in time"
|
|
||||||
return
|
|
||||||
|
|
||||||
if responseCode > ResponseCode.high.byte:
|
|
||||||
trace "invalid response code", responseCode
|
|
||||||
return
|
|
||||||
|
|
||||||
logScope: responseCode = ResponseCode(responseCode)
|
|
||||||
trace "got response code"
|
|
||||||
|
|
||||||
case ResponseCode(responseCode)
|
|
||||||
of InvalidRequest, ServerError:
|
|
||||||
let responseErrMsg = await readChunk(stream, string, false, deadline)
|
|
||||||
debug "P2P request resulted in error", responseErrMsg
|
|
||||||
return
|
|
||||||
|
|
||||||
of Success:
|
|
||||||
# The response is OK, the execution continues below
|
|
||||||
discard
|
|
||||||
|
|
||||||
var sizePrefix = await readSizePrefix(stream, deadline)
|
|
||||||
trace "got msg size prefix", sizePrefix
|
|
||||||
|
|
||||||
if sizePrefix == -1:
|
|
||||||
debug "Failed to read an incoming message size prefix", peer = stream.peer
|
|
||||||
return
|
|
||||||
|
|
||||||
if sizePrefix == 0:
|
|
||||||
debug "Received SSZ with zero size", peer = stream.peer
|
|
||||||
return
|
|
||||||
|
|
||||||
trace "about to read msg bytes", len = sizePrefix
|
|
||||||
var msgBytes = newSeq[byte](sizePrefix)
|
|
||||||
var readBody = stream.readExactly(addr msgBytes[0], sizePrefix)
|
|
||||||
await readBody or deadline
|
|
||||||
if not readBody.finished:
|
|
||||||
trace "msg bytes not received in time"
|
|
||||||
return
|
|
||||||
|
|
||||||
trace "got message bytes", len = sizePrefix
|
|
||||||
return msgBytes
|
|
||||||
|
|
||||||
except TransportIncompleteError:
|
|
||||||
return @[]
|
|
||||||
|
|
||||||
proc readChunk(stream: P2PStream,
|
|
||||||
MsgType: type,
|
|
||||||
withResponseCode: bool,
|
|
||||||
deadline: Future[void]): Future[Option[MsgType]] {.gcsafe, async.} =
|
|
||||||
var msgBytes = await stream.readMsgBytes(withResponseCode, deadline)
|
|
||||||
try:
|
|
||||||
if msgBytes.len > 0:
|
|
||||||
return some SSZ.decode(msgBytes, MsgType)
|
|
||||||
except SerializationError as err:
|
|
||||||
debug "Failed to decode a network message",
|
|
||||||
msgBytes, errMsg = err.formatMsg("<msg>")
|
|
||||||
return
|
|
||||||
|
|
||||||
proc readResponse(
|
|
||||||
stream: P2PStream,
|
|
||||||
MsgType: type,
|
|
||||||
deadline: Future[void]): Future[Option[MsgType]] {.gcsafe, async.} =
|
|
||||||
|
|
||||||
when MsgType is seq:
|
|
||||||
type E = ElemType(MsgType)
|
|
||||||
var results: MsgType
|
|
||||||
while true:
|
|
||||||
let nextRes = await readChunk(stream, E, true, deadline)
|
|
||||||
if nextRes.isNone: break
|
|
||||||
results.add nextRes.get
|
|
||||||
if results.len > 0:
|
|
||||||
return some(results)
|
|
||||||
else:
|
|
||||||
return await readChunk(stream, MsgType, true, deadline)
|
|
||||||
|
|
||||||
proc encodeErrorMsg(responseCode: ResponseCode, errMsg: string): Bytes =
|
|
||||||
var s = init OutputStream
|
|
||||||
s.append byte(responseCode)
|
|
||||||
s.appendVarint errMsg.len
|
|
||||||
s.appendValue SSZ, errMsg
|
|
||||||
s.getOutput
|
|
||||||
|
|
||||||
proc sendErrorResponse(peer: Peer,
|
|
||||||
stream: P2PStream,
|
|
||||||
err: ref SerializationError,
|
|
||||||
msgName: string,
|
|
||||||
msgBytes: Bytes) {.async.} =
|
|
||||||
debug "Received an invalid request",
|
|
||||||
peer, msgName, msgBytes, errMsg = err.formatMsg("<msg>")
|
|
||||||
|
|
||||||
let responseBytes = encodeErrorMsg(InvalidRequest, err.formatMsg("msg"))
|
|
||||||
await stream.writeAllBytes(responseBytes)
|
|
||||||
await stream.close()
|
|
||||||
|
|
||||||
proc sendErrorResponse(peer: Peer,
|
|
||||||
stream: P2PStream,
|
|
||||||
responseCode: ResponseCode,
|
|
||||||
errMsg: string) {.async.} =
|
|
||||||
debug "Error processing request", peer, responseCode, errMsg
|
|
||||||
|
|
||||||
let responseBytes = encodeErrorMsg(ServerError, errMsg)
|
|
||||||
await stream.writeAllBytes(responseBytes)
|
|
||||||
await stream.close()
|
|
||||||
|
|
||||||
proc sendNotificationMsg(peer: Peer, protocolId: string, requestBytes: Bytes) {.async} =
|
|
||||||
var deadline = sleepAsync RESP_TIMEOUT
|
|
||||||
var streamFut = peer.network.openStream(peer, protocolId)
|
|
||||||
await streamFut or deadline
|
|
||||||
if not streamFut.finished:
|
|
||||||
# TODO: we are returning here because the deadline passed, but
|
|
||||||
# the stream can still be opened eventually a bit later. Who is
|
|
||||||
# going to close it then?
|
|
||||||
raise newException(TransmissionError, "Failed to open LibP2P stream")
|
|
||||||
|
|
||||||
let stream = streamFut.read
|
|
||||||
defer:
|
|
||||||
await safeClose(stream)
|
|
||||||
|
|
||||||
var s = init OutputStream
|
|
||||||
s.appendVarint requestBytes.len.uint64
|
|
||||||
s.append requestBytes
|
|
||||||
let bytes = s.getOutput
|
|
||||||
await stream.writeAllBytes(bytes)
|
|
||||||
|
|
||||||
# TODO There is too much duplication in the responder functions, but
|
|
||||||
# I hope to reduce this when I increse the reliance on output streams.
|
|
||||||
proc sendResponseChunkBytes(responder: UntypedResponder, payload: Bytes) {.async.} =
|
|
||||||
var s = init OutputStream
|
|
||||||
s.append byte(Success)
|
|
||||||
s.appendVarint payload.len.uint64
|
|
||||||
s.append payload
|
|
||||||
let bytes = s.getOutput
|
|
||||||
await responder.stream.writeAllBytes(bytes)
|
|
||||||
|
|
||||||
proc sendResponseChunkObj(responder: UntypedResponder, val: auto) {.async.} =
|
|
||||||
var s = init OutputStream
|
|
||||||
s.append byte(Success)
|
|
||||||
s.appendValue SSZ, sizePrefixed(val)
|
|
||||||
let bytes = s.getOutput
|
|
||||||
await responder.stream.writeAllBytes(bytes)
|
|
||||||
|
|
||||||
proc sendResponseChunks[T](responder: UntypedResponder, chunks: seq[T]) {.async.} =
|
|
||||||
var s = init OutputStream
|
|
||||||
for chunk in chunks:
|
|
||||||
s.append byte(Success)
|
|
||||||
s.appendValue SSZ, sizePrefixed(chunk)
|
|
||||||
|
|
||||||
let bytes = s.getOutput
|
|
||||||
await responder.stream.writeAllBytes(bytes)
|
|
||||||
|
|
||||||
proc makeEth2Request(peer: Peer, protocolId: string, requestBytes: Bytes,
|
|
||||||
ResponseMsg: type,
|
|
||||||
timeout: Duration): Future[Option[ResponseMsg]] {.gcsafe, async.} =
|
|
||||||
var deadline = sleepAsync timeout
|
|
||||||
|
|
||||||
# Open a new LibP2P stream
|
|
||||||
var streamFut = peer.network.openStream(peer, protocolId)
|
|
||||||
await streamFut or deadline
|
|
||||||
if not streamFut.finished:
|
|
||||||
# TODO: we are returning here because the deadline passed, but
|
|
||||||
# the stream can still be opened eventually a bit later. Who is
|
|
||||||
# going to close it then?
|
|
||||||
return none(ResponseMsg)
|
|
||||||
|
|
||||||
let stream = streamFut.read
|
|
||||||
defer:
|
|
||||||
await safeClose(stream)
|
|
||||||
|
|
||||||
# Send the request
|
|
||||||
var s = init OutputStream
|
|
||||||
s.appendVarint requestBytes.len.uint64
|
|
||||||
s.append requestBytes
|
|
||||||
let bytes = s.getOutput
|
|
||||||
await stream.writeAllBytes(bytes)
|
|
||||||
|
|
||||||
# Read the response
|
|
||||||
return await stream.readResponse(ResponseMsg, deadline)
|
|
||||||
|
|
||||||
proc init*[MsgType](T: type Responder[MsgType],
|
|
||||||
peer: Peer, stream: P2PStream): T =
|
|
||||||
T(UntypedResponder(peer: peer, stream: stream))
|
|
||||||
|
|
||||||
template write*[M](r: var Responder[M], val: auto): auto =
|
|
||||||
mixin send
|
|
||||||
type Msg = M
|
|
||||||
type MsgRec = RecType(Msg)
|
|
||||||
when MsgRec is seq|openarray:
|
|
||||||
type E = ElemType(MsgRec)
|
|
||||||
when val is E:
|
|
||||||
sendResponseChunkObj(UntypedResponder(r), val)
|
|
||||||
elif val is MsgRec:
|
|
||||||
sendResponseChunks(UntypedResponder(r), val)
|
|
||||||
else:
|
|
||||||
{.fatal: "Unepected message type".}
|
|
||||||
else:
|
|
||||||
send(r, val)
|
|
||||||
|
|
||||||
proc performProtocolHandshakes*(peer: Peer) {.async.} =
|
|
||||||
var subProtocolsHandshakes = newSeqOfCap[Future[void]](allProtocols.len)
|
|
||||||
for protocol in allProtocols:
|
|
||||||
if protocol.handshake != nil:
|
|
||||||
subProtocolsHandshakes.add((protocol.handshake)(peer, nil))
|
|
||||||
|
|
||||||
await all(subProtocolsHandshakes)
|
|
||||||
|
|
||||||
template initializeConnection*(peer: Peer): auto =
|
|
||||||
performProtocolHandshakes(peer)
|
|
||||||
|
|
||||||
proc initProtocol(name: string,
|
|
||||||
peerInit: PeerStateInitializer,
|
|
||||||
networkInit: NetworkStateInitializer): ProtocolInfoObj =
|
|
||||||
result.name = name
|
|
||||||
result.messages = @[]
|
|
||||||
result.peerStateInitializer = peerInit
|
|
||||||
result.networkStateInitializer = networkInit
|
|
||||||
|
|
||||||
proc registerProtocol(protocol: ProtocolInfo) =
|
|
||||||
# TODO: This can be done at compile-time in the future
|
|
||||||
let pos = lowerBound(gProtocols, protocol)
|
|
||||||
gProtocols.insert(protocol, pos)
|
|
||||||
for i in 0 ..< gProtocols.len:
|
|
||||||
gProtocols[i].index = i
|
|
||||||
|
|
||||||
proc setEventHandlers(p: ProtocolInfo,
|
|
||||||
handshake: HandshakeStep,
|
|
||||||
disconnectHandler: DisconnectionHandler) =
|
|
||||||
p.handshake = handshake
|
|
||||||
p.disconnectHandler = disconnectHandler
|
|
||||||
|
|
||||||
proc implementSendProcBody(sendProc: SendProc) =
|
|
||||||
let
|
|
||||||
msg = sendProc.msg
|
|
||||||
UntypedResponder = bindSym "UntypedResponder"
|
|
||||||
await = ident "await"
|
|
||||||
|
|
||||||
proc sendCallGenerator(peer, bytes: NimNode): NimNode =
|
|
||||||
if msg.kind != msgResponse:
|
|
||||||
let msgProto = getRequestProtoName(msg.procDef)
|
|
||||||
case msg.kind
|
|
||||||
of msgRequest:
|
|
||||||
let
|
|
||||||
timeout = msg.timeoutParam[0]
|
|
||||||
ResponseRecord = msg.response.recName
|
|
||||||
quote:
|
|
||||||
makeEth2Request(`peer`, `msgProto`, `bytes`,
|
|
||||||
`ResponseRecord`, `timeout`)
|
|
||||||
else:
|
|
||||||
quote: sendNotificationMsg(`peer`, `msgProto`, `bytes`)
|
|
||||||
else:
|
|
||||||
quote: sendResponseChunkBytes(`UntypedResponder`(`peer`), `bytes`)
|
|
||||||
|
|
||||||
sendProc.useStandardBody(nil, nil, sendCallGenerator)
|
|
||||||
|
|
||||||
proc handleIncomingStream(network: Eth2Node, stream: P2PStream,
|
|
||||||
MsgType, Format: distinct type) {.async, gcsafe.} =
|
|
||||||
mixin callUserHandler, RecType
|
|
||||||
const msgName = typetraits.name(MsgType)
|
|
||||||
|
|
||||||
## Uncomment this to enable tracing on all incoming requests
|
|
||||||
## You can include `msgNameLit` in the condition to select
|
|
||||||
## more specific requests:
|
|
||||||
# when chronicles.runtimeFilteringEnabled:
|
|
||||||
# setLogLevel(LogLevel.TRACE)
|
|
||||||
# defer: setLogLevel(LogLevel.DEBUG)
|
|
||||||
# trace "incoming " & `msgNameLit` & " stream"
|
|
||||||
|
|
||||||
let peer = peerFromStream(network, stream)
|
|
||||||
|
|
||||||
handleIncomingPeer(peer)
|
|
||||||
|
|
||||||
defer:
|
|
||||||
await safeClose(stream)
|
|
||||||
|
|
||||||
let
|
|
||||||
deadline = sleepAsync RESP_TIMEOUT
|
|
||||||
msgBytes = await readMsgBytes(stream, false, deadline)
|
|
||||||
|
|
||||||
if msgBytes.len == 0:
|
|
||||||
await sendErrorResponse(peer, stream, ServerError, readTimeoutErrorMsg)
|
|
||||||
return
|
|
||||||
|
|
||||||
type MsgRec = RecType(MsgType)
|
|
||||||
var msg: MsgRec
|
|
||||||
try:
|
|
||||||
msg = decode(Format, msgBytes, MsgRec)
|
|
||||||
except SerializationError as err:
|
|
||||||
await sendErrorResponse(peer, stream, err, msgName, msgBytes)
|
|
||||||
return
|
|
||||||
except Exception as err:
|
|
||||||
# TODO. This is temporary code that should be removed after interop.
|
|
||||||
# It can be enabled only in certain diagnostic builds where it should
|
|
||||||
# re-raise the exception.
|
|
||||||
debug "Crash during serialization", inputBytes = toHex(msgBytes), msgName
|
|
||||||
await sendErrorResponse(peer, stream, ServerError, err.msg)
|
|
||||||
raise err
|
|
||||||
|
|
||||||
try:
|
|
||||||
logReceivedMsg(peer, MsgType(msg))
|
|
||||||
await callUserHandler(peer, stream, msg)
|
|
||||||
except CatchableError as err:
|
|
||||||
await sendErrorResponse(peer, stream, ServerError, err.msg)
|
|
||||||
|
|
|
@ -1,266 +0,0 @@
|
||||||
import
|
|
||||||
algorithm, typetraits,
|
|
||||||
stew/varints, stew/shims/[macros, tables], chronos, chronicles,
|
|
||||||
libp2p/daemon/daemonapi, faststreams/output_stream, serialization,
|
|
||||||
json_serialization/std/options, eth/p2p/p2p_protocol_dsl,
|
|
||||||
libp2p_json_serialization, ssz
|
|
||||||
|
|
||||||
export
|
|
||||||
daemonapi, p2pProtocol, libp2p_json_serialization, ssz
|
|
||||||
|
|
||||||
type
|
|
||||||
Eth2Node* = ref object of RootObj
|
|
||||||
daemon*: DaemonAPI
|
|
||||||
peers*: Table[PeerID, Peer]
|
|
||||||
protocolStates*: seq[RootRef]
|
|
||||||
|
|
||||||
EthereumNode = Eth2Node # needed for the definitions in p2p_backends_helpers
|
|
||||||
|
|
||||||
Peer* = ref object
|
|
||||||
network*: Eth2Node
|
|
||||||
id*: PeerID
|
|
||||||
wasDialed*: bool
|
|
||||||
connectionState*: ConnectionState
|
|
||||||
protocolStates*: seq[RootRef]
|
|
||||||
maxInactivityAllowed*: Duration
|
|
||||||
|
|
||||||
ConnectionState* = enum
|
|
||||||
None,
|
|
||||||
Connecting,
|
|
||||||
Connected,
|
|
||||||
Disconnecting,
|
|
||||||
Disconnected
|
|
||||||
|
|
||||||
UntypedResponder = object
|
|
||||||
peer*: Peer
|
|
||||||
stream*: P2PStream
|
|
||||||
|
|
||||||
Responder*[MsgType] = distinct UntypedResponder
|
|
||||||
|
|
||||||
MessageInfo* = object
|
|
||||||
name*: string
|
|
||||||
|
|
||||||
# Private fields:
|
|
||||||
thunk*: ThunkProc
|
|
||||||
libp2pProtocol: string
|
|
||||||
printer*: MessageContentPrinter
|
|
||||||
nextMsgResolver*: NextMsgResolver
|
|
||||||
|
|
||||||
ProtocolInfoObj* = object
|
|
||||||
name*: string
|
|
||||||
messages*: seq[MessageInfo]
|
|
||||||
index*: int # the position of the protocol in the
|
|
||||||
# ordered list of supported protocols
|
|
||||||
|
|
||||||
# Private fields:
|
|
||||||
peerStateInitializer*: PeerStateInitializer
|
|
||||||
networkStateInitializer*: NetworkStateInitializer
|
|
||||||
handshake*: HandshakeStep
|
|
||||||
disconnectHandler*: DisconnectionHandler
|
|
||||||
|
|
||||||
ProtocolInfo* = ptr ProtocolInfoObj
|
|
||||||
|
|
||||||
PeerStateInitializer* = proc(peer: Peer): RootRef {.gcsafe.}
|
|
||||||
NetworkStateInitializer* = proc(network: EthereumNode): RootRef {.gcsafe.}
|
|
||||||
HandshakeStep* = proc(peer: Peer, stream: P2PStream): Future[void] {.gcsafe.}
|
|
||||||
DisconnectionHandler* = proc(peer: Peer): Future[void] {.gcsafe.}
|
|
||||||
ThunkProc* = proc(daemon: DaemonAPI, stream: P2PStream): Future[void] {.gcsafe.}
|
|
||||||
MessageContentPrinter* = proc(msg: pointer): string {.gcsafe.}
|
|
||||||
NextMsgResolver* = proc(msgData: SszReader, future: FutureBase) {.gcsafe.}
|
|
||||||
|
|
||||||
DisconnectionReason* = enum
|
|
||||||
ClientShutDown
|
|
||||||
IrrelevantNetwork
|
|
||||||
FaultOrError
|
|
||||||
|
|
||||||
PeerDisconnected* = object of CatchableError
|
|
||||||
reason*: DisconnectionReason
|
|
||||||
|
|
||||||
TransmissionError* = object of CatchableError
|
|
||||||
|
|
||||||
template `$`*(peer: Peer): string = $peer.id
|
|
||||||
chronicles.formatIt(Peer): $it
|
|
||||||
|
|
||||||
# TODO: These exists only as a compatibility layer between the daemon
|
|
||||||
# APIs and the native LibP2P ones. It won't be necessary once the
|
|
||||||
# daemon is removed.
|
|
||||||
#
|
|
||||||
proc writeAllBytes(stream: P2PStream, bytes: seq[byte]) {.async.} =
|
|
||||||
let sent = await stream.transp.write(bytes)
|
|
||||||
if sent != bytes.len:
|
|
||||||
raise newException(TransmissionError, "Failed to deliver msg bytes")
|
|
||||||
|
|
||||||
template readExactly(stream: P2PStream, dst: pointer, dstLen: int): untyped =
|
|
||||||
readExactly(stream.transp, dst, dstLen)
|
|
||||||
|
|
||||||
template openStream(node: Eth2Node, peer: Peer, protocolId: string): untyped =
|
|
||||||
openStream(node.daemon, peer.id, @[protocolId])
|
|
||||||
|
|
||||||
#
|
|
||||||
# End of compatibility layer
|
|
||||||
|
|
||||||
proc init*(T: type Peer, network: Eth2Node, id: PeerID): Peer {.gcsafe.}
|
|
||||||
|
|
||||||
template remote*(peer: Peer): untyped =
|
|
||||||
# TODO: Can we get a proper address here?
|
|
||||||
peer.id
|
|
||||||
|
|
||||||
proc getPeer*(node: Eth2Node, peerId: PeerID): Peer {.gcsafe.} =
|
|
||||||
result = node.peers.getOrDefault(peerId)
|
|
||||||
if result == nil:
|
|
||||||
result = Peer.init(node, peerId)
|
|
||||||
node.peers[peerId] = result
|
|
||||||
|
|
||||||
proc getPeer*(node: Eth2Node, peerInfo: PeerInfo): Peer =
|
|
||||||
node.getPeer(peerInfo.peer)
|
|
||||||
|
|
||||||
proc peerFromStream(node: Eth2Node, stream: P2PStream): Peer {.gcsafe.} =
|
|
||||||
node.getPeer(stream.peer)
|
|
||||||
|
|
||||||
proc disconnect*(peer: Peer, reason: DisconnectionReason, notifyOtherPeer = false) {.async.} =
|
|
||||||
# TODO: How should we notify the other peer?
|
|
||||||
if peer.connectionState notin {Disconnecting, Disconnected}:
|
|
||||||
peer.connectionState = Disconnecting
|
|
||||||
await peer.network.daemon.disconnect(peer.id)
|
|
||||||
peer.connectionState = Disconnected
|
|
||||||
peer.network.peers.del(peer.id)
|
|
||||||
|
|
||||||
proc safeClose(stream: P2PStream) {.async.} =
|
|
||||||
if P2PStreamFlags.Closed notin stream.flags:
|
|
||||||
await close(stream)
|
|
||||||
|
|
||||||
proc handleIncomingPeer*(peer: Peer) {.inline.} =
|
|
||||||
discard
|
|
||||||
|
|
||||||
include eth/p2p/p2p_backends_helpers
|
|
||||||
include eth/p2p/p2p_tracing
|
|
||||||
include libp2p_backends_common
|
|
||||||
|
|
||||||
proc init*(T: type Eth2Node, daemon: DaemonAPI): Future[T] {.async.} =
|
|
||||||
new result
|
|
||||||
result.daemon = daemon
|
|
||||||
result.daemon.userData = result
|
|
||||||
result.peers = initTable[PeerID, Peer]()
|
|
||||||
|
|
||||||
newSeq result.protocolStates, allProtocols.len
|
|
||||||
for proto in allProtocols:
|
|
||||||
if proto.networkStateInitializer != nil:
|
|
||||||
result.protocolStates[proto.index] = proto.networkStateInitializer(result)
|
|
||||||
|
|
||||||
for msg in proto.messages:
|
|
||||||
if msg.libp2pProtocol.len > 0 and msg.thunk != nil:
|
|
||||||
await daemon.addHandler(@[msg.libp2pProtocol], msg.thunk)
|
|
||||||
|
|
||||||
proc init*(T: type Peer, network: Eth2Node, id: PeerID): Peer =
|
|
||||||
new result
|
|
||||||
result.id = id
|
|
||||||
result.network = network
|
|
||||||
result.connectionState = Connected
|
|
||||||
result.maxInactivityAllowed = 15.minutes # TODO: Read this from the config
|
|
||||||
newSeq result.protocolStates, allProtocols.len
|
|
||||||
for i in 0 ..< allProtocols.len:
|
|
||||||
let proto = allProtocols[i]
|
|
||||||
if proto.peerStateInitializer != nil:
|
|
||||||
result.protocolStates[i] = proto.peerStateInitializer(result)
|
|
||||||
|
|
||||||
proc registerMsg(protocol: ProtocolInfo,
|
|
||||||
name: string,
|
|
||||||
thunk: ThunkProc,
|
|
||||||
libp2pProtocol: string,
|
|
||||||
printer: MessageContentPrinter) =
|
|
||||||
protocol.messages.add MessageInfo(name: name,
|
|
||||||
thunk: thunk,
|
|
||||||
libp2pProtocol: libp2pProtocol,
|
|
||||||
printer: printer)
|
|
||||||
|
|
||||||
proc p2pProtocolBackendImpl*(p: P2PProtocol): Backend =
|
|
||||||
var
|
|
||||||
Format = ident "SSZ"
|
|
||||||
Responder = bindSym "Responder"
|
|
||||||
DaemonAPI = bindSym "DaemonAPI"
|
|
||||||
P2PStream = ident "P2PStream"
|
|
||||||
OutputStream = bindSym "OutputStream"
|
|
||||||
Peer = bindSym "Peer"
|
|
||||||
Eth2Node = bindSym "Eth2Node"
|
|
||||||
messagePrinter = bindSym "messagePrinter"
|
|
||||||
milliseconds = bindSym "milliseconds"
|
|
||||||
registerMsg = bindSym "registerMsg"
|
|
||||||
initProtocol = bindSym "initProtocol"
|
|
||||||
bindSymOp = bindSym "bindSym"
|
|
||||||
errVar = ident "err"
|
|
||||||
msgVar = ident "msg"
|
|
||||||
msgBytesVar = ident "msgBytes"
|
|
||||||
daemonVar = ident "daemon"
|
|
||||||
await = ident "await"
|
|
||||||
callUserHandler = ident "callUserHandler"
|
|
||||||
|
|
||||||
p.useRequestIds = false
|
|
||||||
p.useSingleRecordInlining = true
|
|
||||||
|
|
||||||
new result
|
|
||||||
|
|
||||||
result.PeerType = Peer
|
|
||||||
result.NetworkType = Eth2Node
|
|
||||||
result.registerProtocol = bindSym "registerProtocol"
|
|
||||||
result.setEventHandlers = bindSym "setEventHandlers"
|
|
||||||
result.SerializationFormat = Format
|
|
||||||
result.ResponderType = Responder
|
|
||||||
|
|
||||||
result.afterProtocolInit = proc (p: P2PProtocol) =
|
|
||||||
p.onPeerConnected.params.add newIdentDefs(streamVar, P2PStream)
|
|
||||||
|
|
||||||
result.implementMsg = proc (msg: Message) =
|
|
||||||
let
|
|
||||||
protocol = msg.protocol
|
|
||||||
msgName = $msg.ident
|
|
||||||
msgNameLit = newLit msgName
|
|
||||||
MsgRecName = msg.recName
|
|
||||||
MsgStrongRecName = msg.strongRecName
|
|
||||||
|
|
||||||
if msg.procDef.body.kind != nnkEmpty and msg.kind == msgRequest:
|
|
||||||
# Request procs need an extra param - the stream where the response
|
|
||||||
# should be written:
|
|
||||||
msg.userHandler.params.insert(2, newIdentDefs(streamVar, P2PStream))
|
|
||||||
msg.initResponderCall.add streamVar
|
|
||||||
|
|
||||||
##
|
|
||||||
## Implemenmt Thunk
|
|
||||||
##
|
|
||||||
var thunkName: NimNode
|
|
||||||
|
|
||||||
if msg.userHandler != nil:
|
|
||||||
thunkName = ident(msgName & "_thunk")
|
|
||||||
let userHandlerCall = msg.genUserHandlerCall(msgVar, [peerVar, streamVar])
|
|
||||||
msg.defineThunk quote do:
|
|
||||||
template `callUserHandler`(`peerVar`: `Peer`,
|
|
||||||
`streamVar`: `P2PStream`,
|
|
||||||
`msgVar`: `MsgRecName`): untyped =
|
|
||||||
`userHandlerCall`
|
|
||||||
|
|
||||||
proc `thunkName`(`daemonVar`: `DaemonAPI`,
|
|
||||||
`streamVar`: `P2PStream`): Future[void] {.gcsafe.} =
|
|
||||||
return handleIncomingStream(`Eth2Node`(`daemonVar`.userData), `streamVar`,
|
|
||||||
`MsgStrongRecName`, `Format`)
|
|
||||||
else:
|
|
||||||
thunkName = newNilLit()
|
|
||||||
|
|
||||||
##
|
|
||||||
## Implement Senders and Handshake
|
|
||||||
##
|
|
||||||
if msg.kind == msgHandshake:
|
|
||||||
macros.error "Handshake messages are not supported in LibP2P protocols"
|
|
||||||
else:
|
|
||||||
var sendProc = msg.createSendProc()
|
|
||||||
implementSendProcBody sendProc
|
|
||||||
|
|
||||||
protocol.outProcRegistrations.add(
|
|
||||||
newCall(registerMsg,
|
|
||||||
protocol.protocolInfoVar,
|
|
||||||
msgNameLit,
|
|
||||||
thunkName,
|
|
||||||
getRequestProtoName(msg.procDef),
|
|
||||||
newTree(nnkBracketExpr, messagePrinter, MsgRecName)))
|
|
||||||
|
|
||||||
result.implementProtocolInit = proc (p: P2PProtocol): NimNode =
|
|
||||||
return newCall(initProtocol, newLit(p.name), p.peerInit, p.netInit)
|
|
||||||
|
|
|
@ -33,56 +33,39 @@ proc fetchAncestorBlocksFromPeer(
|
||||||
debug "Error while fetching ancestor blocks",
|
debug "Error while fetching ancestor blocks",
|
||||||
err = err.msg, root = rec.root, peer = peer
|
err = err.msg, root = rec.root, peer = peer
|
||||||
|
|
||||||
when networkBackend == libp2p:
|
proc fetchAncestorBlocksFromNetwork(
|
||||||
|
network: Eth2Node,
|
||||||
|
rec: FetchRecord,
|
||||||
|
responseHandler: FetchAncestorsResponseHandler) {.async.} =
|
||||||
|
var peer: Peer
|
||||||
|
try:
|
||||||
|
peer = await network.peerPool.acquire()
|
||||||
|
let blocks = await peer.beaconBlocksByRoot([rec.root])
|
||||||
|
if blocks.isSome:
|
||||||
|
for b in blocks.get:
|
||||||
|
responseHandler(b)
|
||||||
|
except CatchableError as err:
|
||||||
|
debug "Error while fetching ancestor blocks",
|
||||||
|
err = err.msg, root = rec.root, peer = peer
|
||||||
|
finally:
|
||||||
|
if not(isNil(peer)):
|
||||||
|
network.peerPool.release(peer)
|
||||||
|
|
||||||
proc fetchAncestorBlocksFromNetwork(
|
proc fetchAncestorBlocks*(requestManager: RequestManager,
|
||||||
network: Eth2Node,
|
|
||||||
rec: FetchRecord,
|
|
||||||
responseHandler: FetchAncestorsResponseHandler) {.async.} =
|
|
||||||
var peer: Peer
|
|
||||||
try:
|
|
||||||
peer = await network.peerPool.acquire()
|
|
||||||
let blocks = await peer.beaconBlocksByRoot([rec.root])
|
|
||||||
if blocks.isSome:
|
|
||||||
for b in blocks.get:
|
|
||||||
responseHandler(b)
|
|
||||||
except CatchableError as err:
|
|
||||||
debug "Error while fetching ancestor blocks",
|
|
||||||
err = err.msg, root = rec.root, peer = peer
|
|
||||||
finally:
|
|
||||||
if not(isNil(peer)):
|
|
||||||
network.peerPool.release(peer)
|
|
||||||
|
|
||||||
proc fetchAncestorBlocks*(requestManager: RequestManager,
|
|
||||||
roots: seq[FetchRecord],
|
|
||||||
responseHandler: FetchAncestorsResponseHandler) =
|
|
||||||
# TODO: we could have some fancier logic here:
|
|
||||||
#
|
|
||||||
# * Keeps track of what was requested
|
|
||||||
# (this would give a little bit of time for the asked peer to respond)
|
|
||||||
#
|
|
||||||
# * Keep track of the average latency of each peer
|
|
||||||
# (we can give priority to peers with better latency)
|
|
||||||
#
|
|
||||||
const ParallelRequests = 2
|
|
||||||
|
|
||||||
for i in 0 ..< ParallelRequests:
|
|
||||||
traceAsyncErrors fetchAncestorBlocksFromNetwork(requestManager.network,
|
|
||||||
roots.sample(),
|
|
||||||
responseHandler)
|
|
||||||
elif networkBackend == libp2pDaemon:
|
|
||||||
proc fetchAncestorBlocks*(requestManager: RequestManager,
|
|
||||||
roots: seq[FetchRecord],
|
roots: seq[FetchRecord],
|
||||||
responseHandler: FetchAncestorsResponseHandler) =
|
responseHandler: FetchAncestorsResponseHandler) =
|
||||||
# TODO: we could have some fancier logic here:
|
# TODO: we could have some fancier logic here:
|
||||||
#
|
#
|
||||||
# * Keeps track of what was requested
|
# * Keeps track of what was requested
|
||||||
# (this would give a little bit of time for the asked peer to respond)
|
# (this would give a little bit of time for the asked peer to respond)
|
||||||
#
|
#
|
||||||
# * Keep track of the average latency of each peer
|
# * Keep track of the average latency of each peer
|
||||||
# (we can give priority to peers with better latency)
|
# (we can give priority to peers with better latency)
|
||||||
#
|
#
|
||||||
const ParallelRequests = 2
|
const ParallelRequests = 2
|
||||||
|
|
||||||
|
for i in 0 ..< ParallelRequests:
|
||||||
|
traceAsyncErrors fetchAncestorBlocksFromNetwork(requestManager.network,
|
||||||
|
roots.sample(),
|
||||||
|
responseHandler)
|
||||||
|
|
||||||
for peer in requestManager.network.randomPeers(ParallelRequests, BeaconSync):
|
|
||||||
traceAsyncErrors peer.fetchAncestorBlocksFromPeer(roots.sample(), responseHandler)
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ import
|
||||||
./crypto, ./datatypes, ./digest, ./helpers, ./validator,
|
./crypto, ./datatypes, ./digest, ./helpers, ./validator,
|
||||||
../../nbench/bench_lab
|
../../nbench/bench_lab
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#is_valid_merkle_branch
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#is_valid_merkle_branch
|
||||||
func is_valid_merkle_branch*(leaf: Eth2Digest, branch: openarray[Eth2Digest], depth: uint64, index: uint64, root: Eth2Digest): bool {.nbench.}=
|
func is_valid_merkle_branch*(leaf: Eth2Digest, branch: openarray[Eth2Digest], depth: uint64, index: uint64, root: Eth2Digest): bool {.nbench.}=
|
||||||
## Check if ``leaf`` at ``index`` verifies against the Merkle ``root`` and
|
## Check if ``leaf`` at ``index`` verifies against the Merkle ``root`` and
|
||||||
## ``branch``.
|
## ``branch``.
|
||||||
|
@ -30,13 +30,13 @@ func is_valid_merkle_branch*(leaf: Eth2Digest, branch: openarray[Eth2Digest], de
|
||||||
value = eth2hash(buf)
|
value = eth2hash(buf)
|
||||||
value == root
|
value == root
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#increase_balance
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#increase_balance
|
||||||
func increase_balance*(
|
func increase_balance*(
|
||||||
state: var BeaconState, index: ValidatorIndex, delta: Gwei) =
|
state: var BeaconState, index: ValidatorIndex, delta: Gwei) =
|
||||||
# Increase the validator balance at index ``index`` by ``delta``.
|
# Increase the validator balance at index ``index`` by ``delta``.
|
||||||
state.balances[index] += delta
|
state.balances[index] += delta
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#decrease_balance
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#decrease_balance
|
||||||
func decrease_balance*(
|
func decrease_balance*(
|
||||||
state: var BeaconState, index: ValidatorIndex, delta: Gwei) =
|
state: var BeaconState, index: ValidatorIndex, delta: Gwei) =
|
||||||
## Decrease the validator balance at index ``index`` by ``delta``, with
|
## Decrease the validator balance at index ``index`` by ``delta``, with
|
||||||
|
@ -47,7 +47,7 @@ func decrease_balance*(
|
||||||
else:
|
else:
|
||||||
state.balances[index] - delta
|
state.balances[index] - delta
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.4/specs/core/0_beacon-chain.md#deposits
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#deposits
|
||||||
proc process_deposit*(
|
proc process_deposit*(
|
||||||
state: var BeaconState, deposit: Deposit, flags: UpdateFlags = {}): bool {.nbench.}=
|
state: var BeaconState, deposit: Deposit, flags: UpdateFlags = {}): bool {.nbench.}=
|
||||||
# Process an Eth1 deposit, registering a validator or increasing its balance.
|
# Process an Eth1 deposit, registering a validator or increasing its balance.
|
||||||
|
@ -56,7 +56,7 @@ proc process_deposit*(
|
||||||
if skipMerkleValidation notin flags and not is_valid_merkle_branch(
|
if skipMerkleValidation notin flags and not is_valid_merkle_branch(
|
||||||
hash_tree_root(deposit.data),
|
hash_tree_root(deposit.data),
|
||||||
deposit.proof,
|
deposit.proof,
|
||||||
DEPOSIT_CONTRACT_TREE_DEPTH + 1,
|
DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the `List` length mix-in
|
||||||
state.eth1_deposit_index,
|
state.eth1_deposit_index,
|
||||||
state.eth1_data.deposit_root,
|
state.eth1_data.deposit_root,
|
||||||
):
|
):
|
||||||
|
@ -72,8 +72,17 @@ proc process_deposit*(
|
||||||
index = validator_pubkeys.find(pubkey)
|
index = validator_pubkeys.find(pubkey)
|
||||||
|
|
||||||
if index == -1:
|
if index == -1:
|
||||||
# Verify the deposit signature (proof of possession)
|
# Verify the deposit signature (proof of possession) which is not checked
|
||||||
let domain = compute_domain(DOMAIN_DEPOSIT)
|
# by the deposit contract
|
||||||
|
|
||||||
|
# Fork-agnostic domain since deposits are valid across forks
|
||||||
|
#
|
||||||
|
# TODO zcli/zrnt does use the GENESIS_FORK_VERSION which can
|
||||||
|
# vary between minimal/mainnet, though, despite the comment,
|
||||||
|
# which is copied verbatim from the eth2 beacon chain spec.
|
||||||
|
# https://github.com/protolambda/zrnt/blob/v0.11.0/eth2/phase0/kickstart.go#L58
|
||||||
|
let domain = compute_domain(DOMAIN_DEPOSIT, GENESIS_FORK_VERSION)
|
||||||
|
|
||||||
let signing_root = compute_signing_root(deposit.getDepositMessage, domain)
|
let signing_root = compute_signing_root(deposit.getDepositMessage, domain)
|
||||||
if skipBLSValidation notin flags and not bls_verify(
|
if skipBLSValidation notin flags and not bls_verify(
|
||||||
pubkey, signing_root.data,
|
pubkey, signing_root.data,
|
||||||
|
@ -98,13 +107,13 @@ proc process_deposit*(
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#compute_activation_exit_epoch
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#compute_activation_exit_epoch
|
||||||
func compute_activation_exit_epoch(epoch: Epoch): Epoch =
|
func compute_activation_exit_epoch(epoch: Epoch): Epoch =
|
||||||
## Return the epoch during which validator activations and exits initiated in
|
## Return the epoch during which validator activations and exits initiated in
|
||||||
## ``epoch`` take effect.
|
## ``epoch`` take effect.
|
||||||
epoch + 1 + MAX_SEED_LOOKAHEAD
|
epoch + 1 + MAX_SEED_LOOKAHEAD
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#get_validator_churn_limit
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#get_validator_churn_limit
|
||||||
func get_validator_churn_limit(state: BeaconState): uint64 =
|
func get_validator_churn_limit(state: BeaconState): uint64 =
|
||||||
# Return the validator churn limit for the current epoch.
|
# Return the validator churn limit for the current epoch.
|
||||||
let active_validator_indices =
|
let active_validator_indices =
|
||||||
|
@ -112,7 +121,7 @@ func get_validator_churn_limit(state: BeaconState): uint64 =
|
||||||
max(MIN_PER_EPOCH_CHURN_LIMIT,
|
max(MIN_PER_EPOCH_CHURN_LIMIT,
|
||||||
len(active_validator_indices) div CHURN_LIMIT_QUOTIENT).uint64
|
len(active_validator_indices) div CHURN_LIMIT_QUOTIENT).uint64
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#initiate_validator_exit
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#initiate_validator_exit
|
||||||
func initiate_validator_exit*(state: var BeaconState,
|
func initiate_validator_exit*(state: var BeaconState,
|
||||||
index: ValidatorIndex) =
|
index: ValidatorIndex) =
|
||||||
# Initiate the exit of the validator with index ``index``.
|
# Initiate the exit of the validator with index ``index``.
|
||||||
|
@ -184,8 +193,8 @@ proc slash_validator*(state: var BeaconState, slashed_index: ValidatorIndex,
|
||||||
increase_balance(
|
increase_balance(
|
||||||
state, whistleblower_index, whistleblowing_reward - proposer_reward)
|
state, whistleblower_index, whistleblowing_reward - proposer_reward)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#genesis
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#genesis
|
||||||
func initialize_beacon_state_from_eth1*(
|
proc initialize_beacon_state_from_eth1*(
|
||||||
eth1_block_hash: Eth2Digest,
|
eth1_block_hash: Eth2Digest,
|
||||||
eth1_timestamp: uint64,
|
eth1_timestamp: uint64,
|
||||||
deposits: openArray[Deposit],
|
deposits: openArray[Deposit],
|
||||||
|
@ -208,6 +217,10 @@ func initialize_beacon_state_from_eth1*(
|
||||||
|
|
||||||
const SECONDS_PER_DAY = uint64(60*60*24)
|
const SECONDS_PER_DAY = uint64(60*60*24)
|
||||||
var state = BeaconState(
|
var state = BeaconState(
|
||||||
|
fork: Fork(
|
||||||
|
previous_version: GENESIS_FORK_VERSION,
|
||||||
|
current_version: GENESIS_FORK_VERSION,
|
||||||
|
epoch: GENESIS_EPOCH),
|
||||||
genesis_time:
|
genesis_time:
|
||||||
eth1_timestamp + 2'u64 * SECONDS_PER_DAY -
|
eth1_timestamp + 2'u64 * SECONDS_PER_DAY -
|
||||||
(eth1_timestamp mod SECONDS_PER_DAY),
|
(eth1_timestamp mod SECONDS_PER_DAY),
|
||||||
|
@ -248,7 +261,8 @@ func initialize_beacon_state_from_eth1*(
|
||||||
validator.activation_epoch = GENESIS_EPOCH
|
validator.activation_epoch = GENESIS_EPOCH
|
||||||
|
|
||||||
# Set genesis validators root for domain separation and chain versioning
|
# Set genesis validators root for domain separation and chain versioning
|
||||||
state.genesis_validators_root = hash_tree_root(state.validators)
|
state.genesis_validators_root =
|
||||||
|
hash_tree_root(sszList(state.validators, VALIDATOR_REGISTRY_LIMIT))
|
||||||
|
|
||||||
state
|
state
|
||||||
|
|
||||||
|
@ -272,7 +286,7 @@ func get_initial_beacon_block*(state: BeaconState): SignedBeaconBlock =
|
||||||
# parent_root, randao_reveal, eth1_data, signature, and body automatically
|
# parent_root, randao_reveal, eth1_data, signature, and body automatically
|
||||||
# initialized to default values.
|
# initialized to default values.
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#get_block_root_at_slot
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#get_block_root_at_slot
|
||||||
func get_block_root_at_slot*(state: BeaconState,
|
func get_block_root_at_slot*(state: BeaconState,
|
||||||
slot: Slot): Eth2Digest =
|
slot: Slot): Eth2Digest =
|
||||||
# Return the block root at a recent ``slot``.
|
# Return the block root at a recent ``slot``.
|
||||||
|
@ -281,29 +295,29 @@ func get_block_root_at_slot*(state: BeaconState,
|
||||||
doAssert slot < state.slot
|
doAssert slot < state.slot
|
||||||
state.block_roots[slot mod SLOTS_PER_HISTORICAL_ROOT]
|
state.block_roots[slot mod SLOTS_PER_HISTORICAL_ROOT]
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#get_block_root
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#get_block_root
|
||||||
func get_block_root*(state: BeaconState, epoch: Epoch): Eth2Digest =
|
func get_block_root*(state: BeaconState, epoch: Epoch): Eth2Digest =
|
||||||
# Return the block root at the start of a recent ``epoch``.
|
# Return the block root at the start of a recent ``epoch``.
|
||||||
get_block_root_at_slot(state, compute_start_slot_at_epoch(epoch))
|
get_block_root_at_slot(state, compute_start_slot_at_epoch(epoch))
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#get_total_balance
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#get_total_balance
|
||||||
func get_total_balance*(state: BeaconState, validators: auto): Gwei =
|
func get_total_balance*(state: BeaconState, validators: auto): Gwei =
|
||||||
## Return the combined effective balance of the ``indices``. (1 Gwei minimum
|
## Return the combined effective balance of the ``indices``.
|
||||||
## to avoid divisions by zero.)
|
## ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero.
|
||||||
## Math safe up to ~10B ETH, afterwhich this overflows uint64.
|
## Math safe up to ~10B ETH, afterwhich this overflows uint64.
|
||||||
max(1'u64,
|
max(EFFECTIVE_BALANCE_INCREMENT,
|
||||||
foldl(validators, a + state.validators[b].effective_balance, 0'u64)
|
foldl(validators, a + state.validators[b].effective_balance, 0'u64)
|
||||||
)
|
)
|
||||||
|
|
||||||
# XXX: Move to state_transition_epoch.nim?
|
# XXX: Move to state_transition_epoch.nim?
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#is_eligible_for_activation_queue
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#is_eligible_for_activation_queue
|
||||||
func is_eligible_for_activation_queue(validator: Validator): bool =
|
func is_eligible_for_activation_queue(validator: Validator): bool =
|
||||||
# Check if ``validator`` is eligible to be placed into the activation queue.
|
# Check if ``validator`` is eligible to be placed into the activation queue.
|
||||||
validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH and
|
validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH and
|
||||||
validator.effective_balance == MAX_EFFECTIVE_BALANCE
|
validator.effective_balance == MAX_EFFECTIVE_BALANCE
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#is_eligible_for_activation
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#is_eligible_for_activation
|
||||||
func is_eligible_for_activation(state: BeaconState, validator: Validator):
|
func is_eligible_for_activation(state: BeaconState, validator: Validator):
|
||||||
bool =
|
bool =
|
||||||
# Check if ``validator`` is eligible for activation.
|
# Check if ``validator`` is eligible for activation.
|
||||||
|
@ -365,7 +379,7 @@ proc process_registry_updates*(state: var BeaconState) {.nbench.}=
|
||||||
validator.activation_epoch =
|
validator.activation_epoch =
|
||||||
compute_activation_exit_epoch(get_current_epoch(state))
|
compute_activation_exit_epoch(get_current_epoch(state))
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.4/specs/core/0_beacon-chain.md#is_valid_indexed_attestation
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#is_valid_indexed_attestation
|
||||||
proc is_valid_indexed_attestation*(
|
proc is_valid_indexed_attestation*(
|
||||||
state: BeaconState, indexed_attestation: IndexedAttestation,
|
state: BeaconState, indexed_attestation: IndexedAttestation,
|
||||||
flags: UpdateFlags): bool =
|
flags: UpdateFlags): bool =
|
||||||
|
@ -381,7 +395,6 @@ proc is_valid_indexed_attestation*(
|
||||||
return false
|
return false
|
||||||
|
|
||||||
# Verify indices are sorted and unique
|
# Verify indices are sorted and unique
|
||||||
# TODO but why? this is a local artifact
|
|
||||||
if indices != sorted(indices, system.cmp):
|
if indices != sorted(indices, system.cmp):
|
||||||
notice "indexed attestation: indices not sorted"
|
notice "indexed attestation: indices not sorted"
|
||||||
return false
|
return false
|
||||||
|
@ -399,7 +412,7 @@ proc is_valid_indexed_attestation*(
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#get_attesting_indices
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#get_attesting_indices
|
||||||
func get_attesting_indices*(state: BeaconState,
|
func get_attesting_indices*(state: BeaconState,
|
||||||
data: AttestationData,
|
data: AttestationData,
|
||||||
bits: CommitteeValidatorsBits,
|
bits: CommitteeValidatorsBits,
|
||||||
|
@ -413,7 +426,7 @@ func get_attesting_indices*(state: BeaconState,
|
||||||
result.incl index
|
result.incl index
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#get_indexed_attestation
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#get_indexed_attestation
|
||||||
func get_indexed_attestation(state: BeaconState, attestation: Attestation,
|
func get_indexed_attestation*(state: BeaconState, attestation: Attestation,
|
||||||
stateCache: var StateCache): IndexedAttestation =
|
stateCache: var StateCache): IndexedAttestation =
|
||||||
# Return the indexed attestation corresponding to ``attestation``.
|
# Return the indexed attestation corresponding to ``attestation``.
|
||||||
let
|
let
|
||||||
|
@ -421,14 +434,6 @@ func get_indexed_attestation(state: BeaconState, attestation: Attestation,
|
||||||
get_attesting_indices(
|
get_attesting_indices(
|
||||||
state, attestation.data, attestation.aggregation_bits, stateCache)
|
state, attestation.data, attestation.aggregation_bits, stateCache)
|
||||||
|
|
||||||
## TODO No fundamental reason to do so many type conversions
|
|
||||||
## verify_indexed_attestation checks for sortedness but it's
|
|
||||||
## entirely a local artifact, seemingly; networking uses the
|
|
||||||
## Attestation data structure, which can't be unsorted. That
|
|
||||||
## the conversion here otherwise needs sorting is due to the
|
|
||||||
## usage of HashSet -- order only matters in one place (that
|
|
||||||
## 0.6.3 highlights and explicates) except in that the spec,
|
|
||||||
## for no obvious reason, verifies it.
|
|
||||||
IndexedAttestation(
|
IndexedAttestation(
|
||||||
attesting_indices:
|
attesting_indices:
|
||||||
sorted(mapIt(attesting_indices.toSeq, it.uint64), system.cmp),
|
sorted(mapIt(attesting_indices.toSeq, it.uint64), system.cmp),
|
||||||
|
@ -436,7 +441,7 @@ func get_indexed_attestation(state: BeaconState, attestation: Attestation,
|
||||||
signature: attestation.signature
|
signature: attestation.signature
|
||||||
)
|
)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#attestations
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#attestations
|
||||||
proc check_attestation*(
|
proc check_attestation*(
|
||||||
state: BeaconState, attestation: Attestation, flags: UpdateFlags,
|
state: BeaconState, attestation: Attestation, flags: UpdateFlags,
|
||||||
stateCache: var StateCache): bool =
|
stateCache: var StateCache): bool =
|
||||||
|
@ -566,7 +571,7 @@ func makeAttestationData*(
|
||||||
|
|
||||||
doAssert slot.compute_epoch_at_slot == current_epoch
|
doAssert slot.compute_epoch_at_slot == current_epoch
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/validator.md#attestation-data
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#attestation-data
|
||||||
AttestationData(
|
AttestationData(
|
||||||
slot: slot,
|
slot: slot,
|
||||||
index: committee_index,
|
index: committee_index,
|
||||||
|
|
|
@ -104,7 +104,7 @@ func pubKey*(privkey: ValidatorPrivKey): ValidatorPubKey =
|
||||||
else:
|
else:
|
||||||
privkey.getKey
|
privkey.getKey
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#bls-signatures
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#bls-signatures
|
||||||
func aggregate*[T](values: openarray[ValidatorSig]): ValidatorSig =
|
func aggregate*[T](values: openarray[ValidatorSig]): ValidatorSig =
|
||||||
## Aggregate arrays of sequences of Validator Signatures
|
## Aggregate arrays of sequences of Validator Signatures
|
||||||
## This assumes that they are real signatures
|
## This assumes that they are real signatures
|
||||||
|
@ -260,6 +260,10 @@ func initFromBytes*(val: var ValidatorPrivKey, bytes: openarray[byte]) {.inline.
|
||||||
func fromBytes[T](R: type BlsValue[T], bytes: openarray[byte]): R {.inline.}=
|
func fromBytes[T](R: type BlsValue[T], bytes: openarray[byte]): R {.inline.}=
|
||||||
result.initFromBytes(bytes)
|
result.initFromBytes(bytes)
|
||||||
|
|
||||||
|
func fromBytes[T](R: var BlsValue[T], bytes: openarray[byte]) {.inline.}=
|
||||||
|
# This version is only to support tests/test_interop.nim
|
||||||
|
R.initFromBytes(bytes)
|
||||||
|
|
||||||
func fromHex*[T](R: var BlsValue[T], hexStr: string) {.inline.} =
|
func fromHex*[T](R: var BlsValue[T], hexStr: string) {.inline.} =
|
||||||
## Initialize a BLSValue from its hex representation
|
## Initialize a BLSValue from its hex representation
|
||||||
R.fromBytes(hexStr.hexToSeqByte())
|
R.fromBytes(hexStr.hexToSeqByte())
|
||||||
|
|
|
@ -57,7 +57,7 @@ else:
|
||||||
loadCustomPreset const_preset
|
loadCustomPreset const_preset
|
||||||
|
|
||||||
const
|
const
|
||||||
SPEC_VERSION* = "0.11.0" ## \
|
SPEC_VERSION* = "0.11.1" ## \
|
||||||
## Spec version we're aiming to be compatible with, right now
|
## Spec version we're aiming to be compatible with, right now
|
||||||
|
|
||||||
GENESIS_EPOCH* = (GENESIS_SLOT.uint64 div SLOTS_PER_EPOCH).Epoch ##\
|
GENESIS_EPOCH* = (GENESIS_SLOT.uint64 div SLOTS_PER_EPOCH).Epoch ##\
|
||||||
|
@ -74,6 +74,9 @@ const
|
||||||
# TODO: This needs revisiting.
|
# TODO: This needs revisiting.
|
||||||
# Why was the validator WITHDRAWAL_PERIOD altered in the spec?
|
# Why was the validator WITHDRAWAL_PERIOD altered in the spec?
|
||||||
|
|
||||||
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/p2p-interface.md#configuration
|
||||||
|
ATTESTATION_PROPAGATION_SLOT_RANGE* = 32
|
||||||
|
|
||||||
template maxSize*(n: int) {.pragma.}
|
template maxSize*(n: int) {.pragma.}
|
||||||
|
|
||||||
type
|
type
|
||||||
|
@ -81,7 +84,7 @@ type
|
||||||
|
|
||||||
# Domains
|
# Domains
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#domain-types
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#domain-types
|
||||||
DomainType* = enum
|
DomainType* = enum
|
||||||
DOMAIN_BEACON_PROPOSER = 0
|
DOMAIN_BEACON_PROPOSER = 0
|
||||||
DOMAIN_BEACON_ATTESTER = 1
|
DOMAIN_BEACON_ATTESTER = 1
|
||||||
|
@ -99,7 +102,7 @@ type
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase1/custody-game.md#signature-domain-types
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase1/custody-game.md#signature-domain-types
|
||||||
DOMAIN_CUSTODY_BIT_SLASHING = 0x83
|
DOMAIN_CUSTODY_BIT_SLASHING = 0x83
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#custom-types
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#custom-types
|
||||||
Domain* = array[32, byte]
|
Domain* = array[32, byte]
|
||||||
|
|
||||||
# https://github.com/nim-lang/Nim/issues/574 and be consistent across
|
# https://github.com/nim-lang/Nim/issues/574 and be consistent across
|
||||||
|
@ -114,17 +117,17 @@ type
|
||||||
|
|
||||||
BitList*[maxLen: static int] = distinct BitSeq
|
BitList*[maxLen: static int] = distinct BitSeq
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#proposerslashing
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#proposerslashing
|
||||||
ProposerSlashing* = object
|
ProposerSlashing* = object
|
||||||
signed_header_1*: SignedBeaconBlockHeader
|
signed_header_1*: SignedBeaconBlockHeader
|
||||||
signed_header_2*: SignedBeaconBlockHeader
|
signed_header_2*: SignedBeaconBlockHeader
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#attesterslashing
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#attesterslashing
|
||||||
AttesterSlashing* = object
|
AttesterSlashing* = object
|
||||||
attestation_1*: IndexedAttestation
|
attestation_1*: IndexedAttestation
|
||||||
attestation_2*: IndexedAttestation
|
attestation_2*: IndexedAttestation
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#indexedattestation
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#indexedattestation
|
||||||
IndexedAttestation* = object
|
IndexedAttestation* = object
|
||||||
# TODO ValidatorIndex, but that doesn't serialize properly
|
# TODO ValidatorIndex, but that doesn't serialize properly
|
||||||
attesting_indices*: List[uint64, MAX_VALIDATORS_PER_COMMITTEE]
|
attesting_indices*: List[uint64, MAX_VALIDATORS_PER_COMMITTEE]
|
||||||
|
@ -133,24 +136,24 @@ type
|
||||||
|
|
||||||
CommitteeValidatorsBits* = BitList[MAX_VALIDATORS_PER_COMMITTEE]
|
CommitteeValidatorsBits* = BitList[MAX_VALIDATORS_PER_COMMITTEE]
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#attestation
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#attestation
|
||||||
Attestation* = object
|
Attestation* = object
|
||||||
aggregation_bits*: CommitteeValidatorsBits
|
aggregation_bits*: CommitteeValidatorsBits
|
||||||
data*: AttestationData
|
data*: AttestationData
|
||||||
signature*: ValidatorSig
|
signature*: ValidatorSig
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#forkdata
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#forkdata
|
||||||
ForkData* = object
|
ForkData* = object
|
||||||
# TODO: Spec introduced an alias for Version = array[4, byte]
|
# TODO: Spec introduced an alias for Version = array[4, byte]
|
||||||
current_version*: array[4, byte]
|
current_version*: array[4, byte]
|
||||||
genesis_validators_root*: Eth2Digest
|
genesis_validators_root*: Eth2Digest
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#checkpoint
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#checkpoint
|
||||||
Checkpoint* = object
|
Checkpoint* = object
|
||||||
epoch*: Epoch
|
epoch*: Epoch
|
||||||
root*: Eth2Digest
|
root*: Eth2Digest
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#AttestationData
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#AttestationData
|
||||||
AttestationData* = object
|
AttestationData* = object
|
||||||
slot*: Slot
|
slot*: Slot
|
||||||
index*: uint64
|
index*: uint64
|
||||||
|
@ -162,34 +165,34 @@ type
|
||||||
source*: Checkpoint
|
source*: Checkpoint
|
||||||
target*: Checkpoint
|
target*: Checkpoint
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#deposit
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#deposit
|
||||||
Deposit* = object
|
Deposit* = object
|
||||||
proof*: array[DEPOSIT_CONTRACT_TREE_DEPTH + 1, Eth2Digest] ##\
|
proof*: array[DEPOSIT_CONTRACT_TREE_DEPTH + 1, Eth2Digest] ##\
|
||||||
## Merkle path to deposit root
|
## Merkle path to deposit root
|
||||||
|
|
||||||
data*: DepositData
|
data*: DepositData
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#depositmessage
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#depositmessage
|
||||||
DepositMessage* = object
|
DepositMessage* = object
|
||||||
pubkey*: ValidatorPubKey
|
pubkey*: ValidatorPubKey
|
||||||
withdrawal_credentials*: Eth2Digest
|
withdrawal_credentials*: Eth2Digest
|
||||||
amount*: Gwei
|
amount*: Gwei
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#depositdata
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#depositdata
|
||||||
DepositData* = object
|
DepositData* = object
|
||||||
pubkey*: ValidatorPubKey
|
pubkey*: ValidatorPubKey
|
||||||
withdrawal_credentials*: Eth2Digest
|
withdrawal_credentials*: Eth2Digest
|
||||||
amount*: uint64
|
amount*: uint64
|
||||||
signature*: ValidatorSig # Signing over DepositMessage
|
signature*: ValidatorSig # Signing over DepositMessage
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#voluntaryexit
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#voluntaryexit
|
||||||
VoluntaryExit* = object
|
VoluntaryExit* = object
|
||||||
epoch*: Epoch ##\
|
epoch*: Epoch ##\
|
||||||
## Earliest epoch when voluntary exit can be processed
|
## Earliest epoch when voluntary exit can be processed
|
||||||
|
|
||||||
validator_index*: uint64
|
validator_index*: uint64
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#beaconblock
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#beaconblock
|
||||||
BeaconBlock* = object
|
BeaconBlock* = object
|
||||||
## For each slot, a proposer is chosen from the validator pool to propose
|
## For each slot, a proposer is chosen from the validator pool to propose
|
||||||
## a new block. Once the block as been proposed, it is transmitted to
|
## a new block. Once the block as been proposed, it is transmitted to
|
||||||
|
@ -208,7 +211,7 @@ type
|
||||||
|
|
||||||
body*: BeaconBlockBody
|
body*: BeaconBlockBody
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#beaconblockheader
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#beaconblockheader
|
||||||
BeaconBlockHeader* = object
|
BeaconBlockHeader* = object
|
||||||
slot*: Slot
|
slot*: Slot
|
||||||
proposer_index*: uint64
|
proposer_index*: uint64
|
||||||
|
@ -216,7 +219,7 @@ type
|
||||||
state_root*: Eth2Digest
|
state_root*: Eth2Digest
|
||||||
body_root*: Eth2Digest
|
body_root*: Eth2Digest
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#beaconblockbody
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#beaconblockbody
|
||||||
BeaconBlockBody* = object
|
BeaconBlockBody* = object
|
||||||
randao_reveal*: ValidatorSig
|
randao_reveal*: ValidatorSig
|
||||||
eth1_data*: Eth1Data
|
eth1_data*: Eth1Data
|
||||||
|
@ -229,7 +232,7 @@ type
|
||||||
deposits*: List[Deposit, MAX_DEPOSITS]
|
deposits*: List[Deposit, MAX_DEPOSITS]
|
||||||
voluntary_exits*: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS]
|
voluntary_exits*: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS]
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#beaconstate
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#beaconstate
|
||||||
BeaconState* = object
|
BeaconState* = object
|
||||||
# Versioning
|
# Versioning
|
||||||
genesis_time*: uint64
|
genesis_time*: uint64
|
||||||
|
@ -254,8 +257,7 @@ type
|
||||||
eth1_deposit_index*: uint64
|
eth1_deposit_index*: uint64
|
||||||
|
|
||||||
# Registry
|
# Registry
|
||||||
# TODO List[] won't construct due to VALIDATOR_REGISTRY_LIMIT > high(int)
|
validators*: List[Validator, VALIDATOR_REGISTRY_LIMIT]
|
||||||
validators*: seq[Validator]
|
|
||||||
balances*: seq[uint64]
|
balances*: seq[uint64]
|
||||||
|
|
||||||
# Randomness
|
# Randomness
|
||||||
|
@ -283,7 +285,7 @@ type
|
||||||
current_justified_checkpoint*: Checkpoint
|
current_justified_checkpoint*: Checkpoint
|
||||||
finalized_checkpoint*: Checkpoint
|
finalized_checkpoint*: Checkpoint
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#validator
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#validator
|
||||||
Validator* = object
|
Validator* = object
|
||||||
pubkey*: ValidatorPubKey
|
pubkey*: ValidatorPubKey
|
||||||
|
|
||||||
|
@ -305,7 +307,7 @@ type
|
||||||
withdrawable_epoch*: Epoch ##\
|
withdrawable_epoch*: Epoch ##\
|
||||||
## When validator can withdraw or transfer funds
|
## When validator can withdraw or transfer funds
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#pendingattestation
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#pendingattestation
|
||||||
PendingAttestation* = object
|
PendingAttestation* = object
|
||||||
aggregation_bits*: CommitteeValidatorsBits
|
aggregation_bits*: CommitteeValidatorsBits
|
||||||
data*: AttestationData
|
data*: AttestationData
|
||||||
|
@ -315,12 +317,12 @@ type
|
||||||
|
|
||||||
proposer_index*: uint64
|
proposer_index*: uint64
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#historicalbatch
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#historicalbatch
|
||||||
HistoricalBatch* = object
|
HistoricalBatch* = object
|
||||||
block_roots* : array[SLOTS_PER_HISTORICAL_ROOT, Eth2Digest]
|
block_roots* : array[SLOTS_PER_HISTORICAL_ROOT, Eth2Digest]
|
||||||
state_roots* : array[SLOTS_PER_HISTORICAL_ROOT, Eth2Digest]
|
state_roots* : array[SLOTS_PER_HISTORICAL_ROOT, Eth2Digest]
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#fork
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#fork
|
||||||
Fork* = object
|
Fork* = object
|
||||||
# TODO: Spec introduced an alias for Version = array[4, byte]
|
# TODO: Spec introduced an alias for Version = array[4, byte]
|
||||||
# and a default parameter to compute_domain
|
# and a default parameter to compute_domain
|
||||||
|
@ -330,33 +332,33 @@ type
|
||||||
epoch*: Epoch ##\
|
epoch*: Epoch ##\
|
||||||
## Epoch of latest fork
|
## Epoch of latest fork
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#eth1data
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#eth1data
|
||||||
Eth1Data* = object
|
Eth1Data* = object
|
||||||
deposit_root*: Eth2Digest
|
deposit_root*: Eth2Digest
|
||||||
deposit_count*: uint64
|
deposit_count*: uint64
|
||||||
block_hash*: Eth2Digest
|
block_hash*: Eth2Digest
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#signingroot
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#signingroot
|
||||||
SigningRoot* = object
|
SigningRoot* = object
|
||||||
object_root*: Eth2Digest
|
object_root*: Eth2Digest
|
||||||
domain*: Domain
|
domain*: Domain
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#signedvoluntaryexit
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#signedvoluntaryexit
|
||||||
SignedVoluntaryExit* = object
|
SignedVoluntaryExit* = object
|
||||||
message*: VoluntaryExit
|
message*: VoluntaryExit
|
||||||
signature*: ValidatorSig
|
signature*: ValidatorSig
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#signedbeaconblock
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#signedbeaconblock
|
||||||
SignedBeaconBlock* = object
|
SignedBeaconBlock* = object
|
||||||
message*: BeaconBlock
|
message*: BeaconBlock
|
||||||
signature*: ValidatorSig
|
signature*: ValidatorSig
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#signedbeaconblockheader
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#signedbeaconblockheader
|
||||||
SignedBeaconBlockHeader* = object
|
SignedBeaconBlockHeader* = object
|
||||||
message*: BeaconBlockHeader
|
message*: BeaconBlockHeader
|
||||||
signature*: ValidatorSig
|
signature*: ValidatorSig
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/validator.md#aggregateandproof
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#aggregateandproof
|
||||||
AggregateAndProof* = object
|
AggregateAndProof* = object
|
||||||
aggregator_index*: uint64
|
aggregator_index*: uint64
|
||||||
aggregate*: Attestation
|
aggregate*: Attestation
|
||||||
|
@ -367,7 +369,7 @@ type
|
||||||
message*: AggregateAndProof
|
message*: AggregateAndProof
|
||||||
signature*: ValidatorSig
|
signature*: ValidatorSig
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/validator.md#eth1block
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#eth1block
|
||||||
Eth1Block* = object
|
Eth1Block* = object
|
||||||
timestamp*: uint64
|
timestamp*: uint64
|
||||||
# All other eth1 block fields
|
# All other eth1 block fields
|
||||||
|
|
|
@ -15,7 +15,7 @@ import
|
||||||
# Internal
|
# Internal
|
||||||
./datatypes, ./digest, ../ssz
|
./datatypes, ./digest, ../ssz
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#integer_squareroot
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#integer_squareroot
|
||||||
func integer_squareroot*(n: SomeInteger): SomeInteger =
|
func integer_squareroot*(n: SomeInteger): SomeInteger =
|
||||||
# Return the largest integer ``x`` such that ``x**2 <= n``.
|
# Return the largest integer ``x`` such that ``x**2 <= n``.
|
||||||
doAssert n >= 0'u64
|
doAssert n >= 0'u64
|
||||||
|
@ -28,7 +28,7 @@ func integer_squareroot*(n: SomeInteger): SomeInteger =
|
||||||
y = (x + n div x) div 2
|
y = (x + n div x) div 2
|
||||||
x
|
x
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#compute_epoch_at_slot
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#compute_epoch_at_slot
|
||||||
func compute_epoch_at_slot*(slot: Slot|uint64): Epoch =
|
func compute_epoch_at_slot*(slot: Slot|uint64): Epoch =
|
||||||
# Return the epoch number at ``slot``.
|
# Return the epoch number at ``slot``.
|
||||||
(slot div SLOTS_PER_EPOCH).Epoch
|
(slot div SLOTS_PER_EPOCH).Epoch
|
||||||
|
@ -36,17 +36,17 @@ func compute_epoch_at_slot*(slot: Slot|uint64): Epoch =
|
||||||
template epoch*(slot: Slot): Epoch =
|
template epoch*(slot: Slot): Epoch =
|
||||||
compute_epoch_at_slot(slot)
|
compute_epoch_at_slot(slot)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#compute_start_slot_at_epoch
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#compute_start_slot_at_epoch
|
||||||
func compute_start_slot_at_epoch*(epoch: Epoch): Slot =
|
func compute_start_slot_at_epoch*(epoch: Epoch): Slot =
|
||||||
# Return the start slot of ``epoch``.
|
# Return the start slot of ``epoch``.
|
||||||
(epoch * SLOTS_PER_EPOCH).Slot
|
(epoch * SLOTS_PER_EPOCH).Slot
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#is_active_validator
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#is_active_validator
|
||||||
func is_active_validator*(validator: Validator, epoch: Epoch): bool =
|
func is_active_validator*(validator: Validator, epoch: Epoch): bool =
|
||||||
### Check if ``validator`` is active
|
### Check if ``validator`` is active
|
||||||
validator.activation_epoch <= epoch and epoch < validator.exit_epoch
|
validator.activation_epoch <= epoch and epoch < validator.exit_epoch
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#get_active_validator_indices
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#get_active_validator_indices
|
||||||
func get_active_validator_indices*(state: BeaconState, epoch: Epoch):
|
func get_active_validator_indices*(state: BeaconState, epoch: Epoch):
|
||||||
seq[ValidatorIndex] =
|
seq[ValidatorIndex] =
|
||||||
# Return the sequence of active validator indices at ``epoch``.
|
# Return the sequence of active validator indices at ``epoch``.
|
||||||
|
@ -54,7 +54,7 @@ func get_active_validator_indices*(state: BeaconState, epoch: Epoch):
|
||||||
if is_active_validator(val, epoch):
|
if is_active_validator(val, epoch):
|
||||||
result.add idx.ValidatorIndex
|
result.add idx.ValidatorIndex
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#get_committee_count_at_slot
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#get_committee_count_at_slot
|
||||||
func get_committee_count_at_slot*(state: BeaconState, slot: Slot): uint64 =
|
func get_committee_count_at_slot*(state: BeaconState, slot: Slot): uint64 =
|
||||||
# Return the number of committees at ``slot``.
|
# Return the number of committees at ``slot``.
|
||||||
let epoch = compute_epoch_at_slot(slot)
|
let epoch = compute_epoch_at_slot(slot)
|
||||||
|
@ -67,13 +67,13 @@ func get_committee_count_at_slot*(state: BeaconState, slot: Slot): uint64 =
|
||||||
# Otherwise, get_beacon_committee(...) cannot access some committees.
|
# Otherwise, get_beacon_committee(...) cannot access some committees.
|
||||||
doAssert (SLOTS_PER_EPOCH * MAX_COMMITTEES_PER_SLOT).uint64 >= result
|
doAssert (SLOTS_PER_EPOCH * MAX_COMMITTEES_PER_SLOT).uint64 >= result
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#get_current_epoch
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#get_current_epoch
|
||||||
func get_current_epoch*(state: BeaconState): Epoch =
|
func get_current_epoch*(state: BeaconState): Epoch =
|
||||||
# Return the current epoch.
|
# Return the current epoch.
|
||||||
doAssert state.slot >= GENESIS_SLOT, $state.slot
|
doAssert state.slot >= GENESIS_SLOT, $state.slot
|
||||||
compute_epoch_at_slot(state.slot)
|
compute_epoch_at_slot(state.slot)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#get_randao_mix
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#get_randao_mix
|
||||||
func get_randao_mix*(state: BeaconState,
|
func get_randao_mix*(state: BeaconState,
|
||||||
epoch: Epoch): Eth2Digest =
|
epoch: Epoch): Eth2Digest =
|
||||||
## Returns the randao mix at a recent ``epoch``.
|
## Returns the randao mix at a recent ``epoch``.
|
||||||
|
@ -114,7 +114,7 @@ func int_to_bytes4*(x: uint64): array[4, byte] =
|
||||||
result[2] = ((x shr 16) and 0xff).byte
|
result[2] = ((x shr 16) and 0xff).byte
|
||||||
result[3] = ((x shr 24) and 0xff).byte
|
result[3] = ((x shr 24) and 0xff).byte
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#compute_fork_data_root
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#compute_fork_data_root
|
||||||
func compute_fork_data_root(current_version: array[4, byte],
|
func compute_fork_data_root(current_version: array[4, byte],
|
||||||
genesis_validators_root: Eth2Digest): Eth2Digest =
|
genesis_validators_root: Eth2Digest): Eth2Digest =
|
||||||
# Return the 32-byte fork data root for the ``current_version`` and
|
# Return the 32-byte fork data root for the ``current_version`` and
|
||||||
|
@ -126,7 +126,7 @@ func compute_fork_data_root(current_version: array[4, byte],
|
||||||
genesis_validators_root: genesis_validators_root
|
genesis_validators_root: genesis_validators_root
|
||||||
))
|
))
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#compute_fork_digest
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#compute_fork_digest
|
||||||
func compute_fork_digest(current_version: array[4, byte],
|
func compute_fork_digest(current_version: array[4, byte],
|
||||||
genesis_validators_root: Eth2Digest): array[4, byte] =
|
genesis_validators_root: Eth2Digest): array[4, byte] =
|
||||||
# Return the 4-byte fork digest for the ``current_version`` and
|
# Return the 4-byte fork digest for the ``current_version`` and
|
||||||
|
@ -136,7 +136,7 @@ func compute_fork_digest(current_version: array[4, byte],
|
||||||
result[0..3] =
|
result[0..3] =
|
||||||
compute_fork_data_root(current_version, genesis_validators_root).data[0..3]
|
compute_fork_data_root(current_version, genesis_validators_root).data[0..3]
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#compute_domain
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#compute_domain
|
||||||
func compute_domain*(
|
func compute_domain*(
|
||||||
domain_type: DomainType,
|
domain_type: DomainType,
|
||||||
fork_version: array[4, byte] = [0'u8, 0, 0, 0],
|
fork_version: array[4, byte] = [0'u8, 0, 0, 0],
|
||||||
|
@ -147,29 +147,25 @@ func compute_domain*(
|
||||||
result[0..3] = int_to_bytes4(domain_type.uint64)
|
result[0..3] = int_to_bytes4(domain_type.uint64)
|
||||||
result[4..31] = fork_data_root.data[0..27]
|
result[4..31] = fork_data_root.data[0..27]
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#get_domain
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#get_domain
|
||||||
func get_domain*(
|
func get_domain*(
|
||||||
fork: Fork, domain_type: DomainType, epoch: Epoch): Domain =
|
fork: Fork, domain_type: DomainType, epoch: Epoch, genesis_validators_root: Eth2Digest): Domain =
|
||||||
## Return the signature domain (fork version concatenated with domain type)
|
## Return the signature domain (fork version concatenated with domain type)
|
||||||
## of a message.
|
## of a message.
|
||||||
let
|
let fork_version =
|
||||||
fork_version =
|
if epoch < fork.epoch:
|
||||||
if epoch < fork.epoch:
|
fork.previous_version
|
||||||
fork.previous_version
|
else:
|
||||||
else:
|
fork.current_version
|
||||||
fork.current_version
|
compute_domain(domain_type, fork_version, genesis_validators_root)
|
||||||
compute_domain(domain_type, fork_version)
|
|
||||||
|
|
||||||
func get_domain*(
|
func get_domain*(
|
||||||
state: BeaconState, domain_type: DomainType, message_epoch: Epoch): Domain =
|
state: BeaconState, domain_type: DomainType, epoch: Epoch): Domain =
|
||||||
## Return the signature domain (fork version concatenated with domain type)
|
## Return the signature domain (fork version concatenated with domain type)
|
||||||
## of a message.
|
## of a message.
|
||||||
get_domain(state.fork, domain_type, message_epoch)
|
get_domain(state.fork, domain_type, epoch, state. genesis_validators_root)
|
||||||
|
|
||||||
func get_domain*(state: BeaconState, domain_type: DomainType): Domain =
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#compute_signing_root
|
||||||
get_domain(state, domain_type, get_current_epoch(state))
|
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#compute_signing_root
|
|
||||||
func compute_signing_root*(ssz_object: auto, domain: Domain): Eth2Digest =
|
func compute_signing_root*(ssz_object: auto, domain: Domain): Eth2Digest =
|
||||||
# Return the signing root of an object by calculating the root of the
|
# Return the signing root of an object by calculating the root of the
|
||||||
# object-domain tree.
|
# object-domain tree.
|
||||||
|
@ -179,7 +175,7 @@ func compute_signing_root*(ssz_object: auto, domain: Domain): Eth2Digest =
|
||||||
)
|
)
|
||||||
hash_tree_root(domain_wrapped_object)
|
hash_tree_root(domain_wrapped_object)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#get_seed
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#get_seed
|
||||||
func get_seed*(state: BeaconState, epoch: Epoch, domain_type: DomainType): Eth2Digest =
|
func get_seed*(state: BeaconState, epoch: Epoch, domain_type: DomainType): Eth2Digest =
|
||||||
# Return the seed at ``epoch``.
|
# Return the seed at ``epoch``.
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ const
|
||||||
topicVoluntaryExits* = "/eth2/voluntary_exit/ssz"
|
topicVoluntaryExits* = "/eth2/voluntary_exit/ssz"
|
||||||
topicProposerSlashings* = "/eth2/proposer_slashing/ssz"
|
topicProposerSlashings* = "/eth2/proposer_slashing/ssz"
|
||||||
topicAttesterSlashings* = "/eth2/attester_slashing/ssz"
|
topicAttesterSlashings* = "/eth2/attester_slashing/ssz"
|
||||||
|
topicAggregateAndProof* = "/eth2/beacon_aggregate_and_proof/ssz"
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/p2p-interface.md#configuration
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/p2p-interface.md#configuration
|
||||||
ATTESTATION_SUBNET_COUNT* = 64
|
ATTESTATION_SUBNET_COUNT* = 64
|
||||||
|
|
|
@ -20,7 +20,7 @@ type
|
||||||
const
|
const
|
||||||
# Misc
|
# Misc
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/configs/mainnet.yaml#L6
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/configs/mainnet.yaml#L6
|
||||||
|
|
||||||
MAX_COMMITTEES_PER_SLOT* {.intdefine.} = 64
|
MAX_COMMITTEES_PER_SLOT* {.intdefine.} = 64
|
||||||
|
|
||||||
|
@ -49,14 +49,14 @@ const
|
||||||
HYSTERESIS_UPWARD_MULTIPLIER* = 5
|
HYSTERESIS_UPWARD_MULTIPLIER* = 5
|
||||||
|
|
||||||
# Constants (TODO: not actually configurable)
|
# Constants (TODO: not actually configurable)
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#constants
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#constants
|
||||||
BASE_REWARDS_PER_EPOCH* = 4
|
BASE_REWARDS_PER_EPOCH* = 4
|
||||||
|
|
||||||
DEPOSIT_CONTRACT_TREE_DEPTH* = 32
|
DEPOSIT_CONTRACT_TREE_DEPTH* = 32
|
||||||
|
|
||||||
# Gwei values
|
# Gwei values
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/configs/mainnet.yaml#L52
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/configs/mainnet.yaml#L58
|
||||||
|
|
||||||
MIN_DEPOSIT_AMOUNT* = 2'u64^0 * 10'u64^9 ##\
|
MIN_DEPOSIT_AMOUNT* = 2'u64^0 * 10'u64^9 ##\
|
||||||
## Minimum amounth of ETH that can be deposited in one call - deposits can
|
## Minimum amounth of ETH that can be deposited in one call - deposits can
|
||||||
|
@ -73,9 +73,9 @@ const
|
||||||
|
|
||||||
# Initial values
|
# Initial values
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/configs/mainnet.yaml#L64
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/configs/mainnet.yaml#L70
|
||||||
GENESIS_SLOT* = 0.Slot
|
GENESIS_SLOT* = 0.Slot
|
||||||
GENESIS_FORK_VERSION* = 0x00000000
|
GENESIS_FORK_VERSION* = [0'u8, 0'u8, 0'u8, 0'u8]
|
||||||
BLS_WITHDRAWAL_PREFIX* = 0'u8
|
BLS_WITHDRAWAL_PREFIX* = 0'u8
|
||||||
|
|
||||||
# Time parameters
|
# Time parameters
|
||||||
|
@ -130,15 +130,22 @@ const
|
||||||
|
|
||||||
# State vector lengths
|
# State vector lengths
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/configs/mainnet.yaml#L102
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/configs/mainnet.yaml#L105
|
||||||
EPOCHS_PER_HISTORICAL_VECTOR* = 65536
|
|
||||||
EPOCHS_PER_SLASHINGS_VECTOR* = 8192
|
EPOCHS_PER_HISTORICAL_VECTOR* = 65536 ##\
|
||||||
HISTORICAL_ROOTS_LIMIT* = 16777216
|
## epochs (~0.8 years)
|
||||||
|
|
||||||
|
EPOCHS_PER_SLASHINGS_VECTOR* = 8192 ##\
|
||||||
|
## epochs (~36 days)
|
||||||
|
|
||||||
|
HISTORICAL_ROOTS_LIMIT* = 16777216 ##\
|
||||||
|
## epochs (~26,131 years)
|
||||||
|
|
||||||
VALIDATOR_REGISTRY_LIMIT* = 1099511627776
|
VALIDATOR_REGISTRY_LIMIT* = 1099511627776
|
||||||
|
|
||||||
# Reward and penalty quotients
|
# Reward and penalty quotients
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/configs/mainnet.yaml#L114
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/configs/mainnet.yaml#L117
|
||||||
BASE_REWARD_FACTOR* = 2'u64^6
|
BASE_REWARD_FACTOR* = 2'u64^6
|
||||||
WHISTLEBLOWER_REWARD_QUOTIENT* = 2'u64^9
|
WHISTLEBLOWER_REWARD_QUOTIENT* = 2'u64^9
|
||||||
PROPOSER_REWARD_QUOTIENT* = 2'u64^3
|
PROPOSER_REWARD_QUOTIENT* = 2'u64^3
|
||||||
|
@ -156,7 +163,7 @@ const
|
||||||
|
|
||||||
# Fork choice
|
# Fork choice
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/configs/mainnet.yaml#L26
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/configs/mainnet.yaml#L32
|
||||||
SAFE_SLOTS_TO_UPDATE_JUSTIFIED* = 8 # 96 seconds
|
SAFE_SLOTS_TO_UPDATE_JUSTIFIED* = 8 # 96 seconds
|
||||||
|
|
||||||
# Validators
|
# Validators
|
||||||
|
|
|
@ -20,7 +20,7 @@ type
|
||||||
const
|
const
|
||||||
# Misc
|
# Misc
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/configs/minimal.yaml#L4
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/configs/minimal.yaml#L4
|
||||||
|
|
||||||
# Changed
|
# Changed
|
||||||
MAX_COMMITTEES_PER_SLOT* = 4
|
MAX_COMMITTEES_PER_SLOT* = 4
|
||||||
|
@ -43,7 +43,7 @@ const
|
||||||
|
|
||||||
# Constants
|
# Constants
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#constants
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#constants
|
||||||
# TODO "The following values are (non-configurable) constants" ...
|
# TODO "The following values are (non-configurable) constants" ...
|
||||||
# Unchanged
|
# Unchanged
|
||||||
BASE_REWARDS_PER_EPOCH* = 4
|
BASE_REWARDS_PER_EPOCH* = 4
|
||||||
|
@ -52,7 +52,7 @@ const
|
||||||
|
|
||||||
# Gwei values
|
# Gwei values
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/configs/minimal.yaml#L52
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/configs/minimal.yaml#L58
|
||||||
|
|
||||||
# Unchanged
|
# Unchanged
|
||||||
MIN_DEPOSIT_AMOUNT* = 2'u64^0 * 10'u64^9
|
MIN_DEPOSIT_AMOUNT* = 2'u64^0 * 10'u64^9
|
||||||
|
@ -66,12 +66,12 @@ const
|
||||||
|
|
||||||
# Unchanged
|
# Unchanged
|
||||||
GENESIS_SLOT* = 0.Slot
|
GENESIS_SLOT* = 0.Slot
|
||||||
GENESIS_FORK_VERSION* = 0x01000000
|
GENESIS_FORK_VERSION* = [0'u8, 0'u8, 0'u8, 1'u8]
|
||||||
BLS_WITHDRAWAL_PREFIX* = 0'u8
|
BLS_WITHDRAWAL_PREFIX* = 0'u8
|
||||||
|
|
||||||
# Time parameters
|
# Time parameters
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/configs/minimal.yaml#L71
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/configs/minimal.yaml#L77
|
||||||
# Changed: Faster to spin up testnets, but does not give validator
|
# Changed: Faster to spin up testnets, but does not give validator
|
||||||
# reasonable warning time for genesis
|
# reasonable warning time for genesis
|
||||||
MIN_GENESIS_DELAY* = 300
|
MIN_GENESIS_DELAY* = 300
|
||||||
|
@ -117,7 +117,7 @@ const
|
||||||
|
|
||||||
# Reward and penalty quotients
|
# Reward and penalty quotients
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/configs/minimal.yaml#L117
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/configs/minimal.yaml#L117
|
||||||
|
|
||||||
BASE_REWARD_FACTOR* = 2'u64^6
|
BASE_REWARD_FACTOR* = 2'u64^6
|
||||||
WHISTLEBLOWER_REWARD_QUOTIENT* = 2'u64^9
|
WHISTLEBLOWER_REWARD_QUOTIENT* = 2'u64^9
|
||||||
|
@ -127,7 +127,7 @@ const
|
||||||
|
|
||||||
# Max operations per block
|
# Max operations per block
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/configs/minimal.yaml#L131
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/configs/minimal.yaml#L131
|
||||||
|
|
||||||
MAX_PROPOSER_SLASHINGS* = 2^4
|
MAX_PROPOSER_SLASHINGS* = 2^4
|
||||||
MAX_ATTESTER_SLASHINGS* = 2^0
|
MAX_ATTESTER_SLASHINGS* = 2^0
|
||||||
|
@ -144,7 +144,7 @@ const
|
||||||
|
|
||||||
# Validators
|
# Validators
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/configs/minimal.yaml#L32
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/configs/minimal.yaml#L38
|
||||||
|
|
||||||
# Changed
|
# Changed
|
||||||
ETH1_FOLLOW_DISTANCE* = 16 # blocks
|
ETH1_FOLLOW_DISTANCE* = 16 # blocks
|
||||||
|
@ -178,16 +178,6 @@ const
|
||||||
MIN_GASPRICE* = 32 # Gwei
|
MIN_GASPRICE* = 32 # Gwei
|
||||||
GASPRICE_ADJUSTMENT_COEFFICIENT* = 8
|
GASPRICE_ADJUSTMENT_COEFFICIENT* = 8
|
||||||
|
|
||||||
# Phase 1 - Sharding
|
|
||||||
# ---------------------------------------------------------------
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/configs/minimal.yaml#L157
|
|
||||||
# TODO those are included in minimal.yaml but not mainnet.yaml
|
|
||||||
# Why?
|
|
||||||
SHARD_SLOTS_PER_BEACON_SLOT* = 2 # spec: SHARD_SLOTS_PER_EPOCH
|
|
||||||
EPOCHS_PER_SHARD_PERIOD* = 4
|
|
||||||
PHASE_1_FORK_EPOCH* = 8
|
|
||||||
PHASE_1_FORK_SLOT* = 64
|
|
||||||
|
|
||||||
# Phase 1 - Custody game
|
# Phase 1 - Custody game
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase1/custody-game.md#constants
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase1/custody-game.md#constants
|
||||||
|
|
|
@ -44,7 +44,7 @@ declareGauge beacon_previous_live_validators, "Number of active validators that
|
||||||
declareGauge beacon_pending_deposits, "Number of pending deposits (state.eth1_data.deposit_count - state.eth1_deposit_index)" # On block
|
declareGauge beacon_pending_deposits, "Number of pending deposits (state.eth1_data.deposit_count - state.eth1_deposit_index)" # On block
|
||||||
declareGauge beacon_processed_deposits_total, "Number of total deposits included on chain" # On block
|
declareGauge beacon_processed_deposits_total, "Number of total deposits included on chain" # On block
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#block-header
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#block-header
|
||||||
proc process_block_header*(
|
proc process_block_header*(
|
||||||
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags,
|
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags,
|
||||||
stateCache: var StateCache): bool {.nbench.}=
|
stateCache: var StateCache): bool {.nbench.}=
|
||||||
|
@ -108,7 +108,8 @@ proc process_randao(
|
||||||
let proposer = addr state.validators[proposer_index.get]
|
let proposer = addr state.validators[proposer_index.get]
|
||||||
|
|
||||||
# Verify that the provided randao value is valid
|
# Verify that the provided randao value is valid
|
||||||
let signing_root = compute_signing_root(epoch, get_domain(state, DOMAIN_RANDAO))
|
let signing_root = compute_signing_root(
|
||||||
|
epoch, get_domain(state, DOMAIN_RANDAO, get_current_epoch(state)))
|
||||||
if skipBLSValidation notin flags:
|
if skipBLSValidation notin flags:
|
||||||
if not blsVerify(proposer.pubkey, signing_root.data, body.randao_reveal):
|
if not blsVerify(proposer.pubkey, signing_root.data, body.randao_reveal):
|
||||||
notice "Randao mismatch", proposer_pubkey = shortLog(proposer.pubkey),
|
notice "Randao mismatch", proposer_pubkey = shortLog(proposer.pubkey),
|
||||||
|
@ -127,21 +128,21 @@ proc process_randao(
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#eth1-data
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#eth1-data
|
||||||
func process_eth1_data(state: var BeaconState, body: BeaconBlockBody) {.nbench.}=
|
func process_eth1_data(state: var BeaconState, body: BeaconBlockBody) {.nbench.}=
|
||||||
state.eth1_data_votes.add body.eth1_data
|
state.eth1_data_votes.add body.eth1_data
|
||||||
if state.eth1_data_votes.count(body.eth1_data) * 2 >
|
if state.eth1_data_votes.count(body.eth1_data) * 2 >
|
||||||
EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH:
|
EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH:
|
||||||
state.eth1_data = body.eth1_data
|
state.eth1_data = body.eth1_data
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#is_slashable_validator
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#is_slashable_validator
|
||||||
func is_slashable_validator(validator: Validator, epoch: Epoch): bool =
|
func is_slashable_validator(validator: Validator, epoch: Epoch): bool =
|
||||||
# Check if ``validator`` is slashable.
|
# Check if ``validator`` is slashable.
|
||||||
(not validator.slashed) and
|
(not validator.slashed) and
|
||||||
(validator.activation_epoch <= epoch) and
|
(validator.activation_epoch <= epoch) and
|
||||||
(epoch < validator.withdrawable_epoch)
|
(epoch < validator.withdrawable_epoch)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#proposer-slashings
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#proposer-slashings
|
||||||
proc process_proposer_slashing*(
|
proc process_proposer_slashing*(
|
||||||
state: var BeaconState, proposer_slashing: ProposerSlashing,
|
state: var BeaconState, proposer_slashing: ProposerSlashing,
|
||||||
flags: UpdateFlags, stateCache: var StateCache): bool {.nbench.}=
|
flags: UpdateFlags, stateCache: var StateCache): bool {.nbench.}=
|
||||||
|
@ -378,7 +379,7 @@ proc processVoluntaryExits(state: var BeaconState, blck: BeaconBlock, flags: Upd
|
||||||
return false
|
return false
|
||||||
return true
|
return true
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#block-processing
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#block-processing
|
||||||
proc process_block*(
|
proc process_block*(
|
||||||
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags,
|
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags,
|
||||||
stateCache: var StateCache): bool {.nbench.}=
|
stateCache: var StateCache): bool {.nbench.}=
|
||||||
|
@ -414,7 +415,7 @@ proc process_block*(
|
||||||
|
|
||||||
# TODO, everything below is now in process_operations
|
# TODO, everything below is now in process_operations
|
||||||
# and implementation is per element instead of the whole seq
|
# and implementation is per element instead of the whole seq
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#operations
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#operations
|
||||||
if not processProposerSlashings(state, blck, flags, stateCache):
|
if not processProposerSlashings(state, blck, flags, stateCache):
|
||||||
debug "[Block processing] Proposer slashing failure", slot = shortLog(state.slot)
|
debug "[Block processing] Proposer slashing failure", slot = shortLog(state.slot)
|
||||||
return false
|
return false
|
||||||
|
@ -481,42 +482,47 @@ proc makeBeaconBlock*(
|
||||||
|
|
||||||
some(blck)
|
some(blck)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/validator.md
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#aggregation-selection
|
||||||
func get_slot_signature*(
|
func get_slot_signature*(
|
||||||
fork: Fork, slot: Slot, privkey: ValidatorPrivKey): ValidatorSig =
|
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot,
|
||||||
|
privkey: ValidatorPrivKey): ValidatorSig =
|
||||||
let
|
let
|
||||||
domain =
|
domain = get_domain(fork, DOMAIN_SELECTION_PROOF,
|
||||||
get_domain(fork, DOMAIN_SELECTION_PROOF, compute_epoch_at_slot(slot))
|
compute_epoch_at_slot(slot), genesis_validators_root)
|
||||||
signing_root = compute_signing_root(slot, domain)
|
signing_root = compute_signing_root(slot, domain)
|
||||||
|
|
||||||
blsSign(privKey, signing_root.data)
|
blsSign(privKey, signing_root.data)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/validator.md
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#randao-reveal
|
||||||
func get_epoch_signature*(
|
func get_epoch_signature*(
|
||||||
fork: Fork, slot: Slot, privkey: ValidatorPrivKey): ValidatorSig =
|
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot,
|
||||||
|
privkey: ValidatorPrivKey): ValidatorSig =
|
||||||
let
|
let
|
||||||
domain =
|
domain = get_domain(fork, DOMAIN_RANDAO, compute_epoch_at_slot(slot),
|
||||||
get_domain(fork, DOMAIN_RANDAO, compute_epoch_at_slot(slot))
|
genesis_validators_root)
|
||||||
signing_root = compute_signing_root(compute_epoch_at_slot(slot), domain)
|
signing_root = compute_signing_root(compute_epoch_at_slot(slot), domain)
|
||||||
|
|
||||||
blsSign(privKey, signing_root.data)
|
blsSign(privKey, signing_root.data)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/validator.md
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#signature
|
||||||
func get_block_signature*(
|
func get_block_signature*(
|
||||||
fork: Fork, slot: Slot, root: Eth2Digest, privkey: ValidatorPrivKey): ValidatorSig =
|
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot,
|
||||||
|
root: Eth2Digest, privkey: ValidatorPrivKey): ValidatorSig =
|
||||||
let
|
let
|
||||||
domain =
|
domain = get_domain(fork, DOMAIN_BEACON_PROPOSER,
|
||||||
get_domain(fork, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(slot))
|
compute_epoch_at_slot(slot), genesis_validators_root)
|
||||||
signing_root = compute_signing_root(root, domain)
|
signing_root = compute_signing_root(root, domain)
|
||||||
|
|
||||||
blsSign(privKey, signing_root.data)
|
blsSign(privKey, signing_root.data)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/validator.md
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#aggregate-signature
|
||||||
func get_attestation_signature*(
|
func get_attestation_signature*(
|
||||||
fork: Fork, attestation: AttestationData, privkey: ValidatorPrivKey): ValidatorSig =
|
fork: Fork, genesis_validators_root: Eth2Digest, attestation: AttestationData,
|
||||||
|
privkey: ValidatorPrivKey): ValidatorSig =
|
||||||
let
|
let
|
||||||
attestationRoot = hash_tree_root(attestation)
|
attestationRoot = hash_tree_root(attestation)
|
||||||
domain = get_domain(fork, DOMAIN_BEACON_ATTESTER, attestation.target.epoch)
|
domain = get_domain(fork, DOMAIN_BEACON_ATTESTER,
|
||||||
|
attestation.target.epoch, genesis_validators_root)
|
||||||
signing_root = compute_signing_root(attestationRoot, domain)
|
signing_root = compute_signing_root(attestationRoot, domain)
|
||||||
|
|
||||||
blsSign(privKey, signing_root.data)
|
blsSign(privKey, signing_root.data)
|
||||||
|
|
|
@ -63,15 +63,17 @@ declareGauge epoch_transition_final_updates, "Epoch transition final updates tim
|
||||||
# Spec
|
# Spec
|
||||||
# --------------------------------------------------------
|
# --------------------------------------------------------
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#get_total_active_balance
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#get_total_active_balance
|
||||||
func get_total_active_balance*(state: BeaconState): Gwei =
|
func get_total_active_balance*(state: BeaconState): Gwei =
|
||||||
# Return the combined effective balance of the active validators.
|
# Return the combined effective balance of the active validators.
|
||||||
|
# Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei
|
||||||
|
# minimum to avoid divisions by zero.
|
||||||
# TODO it calls get_total_balance with set(g_a_v_i(...))
|
# TODO it calls get_total_balance with set(g_a_v_i(...))
|
||||||
get_total_balance(
|
get_total_balance(
|
||||||
state,
|
state,
|
||||||
get_active_validator_indices(state, get_current_epoch(state)))
|
get_active_validator_indices(state, get_current_epoch(state)))
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#helper-functions-1
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#helper-functions-1
|
||||||
func get_matching_source_attestations(state: BeaconState, epoch: Epoch):
|
func get_matching_source_attestations(state: BeaconState, epoch: Epoch):
|
||||||
seq[PendingAttestation] =
|
seq[PendingAttestation] =
|
||||||
doAssert epoch in [get_current_epoch(state), get_previous_epoch(state)]
|
doAssert epoch in [get_current_epoch(state), get_previous_epoch(state)]
|
||||||
|
@ -98,6 +100,10 @@ func get_matching_head_attestations(state: BeaconState, epoch: Epoch):
|
||||||
func get_attesting_balance(
|
func get_attesting_balance(
|
||||||
state: BeaconState, attestations: seq[PendingAttestation],
|
state: BeaconState, attestations: seq[PendingAttestation],
|
||||||
stateCache: var StateCache): Gwei =
|
stateCache: var StateCache): Gwei =
|
||||||
|
# Return the combined effective balance of the set of unslashed validators
|
||||||
|
# participating in ``attestations``.
|
||||||
|
# Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei
|
||||||
|
# minimum to avoid divisions by zero.
|
||||||
get_total_balance(state, get_unslashed_attesting_indices(
|
get_total_balance(state, get_unslashed_attesting_indices(
|
||||||
state, attestations, stateCache))
|
state, attestations, stateCache))
|
||||||
|
|
||||||
|
@ -144,7 +150,7 @@ proc process_justification_and_finalization*(
|
||||||
## and `get_matching_source_attestations(...)` via
|
## and `get_matching_source_attestations(...)` via
|
||||||
## https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#helper-functions-1
|
## https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#helper-functions-1
|
||||||
## and
|
## and
|
||||||
## https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#final-updates
|
## https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#final-updates
|
||||||
## after which the state.previous_epoch_attestations is replaced.
|
## after which the state.previous_epoch_attestations is replaced.
|
||||||
trace "Non-attesting indices in previous epoch",
|
trace "Non-attesting indices in previous epoch",
|
||||||
missing_all_validators=
|
missing_all_validators=
|
||||||
|
@ -233,7 +239,7 @@ proc process_justification_and_finalization*(
|
||||||
checkpoint = shortLog(state.finalized_checkpoint),
|
checkpoint = shortLog(state.finalized_checkpoint),
|
||||||
cat = "finalization"
|
cat = "finalization"
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#rewards-and-penalties-1
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#rewards-and-penalties-1
|
||||||
func get_base_reward(state: BeaconState, index: ValidatorIndex,
|
func get_base_reward(state: BeaconState, index: ValidatorIndex,
|
||||||
total_balance: auto): Gwei =
|
total_balance: auto): Gwei =
|
||||||
# Spec function recalculates total_balance every time, which creates an
|
# Spec function recalculates total_balance every time, which creates an
|
||||||
|
@ -242,7 +248,7 @@ func get_base_reward(state: BeaconState, index: ValidatorIndex,
|
||||||
effective_balance * BASE_REWARD_FACTOR div
|
effective_balance * BASE_REWARD_FACTOR div
|
||||||
integer_squareroot(total_balance) div BASE_REWARDS_PER_EPOCH
|
integer_squareroot(total_balance) div BASE_REWARDS_PER_EPOCH
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#rewards-and-penalties-1
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#rewards-and-penalties-1
|
||||||
func get_attestation_deltas(state: BeaconState, stateCache: var StateCache):
|
func get_attestation_deltas(state: BeaconState, stateCache: var StateCache):
|
||||||
tuple[a: seq[Gwei], b: seq[Gwei]] {.nbench.}=
|
tuple[a: seq[Gwei], b: seq[Gwei]] {.nbench.}=
|
||||||
let
|
let
|
||||||
|
@ -280,7 +286,7 @@ func get_attestation_deltas(state: BeaconState, stateCache: var StateCache):
|
||||||
const increment = EFFECTIVE_BALANCE_INCREMENT
|
const increment = EFFECTIVE_BALANCE_INCREMENT
|
||||||
let reward_numerator = get_base_reward(state, index, total_balance) *
|
let reward_numerator = get_base_reward(state, index, total_balance) *
|
||||||
(attesting_balance div increment)
|
(attesting_balance div increment)
|
||||||
rewards[index] = reward_numerator div (total_balance div increment)
|
rewards[index] += reward_numerator div (total_balance div increment)
|
||||||
else:
|
else:
|
||||||
penalties[index] += get_base_reward(state, index, total_balance)
|
penalties[index] += get_base_reward(state, index, total_balance)
|
||||||
|
|
||||||
|
@ -367,7 +373,7 @@ func process_slashings*(state: var BeaconState) {.nbench.}=
|
||||||
let penalty = penalty_numerator div total_balance * increment
|
let penalty = penalty_numerator div total_balance * increment
|
||||||
decrease_balance(state, index.ValidatorIndex, penalty)
|
decrease_balance(state, index.ValidatorIndex, penalty)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#final-updates
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#final-updates
|
||||||
func process_final_updates*(state: var BeaconState) {.nbench.}=
|
func process_final_updates*(state: var BeaconState) {.nbench.}=
|
||||||
let
|
let
|
||||||
current_epoch = get_current_epoch(state)
|
current_epoch = get_current_epoch(state)
|
||||||
|
@ -425,7 +431,7 @@ proc process_epoch*(state: var BeaconState) {.nbench.}=
|
||||||
trace "ran process_justification_and_finalization",
|
trace "ran process_justification_and_finalization",
|
||||||
current_epoch = get_current_epoch(state)
|
current_epoch = get_current_epoch(state)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#rewards-and-penalties-1
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#rewards-and-penalties-1
|
||||||
process_rewards_and_penalties(state, per_epoch_cache)
|
process_rewards_and_penalties(state, per_epoch_cache)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#registry-updates
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#registry-updates
|
||||||
|
@ -439,7 +445,7 @@ proc process_epoch*(state: var BeaconState) {.nbench.}=
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#slashings
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#slashings
|
||||||
process_slashings(state)
|
process_slashings(state)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#final-updates
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#final-updates
|
||||||
process_final_updates(state)
|
process_final_updates(state)
|
||||||
|
|
||||||
# Once per epoch metrics
|
# Once per epoch metrics
|
||||||
|
|
|
@ -10,8 +10,8 @@ import
|
||||||
options, nimcrypto, sequtils, math, tables,
|
options, nimcrypto, sequtils, math, tables,
|
||||||
./datatypes, ./digest, ./helpers
|
./datatypes, ./digest, ./helpers
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#compute_shuffled_index
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#compute_shuffled_index
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/beacon-chain.md#compute_committee
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#compute_committee
|
||||||
func get_shuffled_seq*(seed: Eth2Digest,
|
func get_shuffled_seq*(seed: Eth2Digest,
|
||||||
list_size: uint64,
|
list_size: uint64,
|
||||||
): seq[ValidatorIndex] =
|
): seq[ValidatorIndex] =
|
||||||
|
@ -145,7 +145,7 @@ func get_empty_per_epoch_cache*(): StateCache =
|
||||||
initTable[Epoch, seq[ValidatorIndex]]()
|
initTable[Epoch, seq[ValidatorIndex]]()
|
||||||
result.committee_count_cache = initTable[Epoch, uint64]()
|
result.committee_count_cache = initTable[Epoch, uint64]()
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#compute_proposer_index
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#compute_proposer_index
|
||||||
func compute_proposer_index(state: BeaconState, indices: seq[ValidatorIndex],
|
func compute_proposer_index(state: BeaconState, indices: seq[ValidatorIndex],
|
||||||
seed: Eth2Digest, stateCache: var StateCache): Option[ValidatorIndex] =
|
seed: Eth2Digest, stateCache: var StateCache): Option[ValidatorIndex] =
|
||||||
# Return from ``indices`` a random index sampled by effective balance.
|
# Return from ``indices`` a random index sampled by effective balance.
|
||||||
|
@ -176,7 +176,7 @@ func compute_proposer_index(state: BeaconState, indices: seq[ValidatorIndex],
|
||||||
return some(candidate_index)
|
return some(candidate_index)
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/beacon-chain.md#get_beacon_proposer_index
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#get_beacon_proposer_index
|
||||||
func get_beacon_proposer_index*(state: BeaconState, stateCache: var StateCache):
|
func get_beacon_proposer_index*(state: BeaconState, stateCache: var StateCache):
|
||||||
Option[ValidatorIndex] =
|
Option[ValidatorIndex] =
|
||||||
# Return the beacon proposer index at the current slot.
|
# Return the beacon proposer index at the current slot.
|
||||||
|
|
|
@ -62,9 +62,9 @@ type
|
||||||
discard
|
discard
|
||||||
|
|
||||||
when useListType:
|
when useListType:
|
||||||
type List*[T; maxLen: static int] = distinct seq[T]
|
type List*[T; maxLen: static int64] = distinct seq[T]
|
||||||
else:
|
else:
|
||||||
type List*[T; maxLen: static int] = seq[T]
|
type List*[T; maxLen: static int64] = seq[T]
|
||||||
|
|
||||||
macro unsupported*(T: typed): untyped =
|
macro unsupported*(T: typed): untyped =
|
||||||
# TODO: {.fatal.} breaks compilation even in `compiles()` context,
|
# TODO: {.fatal.} breaks compilation even in `compiles()` context,
|
||||||
|
|
|
@ -30,8 +30,8 @@ type
|
||||||
slots*: seq[PeerSlot[A, B]]
|
slots*: seq[PeerSlot[A, B]]
|
||||||
man: SyncManager[A, B]
|
man: SyncManager[A, B]
|
||||||
|
|
||||||
GetLocalHeadSlotCallback* = proc(): Slot
|
GetLocalHeadSlotCallback* = proc(): Slot {.gcsafe.}
|
||||||
UpdateLocalBlocksCallback* = proc(list: openarray[SignedBeaconBlock]): bool
|
UpdateLocalBlocksCallback* = proc(list: openarray[SignedBeaconBlock]): bool {.gcsafe.}
|
||||||
|
|
||||||
SyncManager*[A, B] = ref object
|
SyncManager*[A, B] = ref object
|
||||||
groups*: seq[PeerGroup[A, B]]
|
groups*: seq[PeerGroup[A, B]]
|
||||||
|
@ -128,7 +128,7 @@ proc updateLastSlot*(sq: SyncQueue, last: Slot) {.inline.} =
|
||||||
sq.lastSlot = last
|
sq.lastSlot = last
|
||||||
|
|
||||||
proc push*(sq: SyncQueue, sr: SyncRequest,
|
proc push*(sq: SyncQueue, sr: SyncRequest,
|
||||||
data: seq[SignedBeaconBlock]) {.async.} =
|
data: seq[SignedBeaconBlock]) {.async, gcsafe.} =
|
||||||
## Push successfull result to queue ``sq``.
|
## Push successfull result to queue ``sq``.
|
||||||
while true:
|
while true:
|
||||||
if (sq.queueSize > 0) and (sr.slot >= sq.outSlot + uint64(sq.queueSize)):
|
if (sq.queueSize > 0) and (sr.slot >= sq.outSlot + uint64(sq.queueSize)):
|
||||||
|
@ -888,7 +888,7 @@ proc updateStatus*[A, B](sman: SyncManager[A, B]) {.async.} =
|
||||||
pending[i].cancel()
|
pending[i].cancel()
|
||||||
raise exc
|
raise exc
|
||||||
|
|
||||||
proc synchronize*[A, B](sman: SyncManager[A, B]) {.async.} =
|
proc synchronize*[A, B](sman: SyncManager[A, B]) {.async, gcsafe.} =
|
||||||
## TODO: This synchronization procedure is not optimal, we can do it better
|
## TODO: This synchronization procedure is not optimal, we can do it better
|
||||||
## if spawn N parallel tasks, where N is number of peer groups.
|
## if spawn N parallel tasks, where N is number of peer groups.
|
||||||
var
|
var
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
import
|
import
|
||||||
options, tables, sets, macros,
|
options, tables, sets, macros,
|
||||||
chronicles, chronos, stew/ranges/bitranges,
|
chronicles, chronos, stew/ranges/bitranges, libp2p/switch,
|
||||||
spec/[datatypes, crypto, digest, helpers],
|
spec/[datatypes, crypto, digest, helpers],
|
||||||
beacon_node_types, eth2_network, block_pool, ssz
|
beacon_node_types, eth2_network, block_pool, ssz
|
||||||
|
|
||||||
when networkBackend == libp2p:
|
|
||||||
import libp2p/switch
|
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
topics = "sync"
|
topics = "sync"
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ type
|
||||||
## which blocks are valid - in particular, blocks are not valid if they
|
## which blocks are valid - in particular, blocks are not valid if they
|
||||||
## come from the future as seen from the local clock.
|
## come from the future as seen from the local clock.
|
||||||
##
|
##
|
||||||
## https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/fork-choice.md#fork-choice
|
## https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/fork-choice.md#fork-choice
|
||||||
##
|
##
|
||||||
# TODO replace time in chronos with a proper unit type, then this code can
|
# TODO replace time in chronos with a proper unit type, then this code can
|
||||||
# follow:
|
# follow:
|
||||||
|
|
|
@ -60,7 +60,7 @@ proc sendDeposits*(
|
||||||
|
|
||||||
var web3 = await newWeb3(depositWeb3Url)
|
var web3 = await newWeb3(depositWeb3Url)
|
||||||
if privateKey.len != 0:
|
if privateKey.len != 0:
|
||||||
web3.privateKey = initPrivateKey(privateKey)
|
web3.privateKey = PrivateKey.fromHex(privateKey).tryGet()
|
||||||
else:
|
else:
|
||||||
let accounts = await web3.provider.eth_accounts()
|
let accounts = await web3.provider.eth_accounts()
|
||||||
if accounts.len == 0:
|
if accounts.len == 0:
|
||||||
|
|
|
@ -25,7 +25,8 @@ func getValidator*(pool: ValidatorPool,
|
||||||
pool.validators.getOrDefault(validatorKey)
|
pool.validators.getOrDefault(validatorKey)
|
||||||
|
|
||||||
# TODO: Honest validator - https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/validator.md
|
# TODO: Honest validator - https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/validator.md
|
||||||
proc signBlockProposal*(v: AttachedValidator, fork: Fork, slot: Slot,
|
proc signBlockProposal*(v: AttachedValidator, fork: Fork,
|
||||||
|
genesis_validators_root: Eth2Digest, slot: Slot,
|
||||||
blockRoot: Eth2Digest): Future[ValidatorSig] {.async.} =
|
blockRoot: Eth2Digest): Future[ValidatorSig] {.async.} =
|
||||||
|
|
||||||
if v.kind == inProcess:
|
if v.kind == inProcess:
|
||||||
|
@ -34,30 +35,33 @@ proc signBlockProposal*(v: AttachedValidator, fork: Fork, slot: Slot,
|
||||||
# replaced by something more sensible
|
# replaced by something more sensible
|
||||||
await sleepAsync(chronos.milliseconds(1))
|
await sleepAsync(chronos.milliseconds(1))
|
||||||
|
|
||||||
result = get_block_signature(fork, slot, blockRoot, v.privKey)
|
result = get_block_signature(
|
||||||
|
fork, genesis_validators_root, slot, blockRoot, v.privKey)
|
||||||
else:
|
else:
|
||||||
error "Unimplemented"
|
error "Unimplemented"
|
||||||
quit 1
|
quit 1
|
||||||
|
|
||||||
proc signAttestation*(v: AttachedValidator,
|
proc signAttestation*(v: AttachedValidator,
|
||||||
attestation: AttestationData,
|
attestation: AttestationData,
|
||||||
fork: Fork): Future[ValidatorSig] {.async.} =
|
fork: Fork, genesis_validators_root: Eth2Digest):
|
||||||
|
Future[ValidatorSig] {.async.} =
|
||||||
if v.kind == inProcess:
|
if v.kind == inProcess:
|
||||||
# TODO this is an ugly hack to fake a delay and subsequent async reordering
|
# TODO this is an ugly hack to fake a delay and subsequent async reordering
|
||||||
# for the purpose of testing the external validator delay - to be
|
# for the purpose of testing the external validator delay - to be
|
||||||
# replaced by something more sensible
|
# replaced by something more sensible
|
||||||
await sleepAsync(chronos.milliseconds(1))
|
await sleepAsync(chronos.milliseconds(1))
|
||||||
|
|
||||||
result = get_attestation_signature(fork, attestation, v.privKey)
|
result = get_attestation_signature(
|
||||||
|
fork, genesis_validators_root, attestation, v.privKey)
|
||||||
else:
|
else:
|
||||||
error "Unimplemented"
|
error "Unimplemented"
|
||||||
quit 1
|
quit 1
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/validator.md#randao-reveal
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#randao-reveal
|
||||||
func genRandaoReveal*(k: ValidatorPrivKey, fork: Fork, slot: Slot):
|
func genRandaoReveal*(k: ValidatorPrivKey, fork: Fork,
|
||||||
ValidatorSig =
|
genesis_validators_root: Eth2Digest, slot: Slot): ValidatorSig =
|
||||||
get_epoch_signature(fork, slot, k)
|
get_epoch_signature(fork, genesis_validators_root, slot, k)
|
||||||
|
|
||||||
func genRandaoReveal*(v: AttachedValidator, fork: Fork, slot: Slot):
|
func genRandaoReveal*(v: AttachedValidator, fork: Fork,
|
||||||
ValidatorSig =
|
genesis_validators_root: Eth2Digest, slot: Slot): ValidatorSig =
|
||||||
genRandaoReveal(v.privKey, fork, slot)
|
genRandaoReveal(v.privKey, fork, genesis_validators_root, slot)
|
||||||
|
|
|
@ -1,15 +1,3 @@
|
||||||
type
|
|
||||||
NetworkBackendType* = enum
|
|
||||||
libp2p
|
|
||||||
libp2pDaemon
|
|
||||||
|
|
||||||
const
|
|
||||||
NETWORK_TYPE {.strdefine.} = "libp2p_daemon"
|
|
||||||
|
|
||||||
networkBackend* = when NETWORK_TYPE == "libp2p": libp2p
|
|
||||||
elif NETWORK_TYPE == "libp2p_daemon": libp2pDaemon
|
|
||||||
else: {.fatal: "The 'NETWORK_TYPE' should be either 'libp2p', 'libp2p_daemon'" .}
|
|
||||||
|
|
||||||
const
|
const
|
||||||
copyrights* = "Copyright (c) 2019 Status Research & Development GmbH"
|
copyrights* = "Copyright (c) 2019 Status Research & Development GmbH"
|
||||||
|
|
||||||
|
@ -30,5 +18,5 @@ const
|
||||||
$versionMajor & "." & $versionMinor & "." & $versionBuild
|
$versionMajor & "." & $versionMinor & "." & $versionBuild
|
||||||
|
|
||||||
fullVersionStr* =
|
fullVersionStr* =
|
||||||
versionAsStr & " (" & gitRevision & ", " & NETWORK_TYPE & ")"
|
versionAsStr & " (" & gitRevision & ")"
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,8 @@ else:
|
||||||
# enable metric collection
|
# enable metric collection
|
||||||
--define:metrics
|
--define:metrics
|
||||||
--define:chronicles_line_numbers
|
--define:chronicles_line_numbers
|
||||||
|
# for heap-usage-by-instance-type metrics and object base-type strings
|
||||||
|
--define:nimTypeNames
|
||||||
|
|
||||||
# the default open files limit is too low on macOS (512), breaking the
|
# the default open files limit is too low on macOS (512), breaking the
|
||||||
# "--debugger:native" build. It can be increased with `ulimit -n 1024`.
|
# "--debugger:native" build. It can be increased with `ulimit -n 1024`.
|
||||||
|
@ -47,8 +49,7 @@ if not defined(macosx):
|
||||||
if not (defined(windows) and defined(i386)) and not defined(disable_libbacktrace):
|
if not (defined(windows) and defined(i386)) and not defined(disable_libbacktrace):
|
||||||
# light-weight stack traces using libbacktrace and libunwind
|
# light-weight stack traces using libbacktrace and libunwind
|
||||||
--define:nimStackTraceOverride
|
--define:nimStackTraceOverride
|
||||||
# "--import:libbacktrace" is added to NIM_PARAMS inside the Makefile,
|
switch("import", "libbacktrace")
|
||||||
# because it doesn't work in here ("Error: undeclared identifier: 'copyMem'", like it kicks in in some other NimScript file)
|
|
||||||
|
|
||||||
--define:nimOldCaseObjects # https://github.com/status-im/nim-confutils/issues/9
|
--define:nimOldCaseObjects # https://github.com/status-im/nim-confutils/issues/9
|
||||||
|
|
||||||
|
|
|
@ -15,11 +15,11 @@ RUN cd /root \
|
||||||
&& make -j$(nproc) update \
|
&& make -j$(nproc) update \
|
||||||
&& make deps
|
&& make deps
|
||||||
|
|
||||||
# Please note that the commands above have the goal of caching the compilation
|
# Please note that the commands above have the goal of caching the
|
||||||
# of Nim and p2pd, but don't depend on the current git revision. This means
|
# compilation of Nim, but don't depend on the current git revision.
|
||||||
# that the cache can become outdated over time and you'll start seeing Nim
|
# This means that the cache can become outdated over time and you'll
|
||||||
# being compiled on every run. If this happens, just prune your docker cache
|
# start seeing Nim being compiled on every run. If this happens, just
|
||||||
# to get a fresh up-to-date version of Nim and p2pd.
|
# prune your docker cache to get a fresh up-to-date version of Nim.
|
||||||
ARG GIT_REVISION
|
ARG GIT_REVISION
|
||||||
ARG NETWORK_NIM_FLAGS
|
ARG NETWORK_NIM_FLAGS
|
||||||
ARG MARCH_NIM_FLAGS
|
ARG MARCH_NIM_FLAGS
|
||||||
|
@ -43,13 +43,10 @@ RUN apt-get -qq update \
|
||||||
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
|
||||||
# "COPY" creates new image layers, so we cram all we can into one command
|
# "COPY" creates new image layers, so we cram all we can into one command
|
||||||
COPY --from=build /root/nim-beacon-chain/docker/run_in_docker.sh /root/nim-beacon-chain/build/beacon_node /root/nim-beacon-chain/vendor/go/bin/p2pd /usr/bin/
|
COPY --from=build /root/nim-beacon-chain/build/beacon_node /usr/bin/
|
||||||
|
|
||||||
MAINTAINER Zahary Karadjov <zahary@status.im>
|
MAINTAINER Zahary Karadjov <zahary@status.im>
|
||||||
LABEL description="Nimbus installation that can act as an ETH2 network bootstrap node."
|
LABEL description="Nimbus installation that can act as an ETH2 network bootstrap node."
|
||||||
|
|
||||||
# TODO: This custom entry script is necessary only because we must clean up
|
ENTRYPOINT ["/usr/bin/beacon_node"]
|
||||||
# temporary files left by previous executions of the Go daeamon.
|
|
||||||
# We should be able to remove it once we have a native LibP2P impl.
|
|
||||||
ENTRYPOINT ["/usr/bin/run_in_docker.sh"]
|
|
||||||
|
|
||||||
|
|
|
@ -103,7 +103,7 @@ of restart_nodes:
|
||||||
echo &"ssh {n.server} docker pull -q statusteam/nimbus_beacon_node:{conf.network}"
|
echo &"ssh {n.server} docker pull -q statusteam/nimbus_beacon_node:{conf.network}"
|
||||||
# docker-compose will rebuild the container if it detects a newer image.
|
# docker-compose will rebuild the container if it detects a newer image.
|
||||||
# Prints: "Recreating beacon-node-testnet1-1 ... done".
|
# Prints: "Recreating beacon-node-testnet1-1 ... done".
|
||||||
echo &"ssh {n.server} 'cd /docker/{n.container} && docker-compose up -d'"
|
echo &"ssh {n.server} 'cd /docker/{n.container} && docker-compose --compatibility up -d'"
|
||||||
|
|
||||||
of reset_network:
|
of reset_network:
|
||||||
for n, firstValidator, lastValidator in validatorAssignments():
|
for n, firstValidator, lastValidator in validatorAssignments():
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# TODO This script will no longer be necessary once we switch
|
|
||||||
# to the native LibP2P
|
|
||||||
|
|
||||||
# Deal with previous execution of the deamon leaving behind
|
|
||||||
# socket files that prevent the deamon from launching again
|
|
||||||
# inside the container:
|
|
||||||
killall p2pd
|
|
||||||
rm -rf /tmp/*
|
|
||||||
|
|
||||||
beacon_node "$@"
|
|
||||||
|
|
|
@ -100,8 +100,6 @@ cli do (skipGoerliKey {.
|
||||||
rmDir dataDir
|
rmDir dataDir
|
||||||
|
|
||||||
cd rootDir
|
cd rootDir
|
||||||
if testnet == "testnet1":
|
|
||||||
nimFlags &= " -d:NETWORK_TYPE=libp2p"
|
|
||||||
exec &"""nim c {nimFlags} -d:"const_preset={preset}" -o:"{beaconNodeBinary}" beacon_chain/beacon_node.nim"""
|
exec &"""nim c {nimFlags} -d:"const_preset={preset}" -o:"{beaconNodeBinary}" beacon_chain/beacon_node.nim"""
|
||||||
|
|
||||||
mkDir dumpDir
|
mkDir dumpDir
|
||||||
|
|
|
@ -141,10 +141,9 @@ BOOTSTRAP_IP="127.0.0.1"
|
||||||
--genesis-offset=5 # Delay in seconds
|
--genesis-offset=5 # Delay in seconds
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
killall beacon_node p2pd &>/dev/null || true
|
killall beacon_node &>/dev/null || true
|
||||||
sleep 2
|
sleep 2
|
||||||
killall -9 beacon_node p2pd &>/dev/null || true
|
killall -9 beacon_node &>/dev/null || true
|
||||||
rm -f /tmp/nim-p2pd-*.sock || true
|
|
||||||
}
|
}
|
||||||
cleanup
|
cleanup
|
||||||
|
|
||||||
|
@ -152,9 +151,6 @@ PIDS=""
|
||||||
NODES_WITH_VALIDATORS=${NODES_WITH_VALIDATORS:-4}
|
NODES_WITH_VALIDATORS=${NODES_WITH_VALIDATORS:-4}
|
||||||
VALIDATORS_PER_NODE=$(( $RANDOM_VALIDATORS / $NODES_WITH_VALIDATORS ))
|
VALIDATORS_PER_NODE=$(( $RANDOM_VALIDATORS / $NODES_WITH_VALIDATORS ))
|
||||||
|
|
||||||
# for the p2pd path
|
|
||||||
source env.sh
|
|
||||||
|
|
||||||
for NUM_NODE in $(seq 0 $(( ${NUM_NODES} - 1 ))); do
|
for NUM_NODE in $(seq 0 $(( ${NUM_NODES} - 1 ))); do
|
||||||
if [[ ${NUM_NODE} == 0 ]]; then
|
if [[ ${NUM_NODE} == 0 ]]; then
|
||||||
BOOTSTRAP_ARG=""
|
BOOTSTRAP_ARG=""
|
||||||
|
|
|
@ -16,7 +16,6 @@ add_var () {
|
||||||
}
|
}
|
||||||
|
|
||||||
add_var CONST_PRESET
|
add_var CONST_PRESET
|
||||||
add_var NETWORK_TYPE
|
|
||||||
add_var SLOTS_PER_EPOCH
|
add_var SLOTS_PER_EPOCH
|
||||||
add_var MAX_COMMITTEES_PER_SLOT
|
add_var MAX_COMMITTEES_PER_SLOT
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
CONST_PRESET=minimal
|
CONST_PRESET=minimal
|
||||||
NETWORK_TYPE=libp2p_daemon
|
|
||||||
QUICKSTART_VALIDATORS=8
|
QUICKSTART_VALIDATORS=8
|
||||||
RANDOM_VALIDATORS=120
|
RANDOM_VALIDATORS=120
|
||||||
BOOTSTRAP_PORT=9000
|
BOOTSTRAP_PORT=9000
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
CONST_PRESET=minimal
|
CONST_PRESET=minimal
|
||||||
NETWORK_TYPE=libp2p
|
|
||||||
QUICKSTART_VALIDATORS=8
|
QUICKSTART_VALIDATORS=8
|
||||||
RANDOM_VALIDATORS=120
|
RANDOM_VALIDATORS=120
|
||||||
BOOTSTRAP_PORT=9100
|
BOOTSTRAP_PORT=9100
|
||||||
|
|
|
@ -19,6 +19,7 @@ import # Unit test
|
||||||
./test_discovery_helpers,
|
./test_discovery_helpers,
|
||||||
./test_helpers,
|
./test_helpers,
|
||||||
./test_kvstore,
|
./test_kvstore,
|
||||||
|
./test_mocking,
|
||||||
./test_kvstore_sqlite3,
|
./test_kvstore_sqlite3,
|
||||||
./test_ssz,
|
./test_ssz,
|
||||||
./test_state_transition,
|
./test_state_transition,
|
||||||
|
@ -27,12 +28,8 @@ import # Unit test
|
||||||
./test_zero_signature,
|
./test_zero_signature,
|
||||||
./test_peer_pool,
|
./test_peer_pool,
|
||||||
./test_sync_manager,
|
./test_sync_manager,
|
||||||
./test_honest_validator
|
./test_honest_validator,
|
||||||
|
./test_interop
|
||||||
# ./test_interop
|
|
||||||
# TODO: BLS changes in v0.10.1 will generate different interop signatures
|
|
||||||
# Requires an update of the interop mocked start: https://github.com/ethereum/eth2.0-pm/tree/master/interop/mocked_start
|
|
||||||
# or of ZRNT / ZCLI to v0.10.1
|
|
||||||
|
|
||||||
import # Refactor state transition unit tests
|
import # Refactor state transition unit tests
|
||||||
# TODO re-enable when useful
|
# TODO re-enable when useful
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
# beacon_chain
|
# beacon_chain
|
||||||
# Copyright (c) 2018-2019 Status Research & Development GmbH
|
# Copyright (c) 2018-2020 Status Research & Development GmbH
|
||||||
# Licensed and distributed under either of
|
# Licensed and distributed under either of
|
||||||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/tests/core/pyspec/eth2spec/utils/merkle_minimal.py
|
||||||
|
|
||||||
# Merkle tree helpers
|
# Merkle tree helpers
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
|
|
||||||
import
|
import
|
||||||
|
strutils, macros, bitops,
|
||||||
# Specs
|
# Specs
|
||||||
../../beacon_chain/spec/[datatypes, digest],
|
../../beacon_chain/spec/[beaconstate, datatypes, digest],
|
||||||
../../beacon_chain/ssz
|
../../beacon_chain/ssz
|
||||||
|
|
||||||
func round_step_down*(x: Natural, step: static Natural): int {.inline.} =
|
func round_step_down*(x: Natural, step: static Natural): int {.inline.} =
|
||||||
|
@ -82,9 +85,7 @@ proc getMerkleProof*[Depth: static int](
|
||||||
else:
|
else:
|
||||||
result[depth] = ZeroHashes[depth]
|
result[depth] = ZeroHashes[depth]
|
||||||
|
|
||||||
when isMainModule: # Checks
|
proc testMerkleMinimal*(): bool =
|
||||||
import strutils, macros, bitops
|
|
||||||
|
|
||||||
proc toDigest[N: static int](x: array[N, byte]): Eth2Digest =
|
proc toDigest[N: static int](x: array[N, byte]): Eth2Digest =
|
||||||
result.data[0 .. N-1] = x
|
result.data[0 .. N-1] = x
|
||||||
|
|
||||||
|
@ -122,63 +123,64 @@ when isMainModule: # Checks
|
||||||
# Running tests with hash_tree_root([a, b, c])
|
# Running tests with hash_tree_root([a, b, c])
|
||||||
# works for depth 2 (3 or 4 leaves)
|
# works for depth 2 (3 or 4 leaves)
|
||||||
|
|
||||||
when false:
|
macro roundTrips(): untyped =
|
||||||
macro roundTrips(): untyped =
|
result = newStmtList()
|
||||||
result = newStmtList()
|
|
||||||
|
|
||||||
# Unsure why sszList ident is undeclared in "quote do"
|
# Unsure why sszList ident is undeclared in "quote do"
|
||||||
let list = bindSym"sszList"
|
let list = bindSym"sszList"
|
||||||
|
|
||||||
# compile-time unrolled test
|
# compile-time unrolled test
|
||||||
for nleaves in [3, 4, 5, 7, 8, 1 shl 10, 1 shl 32]:
|
for nleaves in [3, 4, 5, 7, 8, 1 shl 10, 1 shl 32]:
|
||||||
let depth = fastLog2(nleaves-1) + 1
|
let depth = fastLog2(nleaves-1) + 1
|
||||||
|
|
||||||
result.add quote do:
|
result.add quote do:
|
||||||
block:
|
block:
|
||||||
let tree = merkleTreeFromLeaves([a, b, c], Depth = `depth`)
|
let tree = merkleTreeFromLeaves([a, b, c], Depth = `depth`)
|
||||||
echo "Tree: ", tree
|
#echo "Tree: ", tree
|
||||||
|
|
||||||
let leaves = `list`(@[a, b, c], int64(`nleaves`))
|
doAssert tree.nnznodes[`depth`].len == 1
|
||||||
let root = hash_tree_root(leaves)
|
let root = tree.nnznodes[`depth`][0]
|
||||||
echo "Root: ", root
|
#echo "Root: ", root
|
||||||
|
|
||||||
block: # proof for a
|
block: # proof for a
|
||||||
let index = 0
|
let index = 0
|
||||||
let proof = getMerkleProof(tree, index)
|
let proof = getMerkleProof(tree, index)
|
||||||
echo "Proof: ", proof
|
#echo "Proof: ", proof
|
||||||
|
|
||||||
doAssert is_valid_merkle_branch(
|
doAssert is_valid_merkle_branch(
|
||||||
a, get_merkle_proof(tree, index = index),
|
a, get_merkle_proof(tree, index = index),
|
||||||
depth = `depth`,
|
depth = `depth`,
|
||||||
index = index.uint64,
|
index = index.uint64,
|
||||||
root = root
|
root = root
|
||||||
), "Failed (depth: " & $`depth` &
|
), "Failed (depth: " & $`depth` &
|
||||||
", nleaves: " & $`nleaves` & ')'
|
", nleaves: " & $`nleaves` & ')'
|
||||||
|
|
||||||
block: # proof for b
|
block: # proof for b
|
||||||
let index = 1
|
let index = 1
|
||||||
let proof = getMerkleProof(tree, index)
|
let proof = getMerkleProof(tree, index)
|
||||||
# echo "Proof: ", proof
|
|
||||||
|
|
||||||
doAssert is_valid_merkle_branch(
|
doAssert is_valid_merkle_branch(
|
||||||
b, get_merkle_proof(tree, index = index),
|
b, get_merkle_proof(tree, index = index),
|
||||||
depth = `depth`,
|
depth = `depth`,
|
||||||
index = index.uint64,
|
index = index.uint64,
|
||||||
root = root
|
root = root
|
||||||
), "Failed (depth: " & $`depth` &
|
), "Failed (depth: " & $`depth` &
|
||||||
", nleaves: " & $`nleaves` & ')'
|
", nleaves: " & $`nleaves` & ')'
|
||||||
|
|
||||||
block: # proof for c
|
block: # proof for c
|
||||||
let index = 2
|
let index = 2
|
||||||
let proof = getMerkleProof(tree, index)
|
let proof = getMerkleProof(tree, index)
|
||||||
# echo "Proof: ", proof
|
|
||||||
|
|
||||||
doAssert is_valid_merkle_branch(
|
doAssert is_valid_merkle_branch(
|
||||||
c, get_merkle_proof(tree, index = index),
|
c, get_merkle_proof(tree, index = index),
|
||||||
depth = `depth`,
|
depth = `depth`,
|
||||||
index = index.uint64,
|
index = index.uint64,
|
||||||
root = root
|
root = root
|
||||||
), "Failed (depth: " & $`depth` &
|
), "Failed (depth: " & $`depth` &
|
||||||
", nleaves: " & $`nleaves` & ')'
|
", nleaves: " & $`nleaves` & ')'
|
||||||
|
|
||||||
roundTrips()
|
roundTrips()
|
||||||
|
true
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
discard testMerkleMinimal()
|
||||||
|
|
|
@ -66,7 +66,8 @@ proc signMockAttestation*(state: BeaconState, attestation: var Attestation) =
|
||||||
var first_iter = true # Can't do while loop on hashset
|
var first_iter = true # Can't do while loop on hashset
|
||||||
for validator_index in participants:
|
for validator_index in participants:
|
||||||
let sig = get_attestation_signature(
|
let sig = get_attestation_signature(
|
||||||
state.fork, attestation.data, MockPrivKeys[validator_index]
|
state.fork, state.genesis_validators_root, attestation.data,
|
||||||
|
MockPrivKeys[validator_index]
|
||||||
)
|
)
|
||||||
if first_iter:
|
if first_iter:
|
||||||
attestation.signature = sig
|
attestation.signature = sig
|
||||||
|
|
|
@ -28,9 +28,10 @@ proc signMockBlockImpl(
|
||||||
let privkey = MockPrivKeys[proposer_index]
|
let privkey = MockPrivKeys[proposer_index]
|
||||||
|
|
||||||
signedBlock.message.body.randao_reveal = get_epoch_signature(
|
signedBlock.message.body.randao_reveal = get_epoch_signature(
|
||||||
state.fork, block_slot, privkey)
|
state.fork, state.genesis_validators_root, block_slot, privkey)
|
||||||
signedBlock.signature = get_block_signature(
|
signedBlock.signature = get_block_signature(
|
||||||
state.fork, block_slot, hash_tree_root(signedBlock.message), privkey)
|
state.fork, state.genesis_validators_root, block_slot,
|
||||||
|
hash_tree_root(signedBlock.message), privkey)
|
||||||
|
|
||||||
proc signMockBlock*(
|
proc signMockBlock*(
|
||||||
state: BeaconState,
|
state: BeaconState,
|
||||||
|
|
|
@ -36,7 +36,7 @@ proc readValue*(r: var JsonReader, a: var seq[byte]) {.inline.} =
|
||||||
|
|
||||||
const
|
const
|
||||||
FixturesDir* = currentSourcePath.rsplit(DirSep, 1)[0] / ".." / ".." / "vendor" / "nim-eth2-scenarios"
|
FixturesDir* = currentSourcePath.rsplit(DirSep, 1)[0] / ".." / ".." / "vendor" / "nim-eth2-scenarios"
|
||||||
SszTestsDir* = FixturesDir/"tests-v0.11.0"
|
SszTestsDir* = FixturesDir/"tests-v0.11.1"
|
||||||
|
|
||||||
proc parseTest*(path: string, Format: typedesc[Json or SSZ], T: typedesc): T =
|
proc parseTest*(path: string, Format: typedesc[Json or SSZ], T: typedesc): T =
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -19,7 +19,7 @@ import
|
||||||
const
|
const
|
||||||
SpecDir = currentSourcePath.rsplit(DirSep, 1)[0] /
|
SpecDir = currentSourcePath.rsplit(DirSep, 1)[0] /
|
||||||
".."/".."/"beacon_chain"/"spec"
|
".."/".."/"beacon_chain"/"spec"
|
||||||
Config = FixturesDir/"tests-v0.11.0"/const_preset/"config.yaml"
|
Config = FixturesDir/"tests-v0.11.1"/const_preset/"config.yaml"
|
||||||
|
|
||||||
type
|
type
|
||||||
CheckedType = SomeInteger or Slot or Epoch
|
CheckedType = SomeInteger or Slot or Epoch
|
||||||
|
@ -88,6 +88,7 @@ const
|
||||||
const IgnoreKeys = [
|
const IgnoreKeys = [
|
||||||
# Ignore all non-numeric types
|
# Ignore all non-numeric types
|
||||||
"DEPOSIT_CONTRACT_ADDRESS",
|
"DEPOSIT_CONTRACT_ADDRESS",
|
||||||
|
"GENESIS_FORK_VERSION",
|
||||||
"SHARD_BLOCK_OFFSETS"
|
"SHARD_BLOCK_OFFSETS"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -122,5 +123,5 @@ proc checkConfig() =
|
||||||
else:
|
else:
|
||||||
check: ConstsToCheck[constant] == value.getBiggestInt().uint64()
|
check: ConstsToCheck[constant] == value.getBiggestInt().uint64()
|
||||||
|
|
||||||
suiteReport "Official - 0.11.0 - constants & config " & preset():
|
suiteReport "Official - 0.11.1 - constants & config " & preset():
|
||||||
checkConfig()
|
checkConfig()
|
||||||
|
|
|
@ -30,10 +30,7 @@ proc runTest(identifier: string) =
|
||||||
|
|
||||||
proc `testImpl _ operations_attestations _ identifier`() =
|
proc `testImpl _ operations_attestations _ identifier`() =
|
||||||
|
|
||||||
var flags: UpdateFlags
|
|
||||||
var prefix: string
|
var prefix: string
|
||||||
if not existsFile(testDir/"meta.yaml"):
|
|
||||||
flags.incl skipBlsValidation
|
|
||||||
if existsFile(testDir/"post.ssz"):
|
if existsFile(testDir/"post.ssz"):
|
||||||
prefix = "[Valid] "
|
prefix = "[Valid] "
|
||||||
else:
|
else:
|
||||||
|
@ -55,10 +52,10 @@ proc runTest(identifier: string) =
|
||||||
postRef[] = parseTest(testDir/"post.ssz", SSZ, BeaconState)
|
postRef[] = parseTest(testDir/"post.ssz", SSZ, BeaconState)
|
||||||
|
|
||||||
if postRef.isNil:
|
if postRef.isNil:
|
||||||
let done = process_attestation(stateRef[], attestationRef[], flags, cache)
|
let done = process_attestation(stateRef[], attestationRef[], {}, cache)
|
||||||
doAssert done == false, "We didn't expect this invalid attestation to be processed."
|
doAssert done == false, "We didn't expect this invalid attestation to be processed."
|
||||||
else:
|
else:
|
||||||
let done = process_attestation(stateRef[], attestationRef[], flags, cache)
|
let done = process_attestation(stateRef[], attestationRef[], {}, cache)
|
||||||
doAssert done, "Valid attestation not processed"
|
doAssert done, "Valid attestation not processed"
|
||||||
check: stateRef.hash_tree_root() == postRef.hash_tree_root()
|
check: stateRef.hash_tree_root() == postRef.hash_tree_root()
|
||||||
reportDiff(stateRef, postRef)
|
reportDiff(stateRef, postRef)
|
||||||
|
|
|
@ -30,10 +30,7 @@ proc runTest(identifier: string) =
|
||||||
|
|
||||||
proc `testImpl _ operations_attester_slashing _ identifier`() =
|
proc `testImpl _ operations_attester_slashing _ identifier`() =
|
||||||
|
|
||||||
var flags: UpdateFlags
|
|
||||||
var prefix: string
|
var prefix: string
|
||||||
if not existsFile(testDir/"meta.yaml"):
|
|
||||||
flags.incl skipBlsValidation
|
|
||||||
if existsFile(testDir/"post.ssz"):
|
if existsFile(testDir/"post.ssz"):
|
||||||
prefix = "[Valid] "
|
prefix = "[Valid] "
|
||||||
else:
|
else:
|
||||||
|
@ -56,11 +53,11 @@ proc runTest(identifier: string) =
|
||||||
|
|
||||||
if postRef.isNil:
|
if postRef.isNil:
|
||||||
let done = process_attester_slashing(stateRef[], attesterSlashingRef[],
|
let done = process_attester_slashing(stateRef[], attesterSlashingRef[],
|
||||||
flags, cache)
|
{}, cache)
|
||||||
doAssert done == false, "We didn't expect this invalid attester slashing to be processed."
|
doAssert done == false, "We didn't expect this invalid attester slashing to be processed."
|
||||||
else:
|
else:
|
||||||
let done = process_attester_slashing(stateRef[], attesterSlashingRef[],
|
let done = process_attester_slashing(stateRef[], attesterSlashingRef[],
|
||||||
flags, cache)
|
{}, cache)
|
||||||
doAssert done, "Valid attestater slashing not processed"
|
doAssert done, "Valid attestater slashing not processed"
|
||||||
check: stateRef.hash_tree_root() == postRef.hash_tree_root()
|
check: stateRef.hash_tree_root() == postRef.hash_tree_root()
|
||||||
reportDiff(stateRef, postRef)
|
reportDiff(stateRef, postRef)
|
||||||
|
|
|
@ -30,10 +30,7 @@ proc runTest(identifier: string) =
|
||||||
|
|
||||||
proc `testImpl_proposer_slashing _ identifier`() =
|
proc `testImpl_proposer_slashing _ identifier`() =
|
||||||
|
|
||||||
var flags: UpdateFlags
|
|
||||||
var prefix: string
|
var prefix: string
|
||||||
if not existsFile(testDir/"meta.yaml"):
|
|
||||||
flags.incl skipBlsValidation
|
|
||||||
if existsFile(testDir/"post.ssz"):
|
if existsFile(testDir/"post.ssz"):
|
||||||
prefix = "[Valid] "
|
prefix = "[Valid] "
|
||||||
else:
|
else:
|
||||||
|
@ -55,10 +52,10 @@ proc runTest(identifier: string) =
|
||||||
var cache = get_empty_per_epoch_cache()
|
var cache = get_empty_per_epoch_cache()
|
||||||
|
|
||||||
if postRef.isNil:
|
if postRef.isNil:
|
||||||
let done = process_proposer_slashing(stateRef[], proposerSlashing[], flags, cache)
|
let done = process_proposer_slashing(stateRef[], proposerSlashing[], {}, cache)
|
||||||
doAssert done == false, "We didn't expect this invalid proposer slashing to be processed."
|
doAssert done == false, "We didn't expect this invalid proposer slashing to be processed."
|
||||||
else:
|
else:
|
||||||
let done = process_proposer_slashing(stateRef[], proposerSlashing[], flags, cache)
|
let done = process_proposer_slashing(stateRef[], proposerSlashing[], {}, cache)
|
||||||
doAssert done, "Valid proposer slashing not processed"
|
doAssert done, "Valid proposer slashing not processed"
|
||||||
check: stateRef.hash_tree_root() == postRef.hash_tree_root()
|
check: stateRef.hash_tree_root() == postRef.hash_tree_root()
|
||||||
reportDiff(stateRef, postRef)
|
reportDiff(stateRef, postRef)
|
||||||
|
|
|
@ -30,10 +30,7 @@ proc runTest(identifier: string) =
|
||||||
|
|
||||||
proc `testImpl _ voluntary_exit _ identifier`() =
|
proc `testImpl _ voluntary_exit _ identifier`() =
|
||||||
|
|
||||||
var flags: UpdateFlags
|
|
||||||
var prefix: string
|
var prefix: string
|
||||||
if not existsFile(testDir/"meta.yaml"):
|
|
||||||
flags.incl skipBlsValidation
|
|
||||||
if existsFile(testDir/"post.ssz"):
|
if existsFile(testDir/"post.ssz"):
|
||||||
prefix = "[Valid] "
|
prefix = "[Valid] "
|
||||||
else:
|
else:
|
||||||
|
@ -53,10 +50,10 @@ proc runTest(identifier: string) =
|
||||||
postRef[] = parseTest(testDir/"post.ssz", SSZ, BeaconState)
|
postRef[] = parseTest(testDir/"post.ssz", SSZ, BeaconState)
|
||||||
|
|
||||||
if postRef.isNil:
|
if postRef.isNil:
|
||||||
let done = process_voluntary_exit(stateRef[], voluntaryExit[], flags)
|
let done = process_voluntary_exit(stateRef[], voluntaryExit[], {})
|
||||||
doAssert done == false, "We didn't expect this invalid voluntary exit to be processed."
|
doAssert done == false, "We didn't expect this invalid voluntary exit to be processed."
|
||||||
else:
|
else:
|
||||||
let done = process_voluntary_exit(stateRef[], voluntaryExit[], flags)
|
let done = process_voluntary_exit(stateRef[], voluntaryExit[], {})
|
||||||
doAssert done, "Valid voluntary exit not processed"
|
doAssert done, "Valid voluntary exit not processed"
|
||||||
check: stateRef.hash_tree_root() == postRef.hash_tree_root()
|
check: stateRef.hash_tree_root() == postRef.hash_tree_root()
|
||||||
reportDiff(stateRef, postRef)
|
reportDiff(stateRef, postRef)
|
||||||
|
|
|
@ -25,7 +25,7 @@ import
|
||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
const
|
const
|
||||||
SSZDir = FixturesDir/"tests-v0.11.0"/const_preset/"phase0"/"ssz_static"
|
SSZDir = FixturesDir/"tests-v0.11.1"/const_preset/"phase0"/"ssz_static"
|
||||||
|
|
||||||
type
|
type
|
||||||
SSZHashTreeRoot = object
|
SSZHashTreeRoot = object
|
||||||
|
@ -106,7 +106,7 @@ proc runSSZtests() =
|
||||||
else:
|
else:
|
||||||
raise newException(ValueError, "Unsupported test: " & sszType)
|
raise newException(ValueError, "Unsupported test: " & sszType)
|
||||||
|
|
||||||
suiteReport "Official - 0.11.0 - SSZ consensus objects " & preset():
|
suiteReport "Official - 0.11.1 - SSZ consensus objects " & preset():
|
||||||
runSSZtests()
|
runSSZtests()
|
||||||
|
|
||||||
summarizeLongTests("FixtureSSZConsensus")
|
summarizeLongTests("FixtureSSZConsensus")
|
||||||
|
|
|
@ -23,7 +23,7 @@ import
|
||||||
# ------------------------------------------------------------------------
|
# ------------------------------------------------------------------------
|
||||||
|
|
||||||
const
|
const
|
||||||
SSZDir = FixturesDir/"tests-v0.11.0"/"general"/"phase0"/"ssz_generic"
|
SSZDir = FixturesDir/"tests-v0.11.1"/"general"/"phase0"/"ssz_generic"
|
||||||
|
|
||||||
type
|
type
|
||||||
SSZHashTreeRoot = object
|
SSZHashTreeRoot = object
|
||||||
|
|
|
@ -38,11 +38,8 @@ template runSuite(suiteDir, testName: string, transitionProc: untyped{ident}, us
|
||||||
|
|
||||||
let unitTestName = testDir.rsplit(DirSep, 1)[1]
|
let unitTestName = testDir.rsplit(DirSep, 1)[1]
|
||||||
timedTest testName & " - " & unitTestName & preset():
|
timedTest testName & " - " & unitTestName & preset():
|
||||||
var stateRef, postRef: ref BeaconState
|
let stateRef = parseTest(testDir/"pre.ssz", SSZ, ref BeaconState)
|
||||||
new stateRef
|
let postRef = parseTest(testDir/"post.ssz", SSZ, ref BeaconState)
|
||||||
new postRef
|
|
||||||
stateRef[] = parseTest(testDir/"pre.ssz", SSZ, BeaconState)
|
|
||||||
postRef[] = parseTest(testDir/"post.ssz", SSZ, BeaconState)
|
|
||||||
|
|
||||||
when useCache:
|
when useCache:
|
||||||
var cache = get_empty_per_epoch_cache()
|
var cache = get_empty_per_epoch_cache()
|
||||||
|
|
|
@ -9,6 +9,13 @@ shift
|
||||||
# shellcheck source=/dev/null
|
# shellcheck source=/dev/null
|
||||||
source "$(dirname "$0")/vars.sh"
|
source "$(dirname "$0")/vars.sh"
|
||||||
|
|
||||||
|
if [[ ! -z "$1" ]]; then
|
||||||
|
ADDITIONAL_BEACON_NODE_ARGS=$1
|
||||||
|
shift
|
||||||
|
else
|
||||||
|
ADDITIONAL_BEACON_NODE_ARGS=""
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ ! -z "$1" ]]; then
|
if [[ ! -z "$1" ]]; then
|
||||||
BOOTSTRAP_NODE_ID=$1
|
BOOTSTRAP_NODE_ID=$1
|
||||||
BOOTSTRAP_ADDRESS_FILE="${SIMULATION_DIR}/node-${BOOTSTRAP_NODE_ID}/beacon_node.address"
|
BOOTSTRAP_ADDRESS_FILE="${SIMULATION_DIR}/node-${BOOTSTRAP_NODE_ID}/beacon_node.address"
|
||||||
|
@ -47,13 +54,8 @@ fi
|
||||||
rm -rf "$DATA_DIR/dump"
|
rm -rf "$DATA_DIR/dump"
|
||||||
mkdir -p "$DATA_DIR/dump"
|
mkdir -p "$DATA_DIR/dump"
|
||||||
|
|
||||||
NODE_BIN=$BEACON_NODE_BIN
|
|
||||||
if [[ $NODE_ID == $MASTER_NODE ]]; then
|
|
||||||
NODE_BIN=$BOOTSTRAP_NODE_BIN
|
|
||||||
fi
|
|
||||||
|
|
||||||
# if you want tracing messages, add "--log-level=TRACE" below
|
# if you want tracing messages, add "--log-level=TRACE" below
|
||||||
cd "$DATA_DIR" && $NODE_BIN \
|
cd "$DATA_DIR" && $BEACON_NODE_BIN \
|
||||||
--log-level=${LOG_LEVEL:-DEBUG} \
|
--log-level=${LOG_LEVEL:-DEBUG} \
|
||||||
--bootstrap-file=$BOOTSTRAP_ADDRESS_FILE \
|
--bootstrap-file=$BOOTSTRAP_ADDRESS_FILE \
|
||||||
--data-dir=$DATA_DIR \
|
--data-dir=$DATA_DIR \
|
||||||
|
@ -64,12 +66,11 @@ cd "$DATA_DIR" && $NODE_BIN \
|
||||||
--state-snapshot=$SNAPSHOT_FILE \
|
--state-snapshot=$SNAPSHOT_FILE \
|
||||||
$DEPOSIT_WEB3_URL_ARG \
|
$DEPOSIT_WEB3_URL_ARG \
|
||||||
--deposit-contract=$DEPOSIT_CONTRACT_ADDRESS \
|
--deposit-contract=$DEPOSIT_CONTRACT_ADDRESS \
|
||||||
--verify-finalization \
|
|
||||||
--rpc \
|
--rpc \
|
||||||
--rpc-address="127.0.0.1" \
|
--rpc-address="127.0.0.1" \
|
||||||
--rpc-port="$(( $BASE_RPC_PORT + $NODE_ID ))" \
|
--rpc-port="$(( $BASE_RPC_PORT + $NODE_ID ))" \
|
||||||
--metrics \
|
--metrics \
|
||||||
--metrics-address="127.0.0.1" \
|
--metrics-address="127.0.0.1" \
|
||||||
--metrics-port="$(( $BASE_METRICS_PORT + $NODE_ID ))" \
|
--metrics-port="$(( $BASE_METRICS_PORT + $NODE_ID ))" \
|
||||||
|
${ADDITIONAL_BEACON_NODE_ARGS} \
|
||||||
"$@"
|
"$@"
|
||||||
|
|
||||||
|
|
|
@ -39,18 +39,10 @@ build_beacon_node () {
|
||||||
$MAKE NIMFLAGS="-o:$OUTPUT_BIN $PARAMS" LOG_LEVEL="${LOG_LEVEL:-DEBUG}" beacon_node
|
$MAKE NIMFLAGS="-o:$OUTPUT_BIN $PARAMS" LOG_LEVEL="${LOG_LEVEL:-DEBUG}" beacon_node
|
||||||
}
|
}
|
||||||
|
|
||||||
build_beacon_node $BEACON_NODE_BIN -d:"NETWORK_TYPE=$NETWORK_TYPE"
|
build_beacon_node $BEACON_NODE_BIN
|
||||||
|
|
||||||
if [[ "$BOOTSTRAP_NODE_NETWORK_TYPE" != "$NETWORK_TYPE" ]]; then
|
|
||||||
build_beacon_node $BOOTSTRAP_NODE_BIN \
|
|
||||||
--nimcache:nimcache/bootstrap_node \
|
|
||||||
-d:"NETWORK_TYPE=$BOOTSTRAP_NODE_NETWORK_TYPE"
|
|
||||||
else
|
|
||||||
cp $BEACON_NODE_BIN $BOOTSTRAP_NODE_BIN
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f "${LAST_VALIDATOR}" ]; then
|
if [ ! -f "${LAST_VALIDATOR}" ]; then
|
||||||
echo Building $DEPLOY_DEPOSIT_CONTRACT_BIN
|
echo Building "${DEPLOY_DEPOSIT_CONTRACT_BIN}"
|
||||||
$MAKE NIMFLAGS="-o:\"$DEPLOY_DEPOSIT_CONTRACT_BIN\" $CUSTOM_NIMFLAGS $DEFS" deposit_contract
|
$MAKE NIMFLAGS="-o:\"$DEPLOY_DEPOSIT_CONTRACT_BIN\" $CUSTOM_NIMFLAGS $DEFS" deposit_contract
|
||||||
|
|
||||||
if [ "$DEPOSIT_WEB3_URL_ARG" != "" ]; then
|
if [ "$DEPOSIT_WEB3_URL_ARG" != "" ]; then
|
||||||
|
@ -92,10 +84,10 @@ TMUX_SESSION_NAME="${TMUX_SESSION_NAME:-nbc-network-sim}"
|
||||||
|
|
||||||
# Using tmux or multitail is an opt-in
|
# Using tmux or multitail is an opt-in
|
||||||
USE_MULTITAIL="${USE_MULTITAIL:-no}"
|
USE_MULTITAIL="${USE_MULTITAIL:-no}"
|
||||||
type "$MULTITAIL" &>/dev/null || { echo $MULTITAIL is missing; USE_MULTITAIL="no"; }
|
type "$MULTITAIL" &>/dev/null || { echo "${MULTITAIL}" is missing; USE_MULTITAIL="no"; }
|
||||||
|
|
||||||
USE_TMUX="${USE_TMUX:-no}"
|
USE_TMUX="${USE_TMUX:-no}"
|
||||||
type "$TMUX" &>/dev/null || { echo $TMUX is missing; USE_TMUX="no"; }
|
type "$TMUX" &>/dev/null || { echo "${TMUX}" is missing; USE_TMUX="no"; }
|
||||||
|
|
||||||
# Prometheus config (continued inside the loop)
|
# Prometheus config (continued inside the loop)
|
||||||
mkdir -p "${METRICS_DIR}"
|
mkdir -p "${METRICS_DIR}"
|
||||||
|
@ -125,18 +117,18 @@ fi
|
||||||
# Trap and ignore SIGTERM, so we don't kill this process along with its children.
|
# Trap and ignore SIGTERM, so we don't kill this process along with its children.
|
||||||
if [ "$USE_MULTITAIL" = "no" ]; then
|
if [ "$USE_MULTITAIL" = "no" ]; then
|
||||||
trap '' SIGTERM
|
trap '' SIGTERM
|
||||||
trap 'kill -- -$$' SIGINT EXIT
|
trap 'pkill -P $$ beacon_node' SIGINT EXIT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
COMMANDS=()
|
COMMANDS=()
|
||||||
|
|
||||||
if [[ "$USE_TMUX" != "no" ]]; then
|
if [[ "$USE_TMUX" != "no" ]]; then
|
||||||
$TMUX new-session -s $TMUX_SESSION_NAME -d
|
$TMUX new-session -s "${TMUX_SESSION_NAME}" -d
|
||||||
|
|
||||||
# maybe these should be moved to a user config file
|
# maybe these should be moved to a user config file
|
||||||
$TMUX set-option -t $TMUX_SESSION_NAME history-limit 999999
|
$TMUX set-option -t "${TMUX_SESSION_NAME}" history-limit 999999
|
||||||
$TMUX set-option -t $TMUX_SESSION_NAME remain-on-exit on
|
$TMUX set-option -t "${TMUX_SESSION_NAME}" remain-on-exit on
|
||||||
$TMUX set -t $TMUX_SESSION_NAME mouse on
|
$TMUX set -t "${TMUX_SESSION_NAME}" mouse on
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for i in $(seq $MASTER_NODE -1 $TOTAL_USER_NODES); do
|
for i in $(seq $MASTER_NODE -1 $TOTAL_USER_NODES); do
|
||||||
|
@ -147,11 +139,11 @@ for i in $(seq $MASTER_NODE -1 $TOTAL_USER_NODES); do
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
CMD="${SIM_ROOT}/run_node.sh $i"
|
CMD="${SIM_ROOT}/run_node.sh ${i} --verify-finalization"
|
||||||
|
|
||||||
if [[ "$USE_TMUX" != "no" ]]; then
|
if [[ "$USE_TMUX" != "no" ]]; then
|
||||||
$TMUX split-window -t $TMUX_SESSION_NAME "$CMD"
|
$TMUX split-window -t "${TMUX_SESSION_NAME}" "$CMD"
|
||||||
$TMUX select-layout -t $TMUX_SESSION_NAME tiled
|
$TMUX select-layout -t "${TMUX_SESSION_NAME}" tiled
|
||||||
elif [[ "$USE_MULTITAIL" != "no" ]]; then
|
elif [[ "$USE_MULTITAIL" != "no" ]]; then
|
||||||
if [[ "$i" == "$MASTER_NODE" ]]; then
|
if [[ "$i" == "$MASTER_NODE" ]]; then
|
||||||
SLEEP="0"
|
SLEEP="0"
|
||||||
|
@ -166,7 +158,7 @@ for i in $(seq $MASTER_NODE -1 $TOTAL_USER_NODES); do
|
||||||
|
|
||||||
# Prometheus config
|
# Prometheus config
|
||||||
cat >> "${METRICS_DIR}/prometheus.yml" <<EOF
|
cat >> "${METRICS_DIR}/prometheus.yml" <<EOF
|
||||||
- targets: ['127.0.0.1:$(( $BASE_METRICS_PORT + $i ))']
|
- targets: ['127.0.0.1:$(( BASE_METRICS_PORT + i ))']
|
||||||
labels:
|
labels:
|
||||||
node: '$i'
|
node: '$i'
|
||||||
EOF
|
EOF
|
||||||
|
@ -174,8 +166,8 @@ done
|
||||||
|
|
||||||
if [[ "$USE_TMUX" != "no" ]]; then
|
if [[ "$USE_TMUX" != "no" ]]; then
|
||||||
$TMUX kill-pane -t $TMUX_SESSION_NAME:0.0
|
$TMUX kill-pane -t $TMUX_SESSION_NAME:0.0
|
||||||
$TMUX select-layout -t $TMUX_SESSION_NAME tiled
|
$TMUX select-layout -t "${TMUX_SESSION_NAME}" tiled
|
||||||
$TMUX attach-session -t $TMUX_SESSION_NAME -d
|
$TMUX attach-session -t "${TMUX_SESSION_NAME}" -d
|
||||||
elif [[ "$USE_MULTITAIL" != "no" ]]; then
|
elif [[ "$USE_MULTITAIL" != "no" ]]; then
|
||||||
eval $MULTITAIL -s 3 -M 0 -x \"Nimbus beacon chain\" "${COMMANDS[@]}"
|
eval $MULTITAIL -s 3 -M 0 -x \"Nimbus beacon chain\" "${COMMANDS[@]}"
|
||||||
else
|
else
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Read in variables
|
|
||||||
set -a
|
|
||||||
# shellcheck source=/dev/null
|
|
||||||
source "$(dirname "$0")/vars.sh"
|
|
||||||
|
|
||||||
cd $(dirname "$0")
|
|
||||||
rm -rf data
|
|
||||||
|
|
||||||
tmux new-session -s 'beacon_node' -d
|
|
||||||
|
|
||||||
# maybe these should be moved to a user config file
|
|
||||||
tmux set-option -g history-limit 999999
|
|
||||||
tmux set -g mouse on
|
|
||||||
|
|
||||||
tmux send-keys -t 0 './start.sh' Enter
|
|
||||||
tmux new-window -n "demo_node" "./wait_master_node.sh && ./run_node.sh 0"
|
|
||||||
|
|
||||||
tmux attach-session -d
|
|
||||||
|
|
|
@ -25,18 +25,12 @@ TOTAL_USER_NODES=${USER_NODES:-0}
|
||||||
TOTAL_SYSTEM_NODES=$(( TOTAL_NODES - TOTAL_USER_NODES ))
|
TOTAL_SYSTEM_NODES=$(( TOTAL_NODES - TOTAL_USER_NODES ))
|
||||||
MASTER_NODE=$(( TOTAL_NODES - 1 ))
|
MASTER_NODE=$(( TOTAL_NODES - 1 ))
|
||||||
|
|
||||||
# You can run a mixed simulation of daemon and native libp2p nodes
|
|
||||||
# by changing the variables below:
|
|
||||||
NETWORK_TYPE=${NETWORK_TYPE:-"libp2p"}
|
|
||||||
BOOTSTRAP_NODE_NETWORK_TYPE=${BOOTSTRAP_NODE_NETWORK_TYPE:-"libp2p"}
|
|
||||||
|
|
||||||
SIMULATION_DIR="${SIM_ROOT}/data"
|
SIMULATION_DIR="${SIM_ROOT}/data"
|
||||||
METRICS_DIR="${SIM_ROOT}/prometheus"
|
METRICS_DIR="${SIM_ROOT}/prometheus"
|
||||||
VALIDATORS_DIR="${SIM_ROOT}/validators"
|
VALIDATORS_DIR="${SIM_ROOT}/validators"
|
||||||
SNAPSHOT_FILE="${SIMULATION_DIR}/state_snapshot.ssz"
|
SNAPSHOT_FILE="${SIMULATION_DIR}/state_snapshot.ssz"
|
||||||
NETWORK_BOOTSTRAP_FILE="${SIMULATION_DIR}/bootstrap_nodes.txt"
|
NETWORK_BOOTSTRAP_FILE="${SIMULATION_DIR}/bootstrap_nodes.txt"
|
||||||
BEACON_NODE_BIN="${SIMULATION_DIR}/beacon_node"
|
BEACON_NODE_BIN="${SIMULATION_DIR}/beacon_node"
|
||||||
BOOTSTRAP_NODE_BIN="${SIMULATION_DIR}/bootstrap_node"
|
|
||||||
DEPLOY_DEPOSIT_CONTRACT_BIN="${SIMULATION_DIR}/deploy_deposit_contract"
|
DEPLOY_DEPOSIT_CONTRACT_BIN="${SIMULATION_DIR}/deploy_deposit_contract"
|
||||||
MASTER_NODE_ADDRESS_FILE="${SIMULATION_DIR}/node-${MASTER_NODE}/beacon_node.address"
|
MASTER_NODE_ADDRESS_FILE="${SIMULATION_DIR}/node-${MASTER_NODE}/beacon_node.address"
|
||||||
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
if [ ! -f "${MASTER_NODE_ADDRESS_FILE}" ]; then
|
|
||||||
echo Waiting for master node...
|
|
||||||
while [ ! -f "${MASTER_NODE_ADDRESS_FILE}" ]; do
|
|
||||||
sleep 0.1
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
|
@ -3,11 +3,7 @@
|
||||||
import
|
import
|
||||||
unittest, stint, blscurve, ./testutil, stew/byteutils,
|
unittest, stint, blscurve, ./testutil, stew/byteutils,
|
||||||
../beacon_chain/[extras, interop, ssz],
|
../beacon_chain/[extras, interop, ssz],
|
||||||
../beacon_chain/spec/[beaconstate, crypto, helpers, datatypes]
|
../beacon_chain/spec/[beaconstate, crypto, datatypes]
|
||||||
|
|
||||||
# TODO: BLS changes in v0.10.1 will generate different interop signatures
|
|
||||||
# Requires an update of the interop mocked start
|
|
||||||
# or of ZRNT / ZCLI to v0.10.1
|
|
||||||
|
|
||||||
# Interop test yaml, found here:
|
# Interop test yaml, found here:
|
||||||
# https://github.com/ethereum/eth2.0-pm/blob/a0b9d22fad424574b1307828f867b30237758468/interop/mocked_start/keygen_10_validators.yaml
|
# https://github.com/ethereum/eth2.0-pm/blob/a0b9d22fad424574b1307828f867b30237758468/interop/mocked_start/keygen_10_validators.yaml
|
||||||
|
@ -35,87 +31,89 @@ type DepositConfig = object
|
||||||
# - https://github.com/status-im/eth2.0-specs/blob/c58096754b62389b0ea75dbdd717d362691b7c34/test_libs/pyspec/mockup_genesis.py
|
# - https://github.com/status-im/eth2.0-specs/blob/c58096754b62389b0ea75dbdd717d362691b7c34/test_libs/pyspec/mockup_genesis.py
|
||||||
# - "zcli genesis mock" https://github.com/protolambda/zcli
|
# - "zcli genesis mock" https://github.com/protolambda/zcli
|
||||||
|
|
||||||
|
func fromHex(T: type[ValidatorSig], hex: string): T = result.fromhex(hex)
|
||||||
|
|
||||||
let depositsConfig = [
|
let depositsConfig = [
|
||||||
DepositConfig(
|
DepositConfig(
|
||||||
privkey: ValidatorPrivKey.init(hexToSeqByte"0x25295f0d1d592a90b333e26e85149708208e9f8e8bc18f6c77bd62f8ad7a6866"),
|
privkey: ValidatorPrivKey.init("0x25295f0d1d592a90b333e26e85149708208e9f8e8bc18f6c77bd62f8ad7a6866"),
|
||||||
signing_root: hexToByteArray[32]("139b510ea7f2788ab82da1f427d6cbe1db147c15a053db738ad5500cd83754a6"),
|
signing_root: hexToByteArray[32]("139b510ea7f2788ab82da1f427d6cbe1db147c15a053db738ad5500cd83754a6"),
|
||||||
domain: DOMAIN_DEPOSIT,
|
domain: DOMAIN_DEPOSIT,
|
||||||
sig: ValidatorSig.fromHex"8684b7f46d25cdd6f937acdaa54bdd2fb34c78d687dca93884ba79e60ebb0df964faa4c49f3469fb882a50c7726985ff0b20c9584cc1ded7c90467422674a05177b2019661f78a5c5c56f67d586f04fd37f555b4876a910bedff830c2bece0aa"
|
sig: ValidatorSig.fromHex"b796b670fa7eb04b4422bb0872b016895a6adffb1ebd1023db41452701ad65d6fa53d84f3b62e8753bf55230364c6aa318620b574528506ad78517f70c688b82d1c9ad0b12633e0fa5792cf58c21cee9ad25f74156eebd0b6dcd548b91db860f"
|
||||||
), DepositConfig(
|
), DepositConfig(
|
||||||
privkey: ValidatorPrivKey.init(hexToSeqByte"0x51d0b65185db6989ab0b560d6deed19c7ead0e24b9b6372cbecb1f26bdfad000"),
|
privkey: ValidatorPrivKey.init("0x51d0b65185db6989ab0b560d6deed19c7ead0e24b9b6372cbecb1f26bdfad000"),
|
||||||
signing_root: hexToByteArray[32]("bb4b6184b25873cdf430df3838c8d3e3d16cf3dc3b214e2f3ab7df9e6d5a9b52"),
|
signing_root: hexToByteArray[32]("bb4b6184b25873cdf430df3838c8d3e3d16cf3dc3b214e2f3ab7df9e6d5a9b52"),
|
||||||
domain: DOMAIN_DEPOSIT,
|
domain: DOMAIN_DEPOSIT,
|
||||||
sig: ValidatorSig.fromHex"a2c86c4f654a2a229a287aabc8c63f224d9fb8e1d77d4a13276a87a80c8b75aa7c55826febe4bae6c826aeeccaa82f370517db4f0d5eed5fbc06a3846088871696b3c32ff3fdebdb52355d1eede85bcd71aaa2c00d6cf088a647332edc21e4f3"
|
sig: ValidatorSig.fromHex"98c4c6a7e12a2b4aeaa23a7d6ae4d2acabc8193d1c1cb53fabcb107ebcbd9c04189c4278995c62883507926712133d941677bd15407eefa49ea6c1cb97f4f7ee4efc3fe0bfa80e3efc3c6b48646b06e6bb845c4e0e7f21df58ef67147f0da7ea"
|
||||||
), DepositConfig(
|
), DepositConfig(
|
||||||
privkey: ValidatorPrivKey.init(hexToSeqByte"0x315ed405fafe339603932eebe8dbfd650ce5dafa561f6928664c75db85f97857"),
|
privkey: ValidatorPrivKey.init("0x315ed405fafe339603932eebe8dbfd650ce5dafa561f6928664c75db85f97857"),
|
||||||
signing_root: hexToByteArray[32]("c6ddd74b1b45db17a864c87dd941cb6c6e16540c534cdbe1cc0d43e9a5d87f7c"),
|
signing_root: hexToByteArray[32]("c6ddd74b1b45db17a864c87dd941cb6c6e16540c534cdbe1cc0d43e9a5d87f7c"),
|
||||||
domain: DOMAIN_DEPOSIT,
|
domain: DOMAIN_DEPOSIT,
|
||||||
sig: ValidatorSig.fromHex"a5a463d036e9ccb19757b2ddb1e6564a00463aed1ef51bf69264a14b6bfcff93eb6f63664e0df0b5c9e6760c560cb58d135265cecbf360a23641af627bcb17cf6c0541768d3f3b61e27f7c44f21b02cd09b52443405b12fb541f5762cd615d6e"
|
sig: ValidatorSig.fromHex"8e6163059668ff2db1c8d430a1b0f9aeb330e8eaf680ed9709aaff5d437a54fb0144f2703cbb1e2a4a67c505b534718d0450d99203cccaf18e442bddd27e93ebfa289e6ce30a92e7befb656f12a01cb0204ffd14eed39ae457b7fad22faf8eab"
|
||||||
), DepositConfig(
|
), DepositConfig(
|
||||||
privkey: ValidatorPrivKey.init(hexToSeqByte"0x25b1166a43c109cb330af8945d364722757c65ed2bfed5444b5a2f057f82d391"),
|
privkey: ValidatorPrivKey.init("0x25b1166a43c109cb330af8945d364722757c65ed2bfed5444b5a2f057f82d391"),
|
||||||
signing_root: hexToByteArray[32]("9397cd33d4e8883dbdc1a1d7df410aa2b627740d11c5574697a2d483a50ab7bb"),
|
signing_root: hexToByteArray[32]("9397cd33d4e8883dbdc1a1d7df410aa2b627740d11c5574697a2d483a50ab7bb"),
|
||||||
domain: DOMAIN_DEPOSIT,
|
domain: DOMAIN_DEPOSIT,
|
||||||
sig: ValidatorSig.fromHex"8731c258353c8aa46a8e38509eecfdc32018429239d9acad9b634a4d010ca51395828c0c056808c6e6df373fef7e9a570b3d648ec455d90f497e12fc3011148eded7265b0f995de72e5982db1dbb6eca8275fc99cdd10704b8cf19ec0bb9c350"
|
sig: ValidatorSig.fromHex"b389e7b4db5caccad6b0b32394b1e77a814e519f4d0789a1e4bb20e2f7f68d7787fe5f065181eeab72d31d847ae96abc0512466689eafbee0439ab7229fb14272654815f535759467e012d9ab7db6e3b3e86d9f73742c46993c755d1f2893684"
|
||||||
), DepositConfig(
|
), DepositConfig(
|
||||||
privkey: ValidatorPrivKey.init(hexToSeqByte"0x3f5615898238c4c4f906b507ee917e9ea1bb69b93f1dbd11a34d229c3b06784b"),
|
privkey: ValidatorPrivKey.init("0x3f5615898238c4c4f906b507ee917e9ea1bb69b93f1dbd11a34d229c3b06784b"),
|
||||||
signing_root: hexToByteArray[32]("27340cc0f3b76bcc89c78e67166c13a58c97c232889391d1387fc404c4f5255e"),
|
signing_root: hexToByteArray[32]("27340cc0f3b76bcc89c78e67166c13a58c97c232889391d1387fc404c4f5255e"),
|
||||||
domain: DOMAIN_DEPOSIT,
|
domain: DOMAIN_DEPOSIT,
|
||||||
sig: ValidatorSig.fromHex"90b20f054f6a2823d66e159050915335e7a4f64bf7ac449ef83bb1d1ba9a6b2385da977b5ba295ea2d019ee3a8140607079d671352ab233b3bf6be45c61dce5b443f23716d64382e34d7676ae64eedd01babeeb8bfd26386371f6bc01f1d4539"
|
sig: ValidatorSig.fromHex"aeb410612b19c3176fa087fab3e56e278a01cf5ba5379aa7f4e7344dbfa9e3b3f91b6f39af463ce2e448787b0a77ee1a05f22c0d9afd2f0f6137232c432f83c26389c07a8348364ab8a745eda59ecf2aa65fa8eb3f18eacd10e5a8a2e71b1e06"
|
||||||
), DepositConfig(
|
), DepositConfig(
|
||||||
privkey: ValidatorPrivKey.init(hexToSeqByte"0x055794614bc85ed5436c1f5cab586aab6ca84835788621091f4f3b813761e7a8"),
|
privkey: ValidatorPrivKey.init("0x055794614bc85ed5436c1f5cab586aab6ca84835788621091f4f3b813761e7a8"),
|
||||||
signing_root: hexToByteArray[32]("b8cf48542d8531ae59b56e175228e7fcb82415649b5e992e132d3234b31dda2f"),
|
signing_root: hexToByteArray[32]("b8cf48542d8531ae59b56e175228e7fcb82415649b5e992e132d3234b31dda2f"),
|
||||||
domain: DOMAIN_DEPOSIT,
|
domain: DOMAIN_DEPOSIT,
|
||||||
sig: ValidatorSig.fromHex"99df72b850141c67fc956a5ba91abb5a091538d963aa6c082e1ea30b7f7e5a54ec0ff79c749342d4635e4901e8dfc9b90604d5466ff2a7b028c53d4dac01ffb3ac0555abd3f52d35aa1ece7e8e9cce273416b3cf582a5f2190e87a3b15641f0c"
|
sig: ValidatorSig.fromHex"b501a41ca61665dddbe248d2fa15e5498cb2b38dcf2093acd5768efeda1b0ac963e600d8e38c2c91964d8bf72fd197c71824c1d493272caf6140828f7f6b266281f044b4811bbd7ef0f57953b15399b4ef17af5b9c80df5c142600cf17bfee64"
|
||||||
), DepositConfig(
|
), DepositConfig(
|
||||||
privkey: ValidatorPrivKey.init(hexToSeqByte"0x1023c68852075965e0f7352dee3f76a84a83e7582c181c10179936c6d6348893"),
|
privkey: ValidatorPrivKey.init("0x1023c68852075965e0f7352dee3f76a84a83e7582c181c10179936c6d6348893"),
|
||||||
signing_root: hexToByteArray[32]("5f919d91faecece67422edf573a507fc5f9720f4e37063cceb40aa3b371f1aa9"),
|
signing_root: hexToByteArray[32]("5f919d91faecece67422edf573a507fc5f9720f4e37063cceb40aa3b371f1aa9"),
|
||||||
domain: DOMAIN_DEPOSIT,
|
domain: DOMAIN_DEPOSIT,
|
||||||
sig: ValidatorSig.fromHex"a4023f36f4f354f69b615b3651596d4b479f005b04f80ef878aaeb342e94ad6f9acddf237309a79247d560b05f4f7139048b5eee0f08da3a11f3ee148ca76e3e1351a733250515a61e12027468cff2de193ab8ee5cd90bdd1c50e529edda512b"
|
sig: ValidatorSig.fromHex"8f2e2de3c0504cc4d424de1593d508d7488bfc54f61882922b754e97e4faeebe4f24f19184f0630dc51327bc9ab26dd2073d55687f7284ab3395b770d7c4d35bb6e719e6881739e2f4f61e29e11c3b9e61529c202e30f5f5957544eeb0a9626e"
|
||||||
), DepositConfig(
|
), DepositConfig(
|
||||||
privkey: ValidatorPrivKey.init(hexToSeqByte"0x3a941600dc41e5d20e818473b817a28507c23cdfdb4b659c15461ee5c71e41f5"),
|
privkey: ValidatorPrivKey.init("0x3a941600dc41e5d20e818473b817a28507c23cdfdb4b659c15461ee5c71e41f5"),
|
||||||
signing_root: hexToByteArray[32]("d2ff8bfda7e7bcc64c636a4855d2a1eccb7f47379f526a753fd934ae37ba9ec7"),
|
signing_root: hexToByteArray[32]("d2ff8bfda7e7bcc64c636a4855d2a1eccb7f47379f526a753fd934ae37ba9ec7"),
|
||||||
domain: DOMAIN_DEPOSIT,
|
domain: DOMAIN_DEPOSIT,
|
||||||
sig: ValidatorSig.fromHex"81c52ada6d975a5b968509ab16fa58d617dd36a6c333e6ed86a7977030e4c5d37a488596c6776c2cdf4831ea7337ad7902020092f60e547714449253a947277681ff80b7bf641ca782214fc9ec9b58c66ab43c0a554c133073c96ad35edff101"
|
sig: ValidatorSig.fromHex"90a83842b6d215f1da3ebf3eeea6c4bff0682ee3f7aa9d06bb818c716cfdb5cd577f997ddd606c908f7a68157f36ff660a0e73265f17cccbd23be5ed053b3812672ba52bce6ec034fadea3b78f46a9c6da88db6327a18a9bb3a7f2747185fc6f"
|
||||||
), DepositConfig(
|
), DepositConfig(
|
||||||
privkey: ValidatorPrivKey.init(hexToSeqByte"0x066e3bdc0415530e5c7fed6382d5c822c192b620203cf669903e1810a8c67d06"),
|
privkey: ValidatorPrivKey.init("0x066e3bdc0415530e5c7fed6382d5c822c192b620203cf669903e1810a8c67d06"),
|
||||||
signing_root: hexToByteArray[32]("1e19687d32785632ddc9b6b319690ea45c0ea20d7bc8aacbd33f6ebbe30816e1"),
|
signing_root: hexToByteArray[32]("1e19687d32785632ddc9b6b319690ea45c0ea20d7bc8aacbd33f6ebbe30816e1"),
|
||||||
domain: DOMAIN_DEPOSIT,
|
domain: DOMAIN_DEPOSIT,
|
||||||
sig: ValidatorSig.fromHex"b4aab8f6624f61f4f5eb6d75839919a3ef6b4e1b19cae6ef063d6281b60ff1d5efe02bcbfc4b9eb1038c42e0a3325d8a0fcf7b64ff3cd9df5c629b864dfdc5b763283254ccd6cfa28cff53e477fb1743440a18d76a776ec4d66c5f50d695ca85"
|
sig: ValidatorSig.fromHex"a232a8bb03ecd356cf0e18644077880afe7ecfc565c8627841797deb4dfce8366cc0d0f6e151b51c0acc05a66f1363d204e8133e772dfb4878c11f7bf14b8293ce734c37adca9c32cc2987f0bc34242cc30f139d86c44f8d4383af743be3d1ae"
|
||||||
), DepositConfig(
|
), DepositConfig(
|
||||||
privkey: ValidatorPrivKey.init(hexToSeqByte"0x2b3b88a041168a1c4cd04bdd8de7964fd35238f95442dc678514f9dadb81ec34"),
|
privkey: ValidatorPrivKey.init("0x2b3b88a041168a1c4cd04bdd8de7964fd35238f95442dc678514f9dadb81ec34"),
|
||||||
signing_root: hexToByteArray[32]("64a910a0a3e7da9a7a29ee2c92859314a160040ffb2042641fc56cba75b78012"),
|
signing_root: hexToByteArray[32]("64a910a0a3e7da9a7a29ee2c92859314a160040ffb2042641fc56cba75b78012"),
|
||||||
domain: DOMAIN_DEPOSIT,
|
domain: DOMAIN_DEPOSIT,
|
||||||
sig: ValidatorSig.fromHex"9603f7dcab6822edb92eb588f1e15fcc685ceb8bcc7257adb0e4a5995820b8ef77215650792120aff871f30a52475ea31212aa741a3f0e6b2dbcb3a63181571306a411c772a7fd08826ddeab98d1c47b5ead82f8e063b9d7f1f217808ee4fb50"
|
sig: ValidatorSig.fromHex"8e0ccf7dd9dd00820a695161ea865220489ca48504012b7c36c85b3effb896a02ee9714a5e383f7105357a24f791562c1353e331d2cfa048cb94fd4fe42a008b18c5bdec6fcf7c8b75c5f5e582cd9571b308e8b1757d672fbb9092725985a716"
|
||||||
), DepositConfig(
|
), DepositConfig(
|
||||||
privkey: ValidatorPrivKey.init(hexToSeqByte"0x2e62dbea7fe3127c3b236a92795dd633be51ee7cdfe5424882a2f355df497117"),
|
privkey: ValidatorPrivKey.init("0x2e62dbea7fe3127c3b236a92795dd633be51ee7cdfe5424882a2f355df497117"),
|
||||||
signing_root: hexToByteArray[32]("5bf0c7a39df536b3c8a5dc550f0163af0b33a56b9454b5240cea9ad8356c4117"),
|
signing_root: hexToByteArray[32]("5bf0c7a39df536b3c8a5dc550f0163af0b33a56b9454b5240cea9ad8356c4117"),
|
||||||
domain: DOMAIN_DEPOSIT,
|
domain: DOMAIN_DEPOSIT,
|
||||||
sig: ValidatorSig.fromHex"92b04a4128e84b827b46fd91611acc46f97826d13fbdcbf000b6b3585edd8629e38d4c13f7f3fde5a1170f4f3f55bef21883498602396c875275cb2c795d4488383b1e931fefe813296beea823c228af9e0d97e65742d380a0bbd6f370a89b23"
|
sig: ValidatorSig.fromHex"a07adeeb639a974fe3ae78a0a28785b195bffeaa2ec558c6baa63458daaf5b7a245940a2d9b91a993515295075eba4e115c6777eda1e7933cb53f64ab36619e49faadf289a8cc1521ca3ae5f9a3f2b88e355ef0b75dd8a9949c9d2a43c5589e0"
|
||||||
), DepositConfig(
|
), DepositConfig(
|
||||||
privkey: ValidatorPrivKey.init(hexToSeqByte"0x2042dc809c130e91906c9cb0be2fec0d6afaa8f22635efc7a3c2dbf833c1851a"),
|
privkey: ValidatorPrivKey.init("0x2042dc809c130e91906c9cb0be2fec0d6afaa8f22635efc7a3c2dbf833c1851a"),
|
||||||
signing_root: hexToByteArray[32]("e8a45fa71addd854d8d78e0b2cdc8f9100c8a5e03d894c1c382068e8aa4b71e2"),
|
signing_root: hexToByteArray[32]("e8a45fa71addd854d8d78e0b2cdc8f9100c8a5e03d894c1c382068e8aa4b71e2"),
|
||||||
domain: DOMAIN_DEPOSIT,
|
domain: DOMAIN_DEPOSIT,
|
||||||
sig: ValidatorSig.fromHex"89ac6297195e768b5e88cbbb047d8b81c77550c9462df5750f4b899fc0de985fa9e16fccc6c6bd71124eb7806064b7110d534fb8f6ccaf118074cd4f4fac8a22442e8facc2cd380ddc4ebf6b9c2f7e956f418279dc04a6737ede6d7763396ed9"
|
sig: ValidatorSig.fromHex"95719c0c4dae737aac602aeadf9faeb9ad3492450af249c43a1147a6e471ddb3f2b5979b6587e843d20c9caa8ecd83e8001b57a4f7c302927725966acc959eb6668357831b7a0692f2396a18939d9fa974e611beed4a7a59ffe892e77d2680bd"
|
||||||
), DepositConfig(
|
), DepositConfig(
|
||||||
privkey: ValidatorPrivKey.init(hexToSeqByte"0x15283c540041cd85c4533ee47517c8bb101c6207e9acbba2935287405a78502c"),
|
privkey: ValidatorPrivKey.init("0x15283c540041cd85c4533ee47517c8bb101c6207e9acbba2935287405a78502c"),
|
||||||
signing_root: hexToByteArray[32]("3dfab0daa3be9c72c5dd3b383e756d6048bb76cd3d09abb4dc991211ae8a547b"),
|
signing_root: hexToByteArray[32]("3dfab0daa3be9c72c5dd3b383e756d6048bb76cd3d09abb4dc991211ae8a547b"),
|
||||||
domain: DOMAIN_DEPOSIT,
|
domain: DOMAIN_DEPOSIT,
|
||||||
sig: ValidatorSig.fromHex"8adee09a19ca26d5753b9aa447b0af188a769f061d11bf40b32937ad3fa142ca9bc164323631a4bb78f0a5d4fd1262010134adc723ab377a2e6e362d3e2130a46b0a2088517aee519a424147f043cc5007a13f2d2d5311c18ee2f694ca3f19fc"
|
sig: ValidatorSig.fromHex"b8221ad674d7c23378b488555eb6e06ce56a342dad84ba6e3a57e108c1c426161b568a9366d82fd0059a23621922a1fc0e59d8eaa66dbb4611a173be167731367edf8daad3b07b64207faf3ea457a335228def3ca61571c4edc15dc392bf4e56"
|
||||||
), DepositConfig(
|
), DepositConfig(
|
||||||
privkey: ValidatorPrivKey.init(hexToSeqByte"0x03c85e538e1bb30235a87a3758c5571753ca1308b7dee321b74c19f78423999b"),
|
privkey: ValidatorPrivKey.init("0x03c85e538e1bb30235a87a3758c5571753ca1308b7dee321b74c19f78423999b"),
|
||||||
signing_root: hexToByteArray[32]("8905ae60c419e38f263eb818a5536e4144df3c0a800132e07594d457c62b5825"),
|
signing_root: hexToByteArray[32]("8905ae60c419e38f263eb818a5536e4144df3c0a800132e07594d457c62b5825"),
|
||||||
domain: DOMAIN_DEPOSIT,
|
domain: DOMAIN_DEPOSIT,
|
||||||
sig: ValidatorSig.fromHex"90dc90a295644da5c6d441cd0b33e34b8f1f77230755fd78b9ecbd86fd6e845e554c0579ab88c76ca14b56d9f0749f310cd884c193ec69623ccd724469268574c985ee614e80f00331c24f78a3638576d304c67c2aa6ce8949652257581c18a5"
|
sig: ValidatorSig.fromHex"a5e61349958745c80862af84e06924748832cae379b02a50909468fef9f07f21d35a98e1287b6219528a1ad566567d0619e049efa9fa6e81410bb3a247cf53b0f6787f747f8229fb9f851290b140f14f14a2adcb23b7cafaf90b301d14169324"
|
||||||
), DepositConfig(
|
), DepositConfig(
|
||||||
privkey: ValidatorPrivKey.init(hexToSeqByte"0x45a577d5cab31ac5cfff381500e09655f0799f29b130e6ad61c1eec4b15bf8dd"),
|
privkey: ValidatorPrivKey.init("0x45a577d5cab31ac5cfff381500e09655f0799f29b130e6ad61c1eec4b15bf8dd"),
|
||||||
signing_root: hexToByteArray[32]("702d1bd9c27c999923149f6c6578c835943b58b90845086bbf5be3b94aa4663d"),
|
signing_root: hexToByteArray[32]("702d1bd9c27c999923149f6c6578c835943b58b90845086bbf5be3b94aa4663d"),
|
||||||
domain: DOMAIN_DEPOSIT,
|
domain: DOMAIN_DEPOSIT,
|
||||||
sig: ValidatorSig.fromHex"9338c8b0050cdb464efae738d6d89ac48d5839ce750e3f1f20acd52a0b61e5c033fa186d3ed0ddf5856af6c4815971b00a68002b1eba45f5af27f91cad04831e32157fecf5fb091a8087829e2d3dd3438e0b86ff8d036be4a3876fa0dfa60e6c"
|
sig: ValidatorSig.fromHex"893d8e70f2cdb6f7acc3d9828e72d7b20e512956588d8c068b3ef4aa649db369cf962506b7c9107246246d9b20361cd80250109da513809415314af3ef1f220c171dbc2d9c2b62056739703ae4eb1be13fa289ea8472920b2393041f69198dc5"
|
||||||
), DepositConfig(
|
), DepositConfig(
|
||||||
privkey: ValidatorPrivKey.init(hexToSeqByte"0x03cffafa1cbaa7e585eaee07a9d35ae57f6dfe19a9ea53af9c37e9f3dfac617c"),
|
privkey: ValidatorPrivKey.init("0x03cffafa1cbaa7e585eaee07a9d35ae57f6dfe19a9ea53af9c37e9f3dfac617c"),
|
||||||
signing_root: hexToByteArray[32]("77f3da02c410e9ccba39d89983c52e6e77ca5dec3ae423311a578ee28b2ec0cd"),
|
signing_root: hexToByteArray[32]("77f3da02c410e9ccba39d89983c52e6e77ca5dec3ae423311a578ee28b2ec0cd"),
|
||||||
domain: DOMAIN_DEPOSIT,
|
domain: DOMAIN_DEPOSIT,
|
||||||
sig: ValidatorSig.fromHex"8819f719f7af378f27fe65c699b5206f1f7bbfd62200cab09e7ffe3d8fce0346eaa84b274d66d700cd1a0c0c7b46f62100afb2601270292ddf6a2bddff0248bb8ed6085d10c8c9e691a24b15d74bc7a9fcf931d953300d133f8c0e772704b9ba"
|
sig: ValidatorSig.fromHex"87ae1567999d3ceefce04c1a48aa189c3d368efbeda53c01962783941c03d3a26e08e5e9d287a927decf4e77755b97e80856e339c3af41dc5ffd373c6e4768de62718ce76cfd8c2062e7673c9eedd2fec235467967f932e59e0b3a32040c0038"
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -128,14 +126,13 @@ suiteReport "Interop":
|
||||||
|
|
||||||
check:
|
check:
|
||||||
# getBytes is bigendian and returns full 48 bytes of key..
|
# getBytes is bigendian and returns full 48 bytes of key..
|
||||||
Uint256.fromBytesBE(key.getBytes()[48-32..<48]) == v
|
Uint256.fromBytesBE(key.exportRaw()[48-32..<48]) == v
|
||||||
|
|
||||||
timedTest "Interop signatures":
|
timedTest "Interop signatures":
|
||||||
for dep in depositsConfig:
|
for dep in depositsConfig:
|
||||||
let computed_sig = bls_sign(
|
let computed_sig = bls_sign(
|
||||||
key = dep.privkey,
|
privkey = dep.privkey,
|
||||||
msg = dep.signing_root,
|
message = dep.signing_root
|
||||||
domain = compute_domain(dep.domain)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
|
@ -152,20 +149,21 @@ suiteReport "Interop":
|
||||||
privKey = makeInteropPrivKey(i)
|
privKey = makeInteropPrivKey(i)
|
||||||
deposits.add(makeDeposit(privKey.pubKey(), privKey))
|
deposits.add(makeDeposit(privKey.pubKey(), privKey))
|
||||||
|
|
||||||
|
const genesis_time = 1570500000
|
||||||
var
|
var
|
||||||
# TODO this currently requires skipMerkleValidation to pass the test
|
# TODO this currently requires skipMerkleValidation to pass the test
|
||||||
# makeDeposit doesn't appear to produce a proof?
|
# makeDeposit doesn't appear to produce a proof?
|
||||||
initialState = initialize_beacon_state_from_eth1(
|
initialState = initialize_beacon_state_from_eth1(
|
||||||
eth1BlockHash, 1570500000, deposits, {skipMerkleValidation})
|
eth1BlockHash, genesis_time, deposits, {skipMerkleValidation})
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-pm/tree/6e41fcf383ebeb5125938850d8e9b4e9888389b4/interop/mocked_start#create-genesis-state
|
# https://github.com/ethereum/eth2.0-pm/tree/6e41fcf383ebeb5125938850d8e9b4e9888389b4/interop/mocked_start#create-genesis-state
|
||||||
initialState.genesis_time = 1570500000
|
initialState.genesis_time = genesis_time
|
||||||
|
|
||||||
let expected =
|
let expected =
|
||||||
when const_preset == "minimal":
|
when const_preset == "minimal":
|
||||||
"5a3bbcae4ab2b4eafded947689fd7bd8214a616ffffd2521befdfe2a3b2f74c0"
|
"410c8758710155b49208d52c9e4bd2f11aa16a7c7521e560a2d05dcd69a023b3"
|
||||||
elif const_preset == "mainnet":
|
elif const_preset == "mainnet":
|
||||||
"db0a887acd5e201ac579d6cdc0c4932f2a0adf342d84dc5cd11ce959fbce3760"
|
"95a0b1e7b0b77d0cbe2bcd12c90469e68edb141424b1a6126f1d55498afe3ae6"
|
||||||
else:
|
else:
|
||||||
"unimplemented"
|
"unimplemented"
|
||||||
check:
|
check:
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
# beacon_chain
|
||||||
|
# Copyright (c) 2020 Status Research & Development GmbH
|
||||||
|
# Licensed and distributed under either of
|
||||||
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||||
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||||
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
|
{.used.}
|
||||||
|
|
||||||
|
import
|
||||||
|
unittest, ./testutil, ./mocking/merkle_minimal
|
||||||
|
|
||||||
|
suiteReport "Mocking utilities":
|
||||||
|
timedTest "merkle_minimal":
|
||||||
|
check:
|
||||||
|
testMerkleMinimal()
|
|
@ -35,5 +35,5 @@ asyncTest "connect two nodes":
|
||||||
c2.nat = "none"
|
c2.nat = "none"
|
||||||
var n2 = await createEth2Node(c2)
|
var n2 = await createEth2Node(c2)
|
||||||
|
|
||||||
await n2.connectToNetwork(bootstrapNodes = @[n1PersistentAddress])
|
await n2.connectToNetwork(@[n1PersistentAddress])
|
||||||
|
|
||||||
|
|
|
@ -193,14 +193,14 @@ suiteReport "PeerPool testing suite":
|
||||||
itemFut23.finished == false
|
itemFut23.finished == false
|
||||||
itemFut24.finished == false
|
itemFut24.finished == false
|
||||||
|
|
||||||
timedTest "Acquire/Sorting and consistency test":
|
timedTest "Acquire/Sorting and consistency test": closureScope:
|
||||||
const
|
const
|
||||||
TestsCount = 1000
|
TestsCount = 1000
|
||||||
MaxNumber = 1_000_000
|
MaxNumber = 1_000_000
|
||||||
|
|
||||||
var pool = newPeerPool[PeerTest, PeerTestID]()
|
var pool = newPeerPool[PeerTest, PeerTestID]()
|
||||||
|
|
||||||
proc testAcquireRelease(): Future[int] {.async.} =
|
proc testAcquireRelease(): Future[int] {.async, gcsafe.} =
|
||||||
var weight: int
|
var weight: int
|
||||||
var incoming, outgoing, total: seq[PeerTest]
|
var incoming, outgoing, total: seq[PeerTest]
|
||||||
var incWeight1, outWeight1, totWeight1: int
|
var incWeight1, outWeight1, totWeight1: int
|
||||||
|
@ -362,7 +362,7 @@ suiteReport "PeerPool testing suite":
|
||||||
|
|
||||||
check waitFor(testPeerLifetime()) == true
|
check waitFor(testPeerLifetime()) == true
|
||||||
|
|
||||||
timedTest "Safe/Clear test":
|
timedTest "Safe/Clear test": closureScope:
|
||||||
var pool = newPeerPool[PeerTest, PeerTestID]()
|
var pool = newPeerPool[PeerTest, PeerTestID]()
|
||||||
var peer1 = PeerTest.init("peer1", 10)
|
var peer1 = PeerTest.init("peer1", 10)
|
||||||
var peer2 = PeerTest.init("peer2", 9)
|
var peer2 = PeerTest.init("peer2", 9)
|
||||||
|
@ -409,7 +409,7 @@ suiteReport "PeerPool testing suite":
|
||||||
asyncCheck testConsumer()
|
asyncCheck testConsumer()
|
||||||
check waitFor(testClose()) == true
|
check waitFor(testClose()) == true
|
||||||
|
|
||||||
timedTest "Access peers by key test":
|
timedTest "Access peers by key test": closureScope:
|
||||||
var pool = newPeerPool[PeerTest, PeerTestID]()
|
var pool = newPeerPool[PeerTest, PeerTestID]()
|
||||||
var peer1 = PeerTest.init("peer1", 10)
|
var peer1 = PeerTest.init("peer1", 10)
|
||||||
var peer2 = PeerTest.init("peer2", 9)
|
var peer2 = PeerTest.init("peer2", 9)
|
||||||
|
|
|
@ -603,7 +603,7 @@ proc checkRequest(peer: SimplePeer, index: int, slot, count, step: int,
|
||||||
data: varargs[int]): bool {.inline.} =
|
data: varargs[int]): bool {.inline.} =
|
||||||
result = checkRequest(peer.requests[index], slot, count, step, data)
|
result = checkRequest(peer.requests[index], slot, count, step, data)
|
||||||
|
|
||||||
proc syncManagerOnePeerTest(): Future[bool] {.async.} =
|
proc syncManagerOnePeerTest(): Future[bool] {.async, gcsafe.} =
|
||||||
# Syncing with one peer only.
|
# Syncing with one peer only.
|
||||||
var pool = newPeerPool[SimplePeer, SimplePeerKey]()
|
var pool = newPeerPool[SimplePeer, SimplePeerKey]()
|
||||||
var peer = SimplePeer.init("id1")
|
var peer = SimplePeer.init("id1")
|
||||||
|
|
|
@ -43,7 +43,7 @@ func makeDeposit(i: int, flags: UpdateFlags): Deposit =
|
||||||
privkey = makeFakeValidatorPrivKey(i)
|
privkey = makeFakeValidatorPrivKey(i)
|
||||||
pubkey = privkey.pubKey()
|
pubkey = privkey.pubKey()
|
||||||
withdrawal_credentials = makeFakeHash(i)
|
withdrawal_credentials = makeFakeHash(i)
|
||||||
domain = compute_domain(DOMAIN_DEPOSIT)
|
domain = compute_domain(DOMAIN_DEPOSIT, GENESIS_FORK_VERSION)
|
||||||
|
|
||||||
result = Deposit(
|
result = Deposit(
|
||||||
data: DepositData(
|
data: DepositData(
|
||||||
|
@ -84,7 +84,8 @@ proc addTestBlock*(
|
||||||
privKey = hackPrivKey(proposer)
|
privKey = hackPrivKey(proposer)
|
||||||
randao_reveal =
|
randao_reveal =
|
||||||
if skipBlsValidation notin flags:
|
if skipBlsValidation notin flags:
|
||||||
privKey.genRandaoReveal(state.fork, state.slot)
|
privKey.genRandaoReveal(
|
||||||
|
state.fork, state.genesis_validators_root, state.slot)
|
||||||
else:
|
else:
|
||||||
ValidatorSig()
|
ValidatorSig()
|
||||||
|
|
||||||
|
@ -149,7 +150,8 @@ proc makeAttestation*(
|
||||||
let
|
let
|
||||||
sig =
|
sig =
|
||||||
if skipBLSValidation notin flags:
|
if skipBLSValidation notin flags:
|
||||||
get_attestation_signature(state.fork, data, hackPrivKey(validator))
|
get_attestation_signature(state.fork, state.genesis_validators_root,
|
||||||
|
data, hackPrivKey(validator))
|
||||||
else:
|
else:
|
||||||
ValidatorSig()
|
ValidatorSig()
|
||||||
|
|
||||||
|
@ -203,7 +205,7 @@ proc makeFullAttestations*(
|
||||||
aggregation_bits: CommitteeValidatorsBits.init(committee.len),
|
aggregation_bits: CommitteeValidatorsBits.init(committee.len),
|
||||||
data: data,
|
data: data,
|
||||||
signature: get_attestation_signature(
|
signature: get_attestation_signature(
|
||||||
state.fork, data,
|
state.fork, state.genesis_validators_root, data,
|
||||||
hackPrivKey(state.validators[committee[0]]))
|
hackPrivKey(state.validators[committee[0]]))
|
||||||
)
|
)
|
||||||
# Aggregate the remainder
|
# Aggregate the remainder
|
||||||
|
@ -212,7 +214,7 @@ proc makeFullAttestations*(
|
||||||
attestation.aggregation_bits.setBit j
|
attestation.aggregation_bits.setBit j
|
||||||
if skipBLSValidation notin flags:
|
if skipBLSValidation notin flags:
|
||||||
attestation.signature.aggregate(get_attestation_signature(
|
attestation.signature.aggregate(get_attestation_signature(
|
||||||
state.fork, data,
|
state.fork, state.genesis_validators_root, data,
|
||||||
hackPrivKey(state.validators[committee[j]])
|
hackPrivKey(state.validators[committee[j]])
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 924fb6cad1c849eec29d2c96c9803e4f43d6a8f0
|
Subproject commit 68c6d27304245c948526487b37e10951acf7dbc8
|
|
@ -1 +1 @@
|
||||||
Subproject commit 8da0e30c526ab1c6c825e16546fc5db972c5408d
|
Subproject commit fb8af46311965fd076412e6e071dda571d282024
|
|
@ -1 +1 @@
|
||||||
Subproject commit 7ed9f1431a0a8262f988553e9927676cad54e470
|
Subproject commit cbd8e03823c00dd230e48a4613c0f594b77616eb
|
|
@ -1 +1 @@
|
||||||
Subproject commit 24c73359b06340fb3b767c420e0e33e66bd05b86
|
Subproject commit 6e5d570490989c753d4645ba9173ef9358d302bb
|
|
@ -1 +1 @@
|
||||||
Subproject commit 9c442bf65b52a4c857cc6e51efe901352e8b6ebf
|
Subproject commit ec1492a65a1d82e83181c7d216dd97fb3268d5f0
|
|
@ -1 +1 @@
|
||||||
Subproject commit b06d78d1d8c306a3cce80c391856f44bf7db6119
|
Subproject commit 5326d824b1d91ea273095172512eb309f32e0c82
|
|
@ -1 +1 @@
|
||||||
Subproject commit 5fb40e4ffd2c5a7eca88203d99150e2d99732e41
|
Subproject commit 8a3cf6778d483a9d701534dfc2f14f3a4dfc4ab8
|
|
@ -1 +1 @@
|
||||||
Subproject commit 6fbaeb61cab889f74870372bc00cc99371e16794
|
Subproject commit 5c0d0961114bcaaf3da52d5918bf0b85ef0e4ce9
|
|
@ -1 +1 @@
|
||||||
Subproject commit 6350b72b5eda69f7ccfa57a94fd420509dbf6f49
|
Subproject commit c108ba90e6b304515f08fdcff62f428f3f8fbe53
|
|
@ -1 +1 @@
|
||||||
Subproject commit 3b29eed05a204e4efe5b54a50dc4cbe2dfd38c1b
|
Subproject commit da216986c635599dccffa2e71eabad03653e5aef
|
|
@ -1 +1 @@
|
||||||
Subproject commit 0a3e4a764b718d13fc330f228fce60ed265cfde2
|
Subproject commit f4740c8b8e1d55b45ed578cb42e1cd084f9b9644
|
|
@ -1 +1 @@
|
||||||
Subproject commit ab1d8891ef11e4c310c071213bd67aa5ac4b421d
|
Subproject commit 5db86514a1185620a003d1e5ea1da4c0373c3b6e
|
|
@ -1 +1 @@
|
||||||
Subproject commit ae60eef4e8413e49fb0dbcae9a343fb479509fa0
|
Subproject commit 0eab8cfeee55cfa3bb893ec31137d3c25b83a1ae
|
|
@ -1 +1 @@
|
||||||
Subproject commit 76beeb769e30adc912d648c014fd95bf748fef24
|
Subproject commit b06a5b6e32aa4d5abf9c1019ab6728fa8f360cc5
|
|
@ -1 +1 @@
|
||||||
Subproject commit 89d7a0c8fd1eb0f749432bd7136d8f385351c48e
|
Subproject commit 969adf2f1ef42753ba26d5ab7eca01617c846792
|
|
@ -1 +1 @@
|
||||||
Subproject commit 088d3b7f6843fd61c829a5a0c0c29912945963ae
|
Subproject commit 0cce46e1260b053349d0d6f337f5d67a7bc14462
|
|
@ -1 +1 @@
|
||||||
Subproject commit 04f933314ca1d7d79fc6e4f19a0bd7566afbf462
|
Subproject commit cd58cf69a0b883a4672cd3f79ee38ec0cf2c8c56
|
|
@ -19,7 +19,7 @@ rm -rf ncli/nimcache
|
||||||
../env.sh nim c \
|
../env.sh nim c \
|
||||||
--cpu:i386 --os:linux --gc:none --threads:off \
|
--cpu:i386 --os:linux --gc:none --threads:off \
|
||||||
-d:release -d:clang -d:emscripten -d:noSignalHandler -d:usemalloc \
|
-d:release -d:clang -d:emscripten -d:noSignalHandler -d:usemalloc \
|
||||||
--nimcache:ncli/nimcache -d:"network_type=none" \
|
--nimcache:ncli/nimcache \
|
||||||
-u:metrics \
|
-u:metrics \
|
||||||
-c ncli
|
-c ncli
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
-d:"network_type=none"
|
|
||||||
-u:metrics
|
-u:metrics
|
||||||
|
|
Loading…
Reference in New Issue