Merge branch 'dev' into testgenphase1
This commit is contained in:
commit
28137a6176
|
@ -44,32 +44,19 @@ commands:
|
|||
venv_name: v22-pyspec
|
||||
reqs_checksum: cache-{{ checksum "setup.py" }}
|
||||
venv_path: ./venv
|
||||
restore_deposit_contract_compiler_cached_venv:
|
||||
description: "Restore the venv from cache for the deposit contract compiler"
|
||||
steps:
|
||||
- restore_cached_venv:
|
||||
venv_name: v23-deposit-contract-compiler
|
||||
reqs_checksum: cache-{{ checksum "deposit_contract/compiler/requirements.txt" }}
|
||||
save_deposit_contract_compiler_cached_venv:
|
||||
description: "Save the venv to cache for later use of the deposit contract compiler"
|
||||
steps:
|
||||
- save_cached_venv:
|
||||
venv_name: v23-deposit-contract-compiler
|
||||
reqs_checksum: cache-{{ checksum "deposit_contract/compiler/requirements.txt" }}
|
||||
venv_path: ./deposit_contract/compiler/venv
|
||||
restore_deposit_contract_tester_cached_venv:
|
||||
description: "Restore the venv from cache for the deposit contract tester"
|
||||
steps:
|
||||
- restore_cached_venv:
|
||||
venv_name: v22-deposit-contract-tester
|
||||
reqs_checksum: cache-{{ checksum "setup.py" }}-{{ checksum "deposit_contract/tester/requirements.txt" }}
|
||||
venv_name: v23-deposit-contract-tester
|
||||
reqs_checksum: cache-{{ checksum "setup.py" }}-{{ checksum "solidity_deposit_contract/web3_tester/requirements.txt" }}
|
||||
save_deposit_contract_tester_cached_venv:
|
||||
description: "Save the venv to cache for later use of the deposit contract tester"
|
||||
steps:
|
||||
- save_cached_venv:
|
||||
venv_name: v22-deposit-contract-tester
|
||||
reqs_checksum: cache-{{ checksum "setup.py" }}-{{ checksum "deposit_contract/tester/requirements.txt" }}
|
||||
venv_path: ./deposit_contract/tester/venv
|
||||
venv_name: v23-deposit-contract-tester
|
||||
reqs_checksum: cache-{{ checksum "setup.py" }}-{{ checksum "solidity_deposit_contract/web3_tester/requirements.txt" }}
|
||||
venv_path: ./solidity_deposit_contract/web3_tester/venv
|
||||
jobs:
|
||||
checkout_specs:
|
||||
docker:
|
||||
|
@ -145,20 +132,49 @@ jobs:
|
|||
- run:
|
||||
name: Run linter
|
||||
command: make lint
|
||||
install_deposit_contract_compiler:
|
||||
build_deposit_contract:
|
||||
docker:
|
||||
# The deposit contract compiler is pinned to python 3.7 because of the vyper version pin.
|
||||
- image: circleci/python:3.7
|
||||
working_directory: ~/specs-repo
|
||||
- image: ethereum/solc:0.6.11-alpine
|
||||
steps:
|
||||
- restore_cache:
|
||||
key: v3-specs-repo-{{ .Branch }}-{{ .Revision }}
|
||||
- restore_deposit_contract_compiler_cached_venv
|
||||
- checkout
|
||||
- run:
|
||||
name: Install deposit contract compiler requirements
|
||||
command: make install_deposit_contract_compiler
|
||||
- save_deposit_contract_compiler_cached_venv
|
||||
install_deposit_contract_tester:
|
||||
name: Install build essentials
|
||||
command: |
|
||||
apk update
|
||||
apk add git make
|
||||
- run:
|
||||
name: Compile the contract
|
||||
command: |
|
||||
make compile_deposit_contract
|
||||
git diff --color --exit-code
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- ./solidity_deposit_contract/deposit_contract.json
|
||||
- ./build/combined.json
|
||||
- ./solidity_deposit_contract/lib
|
||||
test_deposit_contract:
|
||||
docker:
|
||||
- image: nixorg/nix:circleci
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: nix-store-test-v2
|
||||
- attach_workspace:
|
||||
at: /tmp/
|
||||
- run:
|
||||
name: Test the contract
|
||||
command: |
|
||||
mkdir build
|
||||
cp -r /tmp/build/* build
|
||||
cp -r /tmp/solidity_deposit_contract/lib/* ./solidity_deposit_contract/lib
|
||||
cp -r /tmp/solidity_deposit_contract/deposit_contract.json ./solidity_deposit_contract/deposit_contract.json
|
||||
nix-shell --command 'make test_deposit_contract' ./solidity_deposit_contract/shell.nix
|
||||
- save_cache:
|
||||
key: nix-store-test-v2
|
||||
paths:
|
||||
- /nix
|
||||
install_deposit_contract_web3_tester:
|
||||
docker:
|
||||
- image: circleci/python:3.8
|
||||
working_directory: ~/specs-repo
|
||||
|
@ -168,20 +184,9 @@ jobs:
|
|||
- restore_deposit_contract_tester_cached_venv
|
||||
- run:
|
||||
name: Install deposit contract tester requirements
|
||||
command: make install_deposit_contract_tester
|
||||
command: make install_deposit_contract_web3_tester
|
||||
- save_deposit_contract_tester_cached_venv
|
||||
test_compile_deposit_contract:
|
||||
docker:
|
||||
- image: circleci/python:3.7
|
||||
working_directory: ~/specs-repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
key: v3-specs-repo-{{ .Branch }}-{{ .Revision }}
|
||||
- restore_deposit_contract_compiler_cached_venv
|
||||
- run:
|
||||
name: Run deposit contract compile test
|
||||
command: make test_compile_deposit_contract
|
||||
test_deposit_contract:
|
||||
test_deposit_contract_web3_tests:
|
||||
docker:
|
||||
- image: circleci/python:3.8
|
||||
working_directory: ~/specs-repo
|
||||
|
@ -190,9 +195,8 @@ jobs:
|
|||
key: v3-specs-repo-{{ .Branch }}-{{ .Revision }}
|
||||
- restore_deposit_contract_tester_cached_venv
|
||||
- run:
|
||||
name: Run deposit contract test
|
||||
command: make test_deposit_contract
|
||||
|
||||
name: Run deposit contract test with web3.py
|
||||
command: make test_deposit_contract_web3_tests
|
||||
workflows:
|
||||
version: 2.1
|
||||
test_spec:
|
||||
|
@ -209,15 +213,15 @@ workflows:
|
|||
- lint:
|
||||
requires:
|
||||
- test
|
||||
- install_deposit_contract_compiler:
|
||||
- install_deposit_contract_web3_tester:
|
||||
requires:
|
||||
- checkout_specs
|
||||
- test_compile_deposit_contract:
|
||||
- test_deposit_contract_web3_tests:
|
||||
requires:
|
||||
- install_deposit_contract_compiler
|
||||
- install_deposit_contract_tester:
|
||||
requires:
|
||||
- checkout_specs
|
||||
- install_deposit_contract_web3_tester
|
||||
build_and_test_deposit_contract:
|
||||
jobs:
|
||||
- build_deposit_contract
|
||||
- test_deposit_contract:
|
||||
requires:
|
||||
- install_deposit_contract_tester
|
||||
- build_deposit_contract
|
||||
|
|
|
@ -1 +1 @@
|
|||
*.vy linguist-language=Python
|
||||
*.sol linguist-language=Solidity
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "lib/ds-test"]
|
||||
path = solidity_deposit_contract/lib/ds-test
|
||||
url = https://github.com/dapphub/ds-test
|
42
Makefile
42
Makefile
|
@ -4,8 +4,10 @@ TEST_LIBS_DIR = ./tests/core
|
|||
PY_SPEC_DIR = $(TEST_LIBS_DIR)/pyspec
|
||||
TEST_VECTOR_DIR = ../eth2.0-spec-tests/tests
|
||||
GENERATOR_DIR = ./tests/generators
|
||||
DEPOSIT_CONTRACT_COMPILER_DIR = ./deposit_contract/compiler
|
||||
DEPOSIT_CONTRACT_TESTER_DIR = ./deposit_contract/tester
|
||||
SOLIDITY_DEPOSIT_CONTRACT_DIR = ./solidity_deposit_contract
|
||||
SOLIDITY_DEPOSIT_CONTRACT_SOURCE = ${SOLIDITY_DEPOSIT_CONTRACT_DIR}/deposit_contract.sol
|
||||
SOLIDITY_FILE_NAME = deposit_contract.json
|
||||
DEPOSIT_CONTRACT_TESTER_DIR = ${SOLIDITY_DEPOSIT_CONTRACT_DIR}/web3_tester
|
||||
CONFIGS_DIR = ./configs
|
||||
|
||||
# Collect a list of generator names
|
||||
|
@ -25,6 +27,11 @@ COV_INDEX_FILE=$(PY_SPEC_DIR)/$(COV_HTML_OUT)/index.html
|
|||
CURRENT_DIR = ${CURDIR}
|
||||
LINTER_CONFIG_FILE = $(CURRENT_DIR)/linter.ini
|
||||
|
||||
export DAPP_SKIP_BUILD:=1
|
||||
export DAPP_SRC:=$(SOLIDITY_DEPOSIT_CONTRACT_DIR)
|
||||
export DAPP_LIB:=$(SOLIDITY_DEPOSIT_CONTRACT_DIR)/lib
|
||||
export DAPP_JSON:=build/combined.json
|
||||
|
||||
.PHONY: clean partial_clean all test citest lint generate_tests pyspec install_test open_cov \
|
||||
install_deposit_contract_tester test_deposit_contract install_deposit_contract_compiler \
|
||||
compile_deposit_contract test_compile_deposit_contract check_toc
|
||||
|
@ -38,7 +45,6 @@ partial_clean:
|
|||
rm -rf .pytest_cache
|
||||
rm -f .coverage
|
||||
rm -rf $(PY_SPEC_DIR)/.pytest_cache
|
||||
rm -rf $(DEPOSIT_CONTRACT_COMPILER_DIR)/.pytest_cache
|
||||
rm -rf $(DEPOSIT_CONTRACT_TESTER_DIR)/.pytest_cache
|
||||
rm -rf $(PY_SPEC_DIR)/phase0
|
||||
rm -rf $(PY_SPEC_DIR)/phase1
|
||||
|
@ -46,7 +52,7 @@ partial_clean:
|
|||
rm -rf $(PY_SPEC_DIR)/.coverage
|
||||
rm -rf $(PY_SPEC_DIR)/test-reports
|
||||
rm -rf eth2spec.egg-info dist build
|
||||
|
||||
rm -rf build
|
||||
|
||||
clean: partial_clean
|
||||
rm -rf venv
|
||||
|
@ -107,24 +113,26 @@ lint: pyspec
|
|||
flake8 --config $(LINTER_CONFIG_FILE) ./eth2spec \
|
||||
&& mypy --config-file $(LINTER_CONFIG_FILE) -p eth2spec.phase0 -p eth2spec.phase1
|
||||
|
||||
install_deposit_contract_tester:
|
||||
cd $(DEPOSIT_CONTRACT_TESTER_DIR); python3 -m venv venv; . venv/bin/activate; pip3 install -r requirements.txt
|
||||
compile_deposit_contract:
|
||||
@cd $(SOLIDITY_DEPOSIT_CONTRACT_DIR)
|
||||
@git submodule update --recursive --init
|
||||
@solc --metadata-literal --optimize --optimize-runs 5000000 --bin --abi --combined-json=abi,bin,bin-runtime,srcmap,srcmap-runtime,ast,metadata,storage-layout --overwrite -o build $(SOLIDITY_DEPOSIT_CONTRACT_SOURCE) $(SOLIDITY_DEPOSIT_CONTRACT_DIR)/tests/deposit_contract.t.sol
|
||||
@/bin/echo -n '{"abi": ' > $(SOLIDITY_FILE_NAME)
|
||||
@cat build/DepositContract.abi >> $(SOLIDITY_FILE_NAME)
|
||||
@/bin/echo -n ', "bytecode": "0x' >> $(SOLIDITY_FILE_NAME)
|
||||
@cat build/DepositContract.bin >> $(SOLIDITY_FILE_NAME)
|
||||
@/bin/echo -n '"}' >> $(SOLIDITY_FILE_NAME)
|
||||
|
||||
test_deposit_contract:
|
||||
dapp test -v --fuzz-runs 5
|
||||
|
||||
install_deposit_contract_web3_tester:
|
||||
cd $(DEPOSIT_CONTRACT_TESTER_DIR); python3 -m venv venv; . venv/bin/activate; pip3 install -r requirements.txt
|
||||
|
||||
test_deposit_contract_web3_tests:
|
||||
cd $(DEPOSIT_CONTRACT_TESTER_DIR); . venv/bin/activate; \
|
||||
python -m pytest .
|
||||
|
||||
install_deposit_contract_compiler:
|
||||
cd $(DEPOSIT_CONTRACT_COMPILER_DIR); python3.7 -m venv venv; . venv/bin/activate; pip3.7 install -r requirements.txt
|
||||
|
||||
compile_deposit_contract:
|
||||
cd $(DEPOSIT_CONTRACT_COMPILER_DIR); . venv/bin/activate; \
|
||||
python3.7 deposit_contract/compile.py ../contracts/validator_registration.vy
|
||||
|
||||
test_compile_deposit_contract:
|
||||
cd $(DEPOSIT_CONTRACT_COMPILER_DIR); . venv/bin/activate; \
|
||||
python3.7 -m pytest .
|
||||
|
||||
# Runs a generator, identified by param 1
|
||||
define run_generator
|
||||
# Started!
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Ethereum 2.0 Specifications
|
||||
|
||||
[![Join the chat at https://discord.gg/hpFs23p](https://img.shields.io/badge/chat-on%20discord-blue.svg)](https://discord.gg/hpFs23p) [![Join the chat at https://gitter.im/ethereum/sharding](https://badges.gitter.im/ethereum/sharding.svg)](https://gitter.im/ethereum/sharding?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[![Join the chat at https://discord.gg/qGpsxSA](https://img.shields.io/badge/chat-on%20discord-blue.svg)](https://discord.gg/hpFs23p) [![Join the chat at https://gitter.im/ethereum/sharding](https://badges.gitter.im/ethereum/sharding.svg)](https://gitter.im/ethereum/sharding?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
To learn more about sharding and Ethereum 2.0 (Serenity), see the [sharding FAQ](https://eth.wiki/sharding/Sharding-FAQs) and the [research compendium](https://notes.ethereum.org/s/H1PGqDhpm).
|
||||
|
||||
|
|
|
@ -28,6 +28,8 @@ HYSTERESIS_QUOTIENT: 4
|
|||
HYSTERESIS_DOWNWARD_MULTIPLIER: 1
|
||||
# 5 (plus 1.25)
|
||||
HYSTERESIS_UPWARD_MULTIPLIER: 5
|
||||
# 3
|
||||
PROPORTIONAL_SLASHING_MULTIPLIER: 3
|
||||
|
||||
|
||||
# Fork Choice
|
||||
|
|
|
@ -27,7 +27,8 @@ HYSTERESIS_QUOTIENT: 4
|
|||
HYSTERESIS_DOWNWARD_MULTIPLIER: 1
|
||||
# 5 (plus 1.25)
|
||||
HYSTERESIS_UPWARD_MULTIPLIER: 5
|
||||
|
||||
# 3
|
||||
PROPORTIONAL_SLASHING_MULTIPLIER: 3
|
||||
|
||||
|
||||
# Fork Choice
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
# Deposit contract
|
||||
|
||||
## How to set up the testing environment?
|
||||
|
||||
Under the `eth2.0-specs` directory, execute:
|
||||
|
||||
```sh
|
||||
make install_deposit_contract_tester
|
||||
```
|
||||
|
||||
## How to compile the contract?
|
||||
|
||||
```sh
|
||||
make compile_deposit_contract
|
||||
```
|
||||
|
||||
The compiler dependencies can be installed with:
|
||||
|
||||
```sh
|
||||
make install_deposit_contract_compiler
|
||||
```
|
||||
|
||||
Note that this requires python 3.7 to be installed. The pinned vyper version will not work on 3.8.
|
||||
|
||||
The ABI and bytecode will be updated at [`contracts/validator_registration.json`](./contracts/validator_registration.json).
|
||||
|
||||
|
||||
## How to run tests?
|
||||
|
||||
For running the contract tests:
|
||||
```sh
|
||||
make test_deposit_contract
|
||||
```
|
||||
|
||||
For testing the compiler output against the expected formally-verified bytecode:
|
||||
```sh
|
||||
make test_compile_deposit_contract
|
||||
```
|
|
@ -1,31 +0,0 @@
|
|||
import argparse
|
||||
import json
|
||||
import os
|
||||
|
||||
from vyper import compiler
|
||||
|
||||
DIR = os.path.dirname(__file__)
|
||||
|
||||
|
||||
def generate_compiled_json(file_path: str):
|
||||
deposit_contract_code = open(file_path).read()
|
||||
abi = compiler.mk_full_signature(deposit_contract_code)
|
||||
bytecode = compiler.compile_code(deposit_contract_code)['bytecode']
|
||||
contract_json = {
|
||||
'abi': abi,
|
||||
'bytecode': bytecode,
|
||||
}
|
||||
# write json
|
||||
basename = os.path.basename(file_path)
|
||||
dirname = os.path.dirname(file_path)
|
||||
contract_name = basename.split('.')[0]
|
||||
with open(dirname + "/{}.json".format(contract_name), 'w') as f_write:
|
||||
json.dump(contract_json, f_write)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("path", type=str, help="the path of the contract")
|
||||
args = parser.parse_args()
|
||||
path = args.path
|
||||
generate_compiled_json(path)
|
|
@ -1,29 +0,0 @@
|
|||
from vyper import compiler
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
DIR = os.path.dirname(__file__)
|
||||
|
||||
|
||||
def get_deposit_contract_code():
|
||||
file_path = os.path.join(DIR, '../../contracts/validator_registration.vy')
|
||||
deposit_contract_code = open(file_path).read()
|
||||
return deposit_contract_code
|
||||
|
||||
|
||||
def get_deposit_contract_json():
|
||||
file_path = os.path.join(DIR, '../../contracts/validator_registration.json')
|
||||
deposit_contract_json = open(file_path).read()
|
||||
return json.loads(deposit_contract_json)
|
||||
|
||||
|
||||
def test_compile_deposit_contract():
|
||||
compiled_deposit_contract_json = get_deposit_contract_json()
|
||||
|
||||
deposit_contract_code = get_deposit_contract_code()
|
||||
abi = compiler.mk_full_signature(deposit_contract_code)
|
||||
bytecode = compiler.compile_code(deposit_contract_code)['bytecode']
|
||||
|
||||
assert abi == compiled_deposit_contract_json["abi"]
|
||||
assert bytecode == compiled_deposit_contract_json["bytecode"]
|
|
@ -1,7 +0,0 @@
|
|||
# Vyper beta version used to generate the bytecode that was then formally verified.
|
||||
# On top of this beta version, a later change was backported, and included in the formal verification:
|
||||
# https://github.com/vyperlang/vyper/issues/1761
|
||||
# The resulting vyper version is pinned and maintained as protected branch.
|
||||
git+https://github.com/vyperlang/vyper@1761-HOTFIX-v0.1.0-beta.13
|
||||
|
||||
pytest==3.6.1
|
|
@ -1,10 +0,0 @@
|
|||
from distutils.core import setup
|
||||
|
||||
setup(
|
||||
name='deposit_contract_compiler',
|
||||
packages=['deposit_contract'],
|
||||
package_dir={"": "."},
|
||||
python_requires="3.7", # pinned vyper compiler stops working after 3.7. See vyper issue 1835.
|
||||
tests_requires=["pytest==3.6.1"],
|
||||
install_requires=[], # see requirements.txt file
|
||||
)
|
File diff suppressed because one or more lines are too long
|
@ -1,110 +0,0 @@
|
|||
# Vyper target 0.1.0b13.hotfix1761
|
||||
MIN_DEPOSIT_AMOUNT: constant(uint256) = 1000000000 # Gwei
|
||||
DEPOSIT_CONTRACT_TREE_DEPTH: constant(uint256) = 32
|
||||
MAX_DEPOSIT_COUNT: constant(uint256) = 4294967295 # 2**DEPOSIT_CONTRACT_TREE_DEPTH - 1
|
||||
PUBKEY_LENGTH: constant(uint256) = 48 # bytes
|
||||
WITHDRAWAL_CREDENTIALS_LENGTH: constant(uint256) = 32 # bytes
|
||||
SIGNATURE_LENGTH: constant(uint256) = 96 # bytes
|
||||
AMOUNT_LENGTH: constant(uint256) = 8 # bytes
|
||||
|
||||
DepositEvent: event({
|
||||
pubkey: bytes[48],
|
||||
withdrawal_credentials: bytes[32],
|
||||
amount: bytes[8],
|
||||
signature: bytes[96],
|
||||
index: bytes[8],
|
||||
})
|
||||
|
||||
branch: bytes32[DEPOSIT_CONTRACT_TREE_DEPTH]
|
||||
deposit_count: uint256
|
||||
|
||||
# Compute hashes in empty sparse Merkle tree
|
||||
zero_hashes: bytes32[DEPOSIT_CONTRACT_TREE_DEPTH]
|
||||
@public
|
||||
def __init__():
|
||||
for i in range(DEPOSIT_CONTRACT_TREE_DEPTH - 1):
|
||||
self.zero_hashes[i + 1] = sha256(concat(self.zero_hashes[i], self.zero_hashes[i]))
|
||||
|
||||
|
||||
@private
|
||||
@constant
|
||||
def to_little_endian_64(value: uint256) -> bytes[8]:
|
||||
# Reversing bytes using bitwise uint256 manipulations
|
||||
# Note: array accesses of bytes[] are not currently supported in Vyper
|
||||
# Note: this function is only called when `value < 2**64`
|
||||
y: uint256 = 0
|
||||
x: uint256 = value
|
||||
for _ in range(8):
|
||||
y = shift(y, 8)
|
||||
y = y + bitwise_and(x, 255)
|
||||
x = shift(x, -8)
|
||||
return slice(convert(y, bytes32), start=24, len=8)
|
||||
|
||||
|
||||
@public
|
||||
@constant
|
||||
def get_deposit_root() -> bytes32:
|
||||
zero_bytes32: bytes32 = 0x0000000000000000000000000000000000000000000000000000000000000000
|
||||
node: bytes32 = zero_bytes32
|
||||
size: uint256 = self.deposit_count
|
||||
for height in range(DEPOSIT_CONTRACT_TREE_DEPTH):
|
||||
if bitwise_and(size, 1) == 1: # More gas efficient than `size % 2 == 1`
|
||||
node = sha256(concat(self.branch[height], node))
|
||||
else:
|
||||
node = sha256(concat(node, self.zero_hashes[height]))
|
||||
size /= 2
|
||||
return sha256(concat(node, self.to_little_endian_64(self.deposit_count), slice(zero_bytes32, start=0, len=24)))
|
||||
|
||||
|
||||
@public
|
||||
@constant
|
||||
def get_deposit_count() -> bytes[8]:
|
||||
return self.to_little_endian_64(self.deposit_count)
|
||||
|
||||
|
||||
@payable
|
||||
@public
|
||||
def deposit(pubkey: bytes[PUBKEY_LENGTH],
|
||||
withdrawal_credentials: bytes[WITHDRAWAL_CREDENTIALS_LENGTH],
|
||||
signature: bytes[SIGNATURE_LENGTH],
|
||||
deposit_data_root: bytes32):
|
||||
# Avoid overflowing the Merkle tree (and prevent edge case in computing `self.branch`)
|
||||
assert self.deposit_count < MAX_DEPOSIT_COUNT
|
||||
|
||||
# Check deposit amount
|
||||
deposit_amount: uint256 = msg.value / as_wei_value(1, "gwei")
|
||||
assert deposit_amount >= MIN_DEPOSIT_AMOUNT
|
||||
|
||||
# Length checks for safety
|
||||
assert len(pubkey) == PUBKEY_LENGTH
|
||||
assert len(withdrawal_credentials) == WITHDRAWAL_CREDENTIALS_LENGTH
|
||||
assert len(signature) == SIGNATURE_LENGTH
|
||||
|
||||
# Emit `DepositEvent` log
|
||||
amount: bytes[8] = self.to_little_endian_64(deposit_amount)
|
||||
log.DepositEvent(pubkey, withdrawal_credentials, amount, signature, self.to_little_endian_64(self.deposit_count))
|
||||
|
||||
# Compute deposit data root (`DepositData` hash tree root)
|
||||
zero_bytes32: bytes32 = 0x0000000000000000000000000000000000000000000000000000000000000000
|
||||
pubkey_root: bytes32 = sha256(concat(pubkey, slice(zero_bytes32, start=0, len=64 - PUBKEY_LENGTH)))
|
||||
signature_root: bytes32 = sha256(concat(
|
||||
sha256(slice(signature, start=0, len=64)),
|
||||
sha256(concat(slice(signature, start=64, len=SIGNATURE_LENGTH - 64), zero_bytes32)),
|
||||
))
|
||||
node: bytes32 = sha256(concat(
|
||||
sha256(concat(pubkey_root, withdrawal_credentials)),
|
||||
sha256(concat(amount, slice(zero_bytes32, start=0, len=32 - AMOUNT_LENGTH), signature_root)),
|
||||
))
|
||||
# Verify computed and expected deposit data roots match
|
||||
assert node == deposit_data_root
|
||||
|
||||
# Add deposit data root to Merkle tree (update a single `branch` node)
|
||||
self.deposit_count += 1
|
||||
size: uint256 = self.deposit_count
|
||||
for height in range(DEPOSIT_CONTRACT_TREE_DEPTH):
|
||||
if bitwise_and(size, 1) == 1: # More gas efficient than `size % 2 == 1`
|
||||
self.branch[height] = node
|
||||
break
|
||||
node = sha256(concat(self.branch[height], node))
|
||||
size /= 2
|
||||
|
6
setup.py
6
setup.py
|
@ -156,8 +156,10 @@ SSZObject = TypeVar('SSZObject', bound=View)
|
|||
CONFIG_NAME = 'mainnet'
|
||||
'''
|
||||
SUNDRY_CONSTANTS_FUNCTIONS = '''
|
||||
def ceillog2(x: uint64) -> int:
|
||||
return (x - 1).bit_length()
|
||||
def ceillog2(x: int) -> uint64:
|
||||
if x < 1:
|
||||
raise ValueError(f"ceillog2 accepts only positive values, x={x}")
|
||||
return uint64((x - 1).bit_length())
|
||||
'''
|
||||
PHASE0_SUNDRY_FUNCTIONS = '''
|
||||
def get_eth1_data(block: Eth1Block) -> Eth1Data:
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
The contract logic here is based on the deposit contract written in Vyper.
|
||||
|
||||
The original repository (https://github.com/ethereum/deposit_contract) had the following authors:
|
||||
|
||||
@NIC619
|
||||
@carver
|
||||
@hwwhww
|
||||
@davesque
|
||||
@ralexstokes
|
||||
@ChihChengLiang
|
||||
@nisdas
|
||||
@djrtwo
|
||||
@JustinDrake
|
||||
@vbuterin
|
||||
@njgheorghita
|
||||
@CarlBeek
|
||||
|
||||
In eth2.0-specs repository (https://github.com/ethereum/eth2.0-specs), its current location, it had the following additional authors:
|
||||
|
||||
@daejunpark
|
||||
|
||||
And this repository has the following authors:
|
||||
|
||||
@axic
|
||||
@MrChico
|
||||
@chriseth
|
||||
|
||||
(All of the above are GitHub usernames in order of they appeared first in the commit history.)
|
|
@ -0,0 +1,25 @@
|
|||
# Deposit Contract
|
||||
|
||||
## History
|
||||
|
||||
This is a rewrite of the [Vyper Eth 2.0 deposit contract](https://github.com/ethereum/eth2.0-specs/blob/v0.12.2/deposit_contract/contracts/validator_registration.vy) to Solidity.
|
||||
|
||||
The original motivation was to run the SMTChecker and the new Yul IR generator option (`--ir`) in the compiler.
|
||||
|
||||
As of June 2020, version `r1` of the Solidity deposit contract has been verified and is considered for adoption.
|
||||
See this [blog post](https://blog.ethereum.org/2020/06/23/eth2-quick-update-no-12/) for more information.
|
||||
|
||||
In August 2020, version `r2` was released with metadata modifications and relicensed to CC0-1.0. Afterward, this contract has been ported back to from [`axic/eth2-deposit-contract`](https://github.com/axic/eth2-deposit-contract) to this repository and replaced the Vyper deposit contract.
|
||||
|
||||
## Running web3 tests
|
||||
|
||||
1. In the `eth2.0-specs` directory run `make install_deposit_contract_web3_tester` to install the tools needed (make sure to have Python 3.7 and pip installed).
|
||||
2. In the `eth2.0-specs` directory run `make test_deposit_contract_web3_tests` to execute the tests.
|
||||
|
||||
## Running randomized `dapp` tests:
|
||||
|
||||
Install the latest version of `dapp` by following the instructions at [dapp.tools](https://dapp.tools/). Then in the `eth2.0-specs` directory run:
|
||||
|
||||
```sh
|
||||
make test_deposit_contract
|
||||
```
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,178 @@
|
|||
// ┏━━━┓━┏┓━┏┓━━┏━━━┓━━┏━━━┓━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━┏┓━━━━━┏━━━┓━━━━━━━━━┏┓━━━━━━━━━━━━━━┏┓━
|
||||
// ┃┏━━┛┏┛┗┓┃┃━━┃┏━┓┃━━┃┏━┓┃━━━━┗┓┏┓┃━━━━━━━━━━━━━━━━━━┏┛┗┓━━━━┃┏━┓┃━━━━━━━━┏┛┗┓━━━━━━━━━━━━┏┛┗┓
|
||||
// ┃┗━━┓┗┓┏┛┃┗━┓┗┛┏┛┃━━┃┃━┃┃━━━━━┃┃┃┃┏━━┓┏━━┓┏━━┓┏━━┓┏┓┗┓┏┛━━━━┃┃━┗┛┏━━┓┏━┓━┗┓┏┛┏━┓┏━━┓━┏━━┓┗┓┏┛
|
||||
// ┃┏━━┛━┃┃━┃┏┓┃┏━┛┏┛━━┃┃━┃┃━━━━━┃┃┃┃┃┏┓┃┃┏┓┃┃┏┓┃┃━━┫┣┫━┃┃━━━━━┃┃━┏┓┃┏┓┃┃┏┓┓━┃┃━┃┏┛┗━┓┃━┃┏━┛━┃┃━
|
||||
// ┃┗━━┓━┃┗┓┃┃┃┃┃┃┗━┓┏┓┃┗━┛┃━━━━┏┛┗┛┃┃┃━┫┃┗┛┃┃┗┛┃┣━━┃┃┃━┃┗┓━━━━┃┗━┛┃┃┗┛┃┃┃┃┃━┃┗┓┃┃━┃┗┛┗┓┃┗━┓━┃┗┓
|
||||
// ┗━━━┛━┗━┛┗┛┗┛┗━━━┛┗┛┗━━━┛━━━━┗━━━┛┗━━┛┃┏━┛┗━━┛┗━━┛┗┛━┗━┛━━━━┗━━━┛┗━━┛┗┛┗┛━┗━┛┗┛━┗━━━┛┗━━┛━┗━┛
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┃┃━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┗┛━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
pragma solidity 0.6.11;
|
||||
|
||||
// This interface is designed to be compatible with the Vyper version.
|
||||
/// @notice This is the Ethereum 2.0 deposit contract interface.
|
||||
/// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs
|
||||
interface IDepositContract {
|
||||
/// @notice A processed deposit event.
|
||||
event DepositEvent(
|
||||
bytes pubkey,
|
||||
bytes withdrawal_credentials,
|
||||
bytes amount,
|
||||
bytes signature,
|
||||
bytes index
|
||||
);
|
||||
|
||||
/// @notice Submit a Phase 0 DepositData object.
|
||||
/// @param pubkey A BLS12-381 public key.
|
||||
/// @param withdrawal_credentials Commitment to a public key for withdrawals.
|
||||
/// @param signature A BLS12-381 signature.
|
||||
/// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.
|
||||
/// Used as a protection against malformed input.
|
||||
function deposit(
|
||||
bytes calldata pubkey,
|
||||
bytes calldata withdrawal_credentials,
|
||||
bytes calldata signature,
|
||||
bytes32 deposit_data_root
|
||||
) external payable;
|
||||
|
||||
/// @notice Query the current deposit root hash.
|
||||
/// @return The deposit root hash.
|
||||
function get_deposit_root() external view returns (bytes32);
|
||||
|
||||
/// @notice Query the current deposit count.
|
||||
/// @return The deposit count encoded as a little endian 64-bit number.
|
||||
function get_deposit_count() external view returns (bytes memory);
|
||||
}
|
||||
|
||||
// Based on official specification in https://eips.ethereum.org/EIPS/eip-165
|
||||
interface ERC165 {
|
||||
/// @notice Query if a contract implements an interface
|
||||
/// @param interfaceId The interface identifier, as specified in ERC-165
|
||||
/// @dev Interface identification is specified in ERC-165. This function
|
||||
/// uses less than 30,000 gas.
|
||||
/// @return `true` if the contract implements `interfaceId` and
|
||||
/// `interfaceId` is not 0xffffffff, `false` otherwise
|
||||
function supportsInterface(bytes4 interfaceId) external pure returns (bool);
|
||||
}
|
||||
|
||||
// This is a rewrite of the Vyper Eth2.0 deposit contract in Solidity.
|
||||
// It tries to stay as close as possible to the original source code.
|
||||
/// @notice This is the Ethereum 2.0 deposit contract interface.
|
||||
/// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs
|
||||
contract DepositContract is IDepositContract, ERC165 {
|
||||
uint constant DEPOSIT_CONTRACT_TREE_DEPTH = 32;
|
||||
// NOTE: this also ensures `deposit_count` will fit into 64-bits
|
||||
uint constant MAX_DEPOSIT_COUNT = 2**DEPOSIT_CONTRACT_TREE_DEPTH - 1;
|
||||
|
||||
bytes32[DEPOSIT_CONTRACT_TREE_DEPTH] branch;
|
||||
uint256 deposit_count;
|
||||
|
||||
bytes32[DEPOSIT_CONTRACT_TREE_DEPTH] zero_hashes;
|
||||
|
||||
constructor() public {
|
||||
// Compute hashes in empty sparse Merkle tree
|
||||
for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH - 1; height++)
|
||||
zero_hashes[height + 1] = sha256(abi.encodePacked(zero_hashes[height], zero_hashes[height]));
|
||||
}
|
||||
|
||||
function get_deposit_root() override external view returns (bytes32) {
|
||||
bytes32 node;
|
||||
uint size = deposit_count;
|
||||
for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH; height++) {
|
||||
if ((size & 1) == 1)
|
||||
node = sha256(abi.encodePacked(branch[height], node));
|
||||
else
|
||||
node = sha256(abi.encodePacked(node, zero_hashes[height]));
|
||||
size /= 2;
|
||||
}
|
||||
return sha256(abi.encodePacked(
|
||||
node,
|
||||
to_little_endian_64(uint64(deposit_count)),
|
||||
bytes24(0)
|
||||
));
|
||||
}
|
||||
|
||||
function get_deposit_count() override external view returns (bytes memory) {
|
||||
return to_little_endian_64(uint64(deposit_count));
|
||||
}
|
||||
|
||||
function deposit(
|
||||
bytes calldata pubkey,
|
||||
bytes calldata withdrawal_credentials,
|
||||
bytes calldata signature,
|
||||
bytes32 deposit_data_root
|
||||
) override external payable {
|
||||
// Extended ABI length checks since dynamic types are used.
|
||||
require(pubkey.length == 48, "DepositContract: invalid pubkey length");
|
||||
require(withdrawal_credentials.length == 32, "DepositContract: invalid withdrawal_credentials length");
|
||||
require(signature.length == 96, "DepositContract: invalid signature length");
|
||||
|
||||
// Check deposit amount
|
||||
require(msg.value >= 1 ether, "DepositContract: deposit value too low");
|
||||
require(msg.value % 1 gwei == 0, "DepositContract: deposit value not multiple of gwei");
|
||||
uint deposit_amount = msg.value / 1 gwei;
|
||||
require(deposit_amount <= type(uint64).max, "DepositContract: deposit value too high");
|
||||
|
||||
// Emit `DepositEvent` log
|
||||
bytes memory amount = to_little_endian_64(uint64(deposit_amount));
|
||||
emit DepositEvent(
|
||||
pubkey,
|
||||
withdrawal_credentials,
|
||||
amount,
|
||||
signature,
|
||||
to_little_endian_64(uint64(deposit_count))
|
||||
);
|
||||
|
||||
// Compute deposit data root (`DepositData` hash tree root)
|
||||
bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, bytes16(0)));
|
||||
bytes32 signature_root = sha256(abi.encodePacked(
|
||||
sha256(abi.encodePacked(signature[:64])),
|
||||
sha256(abi.encodePacked(signature[64:], bytes32(0)))
|
||||
));
|
||||
bytes32 node = sha256(abi.encodePacked(
|
||||
sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)),
|
||||
sha256(abi.encodePacked(amount, bytes24(0), signature_root))
|
||||
));
|
||||
|
||||
// Verify computed and expected deposit data roots match
|
||||
require(node == deposit_data_root, "DepositContract: reconstructed DepositData does not match supplied deposit_data_root");
|
||||
|
||||
// Avoid overflowing the Merkle tree (and prevent edge case in computing `branch`)
|
||||
require(deposit_count < MAX_DEPOSIT_COUNT, "DepositContract: merkle tree full");
|
||||
|
||||
// Add deposit data root to Merkle tree (update a single `branch` node)
|
||||
deposit_count += 1;
|
||||
uint size = deposit_count;
|
||||
for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH; height++) {
|
||||
if ((size & 1) == 1) {
|
||||
branch[height] = node;
|
||||
return;
|
||||
}
|
||||
node = sha256(abi.encodePacked(branch[height], node));
|
||||
size /= 2;
|
||||
}
|
||||
// As the loop should always end prematurely with the `return` statement,
|
||||
// this code should be unreachable. We assert `false` just to be safe.
|
||||
assert(false);
|
||||
}
|
||||
|
||||
function supportsInterface(bytes4 interfaceId) override external pure returns (bool) {
|
||||
return interfaceId == type(ERC165).interfaceId || interfaceId == type(IDepositContract).interfaceId;
|
||||
}
|
||||
|
||||
function to_little_endian_64(uint64 value) internal pure returns (bytes memory ret) {
|
||||
ret = new bytes(8);
|
||||
bytes8 bytesValue = bytes8(value);
|
||||
// Byteswapping during copying to bytes.
|
||||
ret[0] = bytesValue[7];
|
||||
ret[1] = bytesValue[6];
|
||||
ret[2] = bytesValue[5];
|
||||
ret[3] = bytesValue[4];
|
||||
ret[4] = bytesValue[3];
|
||||
ret[5] = bytesValue[2];
|
||||
ret[6] = bytesValue[1];
|
||||
ret[7] = bytesValue[0];
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
Subproject commit eb7148d43c1ca6f9890361e2e2378364af2430ba
|
|
@ -0,0 +1,18 @@
|
|||
let dapptools = builtins.fetchGit {
|
||||
url = "https://github.com/dapphub/dapptools.git";
|
||||
rev = "11dcefe1f03b0acafe76b4d7d54821ef6bd63131";
|
||||
};
|
||||
nixpkgs = builtins.fetchGit {
|
||||
url = "https://github.com/nixos/nixpkgs";
|
||||
ref = "release-19.03";
|
||||
rev = "f1707d8875276cfa110139435a7e8998b4c2a4fd";
|
||||
};
|
||||
pkgs-for-dapp = import nixpkgs {
|
||||
overlays = [
|
||||
(import (dapptools + /overlay.nix))
|
||||
];
|
||||
};
|
||||
in
|
||||
pkgs-for-dapp.mkShell {
|
||||
buildInputs = [ pkgs-for-dapp.dapp pkgs-for-dapp.solc pkgs-for-dapp.hevm ];
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
pragma solidity ^0.6.2;
|
||||
|
||||
import "../lib/ds-test/src/test.sol";
|
||||
|
||||
import "./vyper_setup.sol";
|
||||
import "../deposit_contract.sol";
|
||||
|
||||
contract DepositContractTest is DSTest {
|
||||
DepositContract depositContract_sol;
|
||||
DepositContract depositContract_vyp;
|
||||
uint64 constant GWEI = 1000000000;
|
||||
|
||||
function setUp() public {
|
||||
VyperSetup vyperSetup = new VyperSetup();
|
||||
depositContract_vyp = DepositContract(vyperSetup.deployDeposit());
|
||||
depositContract_sol = new DepositContract();
|
||||
}
|
||||
|
||||
// --- SUCCESS TESTS ---
|
||||
|
||||
// Tests initialized storage values, comparing vyper and solidity
|
||||
function test_empty_root() public {
|
||||
bytes32 zHash = 0x0000000000000000000000000000000000000000000000000000000000000000;
|
||||
bytes32 zHashN = zHash;
|
||||
for (uint i = 0; i <= 31; i++) {
|
||||
zHashN = sha256(abi.encodePacked(zHashN, zHashN));
|
||||
}
|
||||
assertEq(sha256(abi.encodePacked(zHashN, zHash)), depositContract_vyp.get_deposit_root());
|
||||
assertEq(depositContract_sol.get_deposit_root(), depositContract_vyp.get_deposit_root());
|
||||
}
|
||||
|
||||
// Generates 16 random deposits, insert them in both vyper and solidity version and compare get_deposit_root after each insertion
|
||||
function test_16_deposits(bytes32[16] memory pubkey_one, bytes16[16] memory pubkey_two, bytes32[16] memory _withdrawal_credentials,
|
||||
bytes32[16] memory sig_one, bytes32[16] memory sig_two, bytes32[16] memory sig_three, uint32[16] memory amount) public {
|
||||
uint j;
|
||||
for (uint i = 0; i < 16; i++) {
|
||||
// as of dcaa774, the solidity version is more restrictive than vyper and requires deposits to be divisible by GWEI
|
||||
uint64 gweiamount = amount[i] * GWEI;
|
||||
if (1 ether <= gweiamount) {
|
||||
j++;
|
||||
deposit_in(depositContract_sol, pubkey_one[i], pubkey_two[i], _withdrawal_credentials[i], sig_one[i], sig_two[i], sig_three[i], gweiamount);
|
||||
deposit_in(depositContract_vyp, pubkey_one[i], pubkey_two[i], _withdrawal_credentials[i], sig_one[i], sig_two[i], sig_three[i], gweiamount);
|
||||
|
||||
assertEq(depositContract_sol.get_deposit_root(), depositContract_vyp.get_deposit_root());
|
||||
assertEq(keccak256(abi.encodePacked(depositContract_sol.get_deposit_count())), keccak256(abi.encodePacked(depositContract_vyp.get_deposit_count())));
|
||||
assertEq(keccak256(abi.encodePacked(depositContract_sol.get_deposit_count())), keccak256(to_little_endian_64(uint64(j))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The solidity contract fails when given a deposit which is not divisible by GWEI
|
||||
|
||||
function testFail_deposit_not_divisible_by_gwei(bytes32 pubkey_one, bytes16 pubkey_two, bytes32 _withdrawal_credentials,
|
||||
bytes32 sig_one, bytes32 sig_two, bytes32 sig_three) public {
|
||||
deposit_in(depositContract_sol, pubkey_one, pubkey_two, _withdrawal_credentials, sig_one, sig_two, sig_three, 1 ether + 1);
|
||||
}
|
||||
|
||||
// --- FAILURE TESTS ---
|
||||
|
||||
// if the node is given randomly instead of as the ssz root, the chances of success are so unlikely that we can assert it to be false
|
||||
function testFail_malformed_node_vyp(bytes32 pubkey_one, bytes16 pubkey_two, bytes32 _withdrawal_credentials, bytes32 sig_one,
|
||||
bytes32 sig_two, bytes32 sig_three, uint64 amount, bytes32 node) public {
|
||||
bytes memory pubkey = abi.encodePacked(pubkey_one, pubkey_two);
|
||||
bytes memory withdrawal_credentials = abi.encodePacked(_withdrawal_credentials); //I wish just recasting to `bytes` would work..
|
||||
bytes memory signature = abi.encodePacked(sig_one, sig_two, sig_three);
|
||||
depositContract_vyp.deposit{value: amount}(pubkey, withdrawal_credentials, signature, node);
|
||||
}
|
||||
|
||||
// If the node is taken randomly instead of as the ssz root, the chances of success are so unlikely that we can assert it to be false
|
||||
function testFail_malformed_node_sol(bytes32 pubkey_one, bytes16 pubkey_two, bytes32 _withdrawal_credentials, bytes32 sig_one,
|
||||
bytes32 sig_two, bytes32 sig_three, uint64 amount, bytes32 node) public {
|
||||
bytes memory pubkey = abi.encodePacked(pubkey_one, pubkey_two);
|
||||
bytes memory withdrawal_credentials = abi.encodePacked(_withdrawal_credentials);
|
||||
bytes memory signature = abi.encodePacked(sig_one, sig_two, sig_three);
|
||||
depositContract_sol.deposit{value: amount}(pubkey, withdrawal_credentials, signature, node);
|
||||
}
|
||||
|
||||
// if bytes lengths are wrong, the call will fail
|
||||
function testFail_malformed_calldata_vyp(bytes memory pubkey, bytes memory withdrawal_credentials, bytes memory signature, uint64 amount) public {
|
||||
if (amount >= 1000000000000000000) {
|
||||
if (!(pubkey.length == 48 && withdrawal_credentials.length == 32 && signature.length == 96)) {
|
||||
depositContract_vyp.deposit{value: amount}(pubkey, withdrawal_credentials, signature,
|
||||
encode_node(pubkey, withdrawal_credentials, signature, to_little_endian_64(amount / GWEI))
|
||||
);
|
||||
} else { revert(); }
|
||||
} else { revert(); }
|
||||
}
|
||||
|
||||
function testFail_malformed_calldata_sol(bytes memory pubkey, bytes memory withdrawal_credentials, bytes memory signature, uint64 amount) public {
|
||||
if (amount >= 1000000000000000000) {
|
||||
if (!(pubkey.length == 48 && withdrawal_credentials.length == 32 && signature.length == 96)) {
|
||||
depositContract_sol.deposit{value: amount}(pubkey, withdrawal_credentials, signature,
|
||||
encode_node(pubkey, withdrawal_credentials, signature, to_little_endian_64(amount / GWEI))
|
||||
);
|
||||
} else { revert(); }
|
||||
} else { revert(); }
|
||||
}
|
||||
|
||||
// --- HELPER FUNCTIONS ---
|
||||
|
||||
function deposit_in(DepositContract depositContract, bytes32 pubkey_one, bytes16 pubkey_two, bytes32 _withdrawal_credentials, bytes32 sig_one, bytes32 sig_two, bytes32 sig_three, uint64 amount) public {
|
||||
bytes memory pubkey = abi.encodePacked(pubkey_one, pubkey_two);
|
||||
bytes memory withdrawal_credentials = abi.encodePacked(_withdrawal_credentials);
|
||||
bytes memory signature = abi.encodePacked(sig_one, sig_two, sig_three);
|
||||
bytes32 node = encode_node(pubkey, withdrawal_credentials, signature, to_little_endian_64(amount / GWEI));
|
||||
depositContract.deposit{value: amount}(pubkey, withdrawal_credentials, signature, node);
|
||||
}
|
||||
|
||||
function slice(bytes memory a, uint32 offset, uint32 size) pure internal returns (bytes memory result) {
|
||||
result = new bytes(size);
|
||||
for (uint i = 0; i < size; i++) {
|
||||
result[i] = a[offset + i];
|
||||
}
|
||||
}
|
||||
|
||||
function encode_node(bytes memory pubkey, bytes memory withdrawal_credentials, bytes memory signature, bytes memory amount) public pure returns (bytes32) {
|
||||
bytes16 zero_bytes16;
|
||||
bytes24 zero_bytes24;
|
||||
bytes32 zero_bytes32;
|
||||
bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, zero_bytes16));
|
||||
bytes32 signature_root = sha256(abi.encodePacked(
|
||||
sha256(abi.encodePacked(slice(signature, 0, 64))),
|
||||
sha256(abi.encodePacked(slice(signature, 64, 32), zero_bytes32))
|
||||
));
|
||||
return sha256(abi.encodePacked(
|
||||
sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)),
|
||||
sha256(abi.encodePacked(amount, zero_bytes24, signature_root))
|
||||
));
|
||||
}
|
||||
|
||||
function to_little_endian_64(uint64 value) internal pure returns (bytes memory ret) {
|
||||
ret = new bytes(8);
|
||||
ret[0] = bytes1(uint8(value & 0xff));
|
||||
ret[1] = bytes1(uint8((value >> 8) & 0xff));
|
||||
ret[2] = bytes1(uint8((value >> 16) & 0xff));
|
||||
ret[3] = bytes1(uint8((value >> 24) & 0xff));
|
||||
ret[4] = bytes1(uint8((value >> 32) & 0xff));
|
||||
ret[5] = bytes1(uint8((value >> 40) & 0xff));
|
||||
ret[6] = bytes1(uint8((value >> 48) & 0xff));
|
||||
ret[7] = bytes1(uint8((value >> 56) & 0xff));
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -15,7 +15,7 @@ DIR = os.path.dirname(__file__)
|
|||
|
||||
|
||||
def get_deposit_contract_json():
|
||||
file_path = os.path.join(DIR, '../../contracts/validator_registration.json')
|
||||
file_path = os.path.join(DIR, '../../deposit_contract.json')
|
||||
deposit_contract_json = open(file_path).read()
|
||||
return json.loads(deposit_contract_json)
|
||||
|
|
@ -6,7 +6,7 @@ from eth2spec.phase0.spec import DepositData
|
|||
from eth2spec.utils.ssz.ssz_typing import List
|
||||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
||||
|
||||
from deposit_contract.conftest import (
|
||||
from tests.conftest import (
|
||||
FULL_DEPOSIT_AMOUNT,
|
||||
MIN_DEPOSIT_AMOUNT,
|
||||
)
|
|
@ -195,6 +195,7 @@ The following values are (non-configurable) constants used throughout the specif
|
|||
| `HYSTERESIS_QUOTIENT` | `uint64(4)` |
|
||||
| `HYSTERESIS_DOWNWARD_MULTIPLIER` | `uint64(1)` |
|
||||
| `HYSTERESIS_UPWARD_MULTIPLIER` | `uint64(5)` |
|
||||
| `PROPORTIONAL_SLASHING_MULTIPLIER` | `uint64(3)` |
|
||||
|
||||
- For the safety of committees, `TARGET_COMMITTEE_SIZE` exceeds [the recommended minimum committee size of 111](http://web.archive.org/web/20190504131341/https://vitalik.ca/files/Ithaca201807_Sharding.pdf); with sufficient active validators (at least `SLOTS_PER_EPOCH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes of at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.)
|
||||
|
||||
|
@ -1554,10 +1555,11 @@ def process_registry_updates(state: BeaconState) -> None:
|
|||
def process_slashings(state: BeaconState) -> None:
|
||||
epoch = get_current_epoch(state)
|
||||
total_balance = get_total_active_balance(state)
|
||||
adjusted_total_slashing_balance = min(sum(state.slashings) * PROPORTIONAL_SLASHING_MULTIPLIER, total_balance)
|
||||
for index, validator in enumerate(state.validators):
|
||||
if validator.slashed and epoch + EPOCHS_PER_SLASHINGS_VECTOR // 2 == validator.withdrawable_epoch:
|
||||
increment = EFFECTIVE_BALANCE_INCREMENT # Factored out from penalty numerator to avoid uint64 overflow
|
||||
penalty_numerator = validator.effective_balance // increment * min(sum(state.slashings) * 3, total_balance)
|
||||
penalty_numerator = validator.effective_balance // increment * adjusted_total_slashing_balance
|
||||
penalty = penalty_numerator // total_balance * increment
|
||||
decrease_balance(state, ValidatorIndex(index), penalty)
|
||||
```
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
- [Deposit amount](#deposit-amount)
|
||||
- [Withdrawal credentials](#withdrawal-credentials)
|
||||
- [`DepositEvent` log](#depositevent-log)
|
||||
- [Vyper code](#vyper-code)
|
||||
- [Solidity code](#solidity-code)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- /TOC -->
|
||||
|
@ -53,7 +53,7 @@ _Note_: See [here](https://chainid.network/) for a comprehensive list of public
|
|||
|
||||
### `deposit` function
|
||||
|
||||
The deposit contract has a public `deposit` function to make deposits. It takes as arguments `pubkey: bytes[48], withdrawal_credentials: bytes[32], signature: bytes[96], deposit_data_root: bytes32`. The first three arguments populate a [`DepositData`](./beacon-chain.md#depositdata) object, and `deposit_data_root` is the expected `DepositData` root as a protection against malformatted calldata.
|
||||
The deposit contract has a public `deposit` function to make deposits. It takes as arguments `bytes calldata pubkey, bytes calldata withdrawal_credentials, bytes calldata signature, bytes32 deposit_data_root`. The first three arguments populate a [`DepositData`](./beacon-chain.md#depositdata) object, and `deposit_data_root` is the expected `DepositData` root as a protection against malformatted calldata.
|
||||
|
||||
#### Deposit amount
|
||||
|
||||
|
@ -72,8 +72,8 @@ The private key corresponding to `withdrawal_pubkey` will be required to initiat
|
|||
|
||||
Every Ethereum 1.0 deposit emits a `DepositEvent` log for consumption by the beacon chain. The deposit contract does little validation, pushing most of the validator onboarding logic to the beacon chain. In particular, the proof of possession (a BLS12-381 signature) is not verified by the deposit contract.
|
||||
|
||||
## Vyper code
|
||||
## Solidity code
|
||||
|
||||
The deposit contract source code, written in Vyper, is available [here](../../deposit_contract/contracts/validator_registration.vy).
|
||||
The deposit contract source code, written in Solidity, is available [here](../../solidity_deposit_contract/deposit_contract.sol).
|
||||
|
||||
*Note*: To save on gas, the deposit contract uses a progressive Merkle root calculation algorithm that requires only O(log(n)) storage. See [here](https://github.com/ethereum/research/blob/master/beacon_chain_impl/progressive_merkle_tree.py) for a Python implementation, and [here](https://github.com/runtimeverification/verified-smart-contracts/blob/master/deposit/formal-incremental-merkle-tree-algorithm.pdf) for a formal correctness proof.
|
||||
|
|
|
@ -391,8 +391,10 @@ The following validations MUST pass before forwarding the `attestation` on the s
|
|||
(within a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) --
|
||||
i.e. `attestation.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= current_slot >= attestation.data.slot`
|
||||
(a client MAY queue future attestations for processing at the appropriate slot).
|
||||
- _[REJECT]_ The attestation's epoch matches its target -- i.e. `attestation.data.target.epoch ==
|
||||
compute_epoch_at_slot(attestation.data.slot)`
|
||||
- _[REJECT]_ The attestation is unaggregated --
|
||||
that is, it has exactly one participating validator (`len([bit in bit attestation.aggregation_bits if bit]) == 1`, i.e. exactly 1 bit is set).
|
||||
that is, it has exactly one participating validator (`len([bit for bit in attestation.aggregation_bits if bit]) == 1`, i.e. exactly 1 bit is set).
|
||||
- _[IGNORE]_ There has been no other valid attestation seen on an attestation subnet
|
||||
that has an identical `attestation.data.target.epoch` and participating validator index.
|
||||
- _[REJECT]_ The signature of `attestation` is valid.
|
||||
|
@ -400,12 +402,15 @@ The following validations MUST pass before forwarding the `attestation` on the s
|
|||
(via both gossip and non-gossip sources)
|
||||
(a client MAY queue aggregates for processing once block is retrieved).
|
||||
- _[REJECT]_ The block being voted for (`attestation.data.beacon_block_root`) passes validation.
|
||||
- _[REJECT]_ The attestation's target block is an ancestor of the block named in the LMD vote -- i.e.
|
||||
`get_ancestor(store, attestation.data.beacon_block_root, compute_start_slot_at_epoch(attestation.data.target.epoch)) == attestation.data.target.root`
|
||||
- _[REJECT]_ The current `finalized_checkpoint` is an ancestor of the `block` defined by `attestation.data.beacon_block_root` -- i.e.
|
||||
`get_ancestor(store, attestation.data.beacon_block_root, compute_start_slot_at_epoch(store.finalized_checkpoint.epoch))
|
||||
== store.finalized_checkpoint.root`
|
||||
|
||||
|
||||
|
||||
|
||||
#### Attestations and Aggregation
|
||||
|
||||
Attestation broadcasting is grouped into subnets defined by a topic.
|
||||
|
@ -723,7 +728,7 @@ The request MUST be encoded as an SSZ-container.
|
|||
The response MUST consist of zero or more `response_chunk`.
|
||||
Each _successful_ `response_chunk` MUST contain a single `SignedBeaconBlock` payload.
|
||||
|
||||
Clients MUST keep a record of signed blocks seen since the since the start of the weak subjectivity period
|
||||
Clients MUST keep a record of signed blocks seen since the start of the weak subjectivity period
|
||||
and MUST support serving requests of blocks up to their own `head_block_root`.
|
||||
|
||||
Clients MUST respond with at least the first block that exists in the range, if they have it, and no more than `MAX_REQUEST_BLOCKS` blocks.
|
||||
|
|
|
@ -194,9 +194,14 @@ def is_proposer(state: BeaconState, validator_index: ValidatorIndex) -> bool:
|
|||
|
||||
### Lookahead
|
||||
|
||||
The beacon chain shufflings are designed to provide a minimum of 1 epoch lookahead on the validator's upcoming committee assignments for attesting dictated by the shuffling and slot. Note that this lookahead does not apply to proposing, which must be checked during the epoch in question.
|
||||
The beacon chain shufflings are designed to provide a minimum of 1 epoch lookahead
|
||||
on the validator's upcoming committee assignments for attesting dictated by the shuffling and slot.
|
||||
Note that this lookahead does not apply to proposing, which must be checked during the epoch in question.
|
||||
|
||||
`get_committee_assignment` should be called at the start of each epoch to get the assignment for the next epoch (`current_epoch + 1`). A validator should plan for future assignments by noting at which future slot they will have to attest and joining the committee index attestation subnet related to their committee assignment.
|
||||
`get_committee_assignment` should be called at the start of each epoch
|
||||
to get the assignment for the next epoch (`current_epoch + 1`).
|
||||
A validator should plan for future assignments by noting their assigned attestation
|
||||
slot and joining the committee index attestation subnet related to their committee assignment.
|
||||
|
||||
Specifically a validator should:
|
||||
* Call `get_committee_assignment(state, next_epoch, validator_index)` when checking for next epoch assignments.
|
||||
|
@ -214,9 +219,18 @@ A validator has two primary responsibilities to the beacon chain: [proposing blo
|
|||
|
||||
### Block proposal
|
||||
|
||||
A validator is expected to propose a [`SignedBeaconBlock`](./beacon-chain.md#signedbeaconblock) at the beginning of any slot during which `is_proposer(state, validator_index)` returns `True`. To propose, the validator selects the `BeaconBlock`, `parent`, that in their view of the fork choice is the head of the chain during `slot - 1`. The validator creates, signs, and broadcasts a `block` that is a child of `parent` that satisfies a valid [beacon chain state transition](./beacon-chain.md#beacon-chain-state-transition-function).
|
||||
A validator is expected to propose a [`SignedBeaconBlock`](./beacon-chain.md#signedbeaconblock) at
|
||||
the beginning of any slot during which `is_proposer(state, validator_index)` returns `True`.
|
||||
To propose, the validator selects the `BeaconBlock`, `parent`,
|
||||
that in their view of the fork choice is the head of the chain during `slot - 1`.
|
||||
The validator creates, signs, and broadcasts a `block` that is a child of `parent`
|
||||
that satisfies a valid [beacon chain state transition](./beacon-chain.md#beacon-chain-state-transition-function).
|
||||
|
||||
There is one proposer per slot, so if there are N active validators any individual validator will on average be assigned to propose once per N slots (e.g. at 312,500 validators = 10 million ETH, that's once per ~6 weeks).
|
||||
There is one proposer per slot, so if there are N active validators any individual validator
|
||||
will on average be assigned to propose once per N slots (e.g. at 312,500 validators = 10 million ETH, that's once per ~6 weeks).
|
||||
|
||||
*Note*: In this section, `state` is the state of the slot for the block proposal _without_ the block yet applied.
|
||||
That is, `state` is the `previous_state` processed through any empty slots up to the assigned slot using `process_slots(previous_state, slot)`.
|
||||
|
||||
#### Preparing for a `BeaconBlock`
|
||||
|
||||
|
@ -251,7 +265,13 @@ def get_epoch_signature(state: BeaconState, block: BeaconBlock, privkey: int) ->
|
|||
|
||||
##### Eth1 Data
|
||||
|
||||
The `block.body.eth1_data` field is for block proposers to vote on recent Eth1 data. This recent data contains an Eth1 block hash as well as the associated deposit root (as calculated by the `get_deposit_root()` method of the deposit contract) and deposit count after execution of the corresponding Eth1 block. If over half of the block proposers in the current Eth1 voting period vote for the same `eth1_data` then `state.eth1_data` updates immediately allowing new deposits to be processed. Each deposit in `block.body.deposits` must verify against `state.eth1_data.eth1_deposit_root`.
|
||||
The `block.body.eth1_data` field is for block proposers to vote on recent Eth1 data.
|
||||
This recent data contains an Eth1 block hash as well as the associated deposit root
|
||||
(as calculated by the `get_deposit_root()` method of the deposit contract) and
|
||||
deposit count after execution of the corresponding Eth1 block.
|
||||
If over half of the block proposers in the current Eth1 voting period vote for the same
|
||||
`eth1_data` then `state.eth1_data` updates immediately allowing new deposits to be processed.
|
||||
Each deposit in `block.body.deposits` must verify against `state.eth1_data.eth1_deposit_root`.
|
||||
|
||||
###### `Eth1Block`
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
- [`compute_updated_gasprice`](#compute_updated_gasprice)
|
||||
- [`compute_committee_source_epoch`](#compute_committee_source_epoch)
|
||||
- [Beacon state accessors](#beacon-state-accessors)
|
||||
- [Updated `get_committee_count_per_slot`](#updated-get_committee_count_per_slot)
|
||||
- [`get_active_shard_count`](#get_active_shard_count)
|
||||
- [`get_online_validator_indices`](#get_online_validator_indices)
|
||||
- [`get_shard_committee`](#get_shard_committee)
|
||||
|
@ -105,6 +106,7 @@ Configuration is not namespaced. Instead it is strictly an extension;
|
|||
| Name | Value |
|
||||
| - | - |
|
||||
| `MAX_SHARDS` | `2**10` (= 1024) |
|
||||
| `INITIAL_ACTIVE_SHARDS` | `2**6` (= 64) |
|
||||
| `LIGHT_CLIENT_COMMITTEE_SIZE` | `2**7` (= 128) |
|
||||
| `GASPRICE_ADJUSTMENT_COEFFICIENT` | `2**3` (= 8) |
|
||||
|
||||
|
@ -510,11 +512,28 @@ def compute_committee_source_epoch(epoch: Epoch, period: uint64) -> Epoch:
|
|||
|
||||
### Beacon state accessors
|
||||
|
||||
#### Updated `get_committee_count_per_slot`
|
||||
|
||||
```python
|
||||
def get_committee_count_per_slot(state: BeaconState, epoch: Epoch) -> uint64:
|
||||
"""
|
||||
Return the number of committees in each slot for the given ``epoch``.
|
||||
"""
|
||||
return max(uint64(1), min(
|
||||
get_active_shard_count(state),
|
||||
uint64(len(get_active_validator_indices(state, epoch))) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE,
|
||||
))
|
||||
```
|
||||
|
||||
#### `get_active_shard_count`
|
||||
|
||||
```python
|
||||
def get_active_shard_count(state: BeaconState) -> uint64:
|
||||
return len(state.shard_states) # May adapt in the future, or change over time.
|
||||
"""
|
||||
Return the number of active shards.
|
||||
Note that this puts an upper bound on the number of committees per slot.
|
||||
"""
|
||||
return INITIAL_ACTIVE_SHARDS
|
||||
```
|
||||
|
||||
#### `get_online_validator_indices`
|
||||
|
@ -535,12 +554,11 @@ def get_shard_committee(beacon_state: BeaconState, epoch: Epoch, shard: Shard) -
|
|||
source_epoch = compute_committee_source_epoch(epoch, SHARD_COMMITTEE_PERIOD)
|
||||
active_validator_indices = get_active_validator_indices(beacon_state, source_epoch)
|
||||
seed = get_seed(beacon_state, source_epoch, DOMAIN_SHARD_COMMITTEE)
|
||||
active_shard_count = get_active_shard_count(beacon_state)
|
||||
return compute_committee(
|
||||
indices=active_validator_indices,
|
||||
seed=seed,
|
||||
index=shard,
|
||||
count=active_shard_count,
|
||||
count=get_active_shard_count(beacon_state),
|
||||
)
|
||||
```
|
||||
|
||||
|
@ -607,10 +625,11 @@ def get_start_shard(state: BeaconState, slot: Slot) -> Shard:
|
|||
else:
|
||||
# Previous epoch
|
||||
shard_delta = get_committee_count_delta(state, start_slot=slot, stop_slot=current_epoch_start_slot)
|
||||
max_committees_per_epoch = MAX_COMMITTEES_PER_SLOT * SLOTS_PER_EPOCH
|
||||
max_committees_per_slot = active_shard_count
|
||||
max_committees_in_span = max_committees_per_slot * (current_epoch_start_slot - slot)
|
||||
return Shard(
|
||||
# Ensure positive
|
||||
(state.current_epoch_start_shard + max_committees_per_epoch * active_shard_count - shard_delta)
|
||||
(state.current_epoch_start_shard + max_committees_in_span - shard_delta)
|
||||
% active_shard_count
|
||||
)
|
||||
```
|
||||
|
@ -742,7 +761,6 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
|
|||
def validate_attestation(state: BeaconState, attestation: Attestation) -> None:
|
||||
data = attestation.data
|
||||
assert data.index < get_committee_count_per_slot(state, data.target.epoch)
|
||||
assert data.index < get_active_shard_count(state)
|
||||
assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state))
|
||||
assert data.target.epoch == compute_epoch_at_slot(data.slot)
|
||||
assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= data.slot + SLOTS_PER_EPOCH
|
||||
|
|
|
@ -36,7 +36,6 @@ Warning: this configuration is not definitive.
|
|||
| - | - |
|
||||
| `PHASE_1_FORK_VERSION` | `Version('0x01000000')` |
|
||||
| `PHASE_1_FORK_SLOT` | `Slot(0)` **TBD** |
|
||||
| `INITIAL_ACTIVE_SHARDS` | `2**6` (= 64) |
|
||||
|
||||
## Fork to Phase 1
|
||||
|
||||
|
|
|
@ -165,7 +165,7 @@ variable_lengths = [len(part) for part in variable_parts]
|
|||
assert sum(fixed_lengths + variable_lengths) < 2**(BYTES_PER_LENGTH_OFFSET * BITS_PER_BYTE)
|
||||
|
||||
# Interleave offsets of variable-size parts with fixed-size parts
|
||||
variable_offsets = [serialize(sum(fixed_lengths + variable_lengths[:i])) for i in range(len(value))]
|
||||
variable_offsets = [serialize(uint32(sum(fixed_lengths + variable_lengths[:i]))) for i in range(len(value))]
|
||||
fixed_parts = [part if part != None else variable_offsets[i] for i, part in enumerate(fixed_parts)]
|
||||
|
||||
# Return the concatenation of the fixed-size parts (offsets interleaved) with the variable-size parts
|
||||
|
|
|
@ -31,7 +31,7 @@ def pytest_addoption(parser):
|
|||
help="config: make the pyspec use the specified configuration"
|
||||
)
|
||||
parser.addoption(
|
||||
"--disable-bls", action="store_true",
|
||||
"--disable-bls", action="store_true", default=False,
|
||||
help="bls-default: make tests that are not dependent on BLS run without BLS"
|
||||
)
|
||||
parser.addoption(
|
||||
|
|
|
@ -158,6 +158,15 @@ def low_single_balance(spec):
|
|||
return [1]
|
||||
|
||||
|
||||
def large_validator_set(spec):
|
||||
"""
|
||||
Helper method to create a series of default balances.
|
||||
Usage: `@with_custom_state(balances_fn=default_balances, ...)`
|
||||
"""
|
||||
num_validators = 2 * spec.SLOTS_PER_EPOCH * spec.MAX_COMMITTEES_PER_SLOT * spec.TARGET_COMMITTEE_SIZE
|
||||
return [spec.MAX_EFFECTIVE_BALANCE] * num_validators
|
||||
|
||||
|
||||
def single_phase(fn):
|
||||
"""
|
||||
Decorator that filters out the phases data.
|
||||
|
|
|
@ -22,8 +22,12 @@ from eth2spec.test.helpers.shard_transitions import get_shard_transition_of_comm
|
|||
|
||||
from eth2spec.test.context import (
|
||||
PHASE0, PHASE1,
|
||||
spec_state_test, with_all_phases, expect_assertion_error, always_bls, with_phases, dump_skipping_message,
|
||||
spec_test, spec_state_test, dump_skipping_message,
|
||||
with_phases, with_all_phases, single_phase,
|
||||
expect_assertion_error, always_bls,
|
||||
disable_process_reveal_deadlines,
|
||||
with_custom_state,
|
||||
large_validator_set,
|
||||
)
|
||||
|
||||
|
||||
|
@ -71,6 +75,7 @@ def test_same_slot_block_transition(spec, state):
|
|||
def test_empty_block_transition(spec, state):
|
||||
pre_slot = state.slot
|
||||
pre_eth1_votes = len(state.eth1_data_votes)
|
||||
pre_mix = spec.get_randao_mix(state, spec.get_current_epoch(state))
|
||||
|
||||
yield 'pre', state
|
||||
|
||||
|
@ -83,7 +88,30 @@ def test_empty_block_transition(spec, state):
|
|||
|
||||
assert len(state.eth1_data_votes) == pre_eth1_votes + 1
|
||||
assert spec.get_block_root_at_slot(state, pre_slot) == signed_block.message.parent_root
|
||||
assert spec.get_randao_mix(state, spec.get_current_epoch(state)) != spec.Bytes32()
|
||||
assert spec.get_randao_mix(state, spec.get_current_epoch(state)) != pre_mix
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_test
|
||||
@with_custom_state(balances_fn=large_validator_set, threshold_fn=lambda spec: spec.EJECTION_BALANCE)
|
||||
@single_phase
|
||||
def test_empty_block_transition_large_validator_set(spec, state):
|
||||
pre_slot = state.slot
|
||||
pre_eth1_votes = len(state.eth1_data_votes)
|
||||
pre_mix = spec.get_randao_mix(state, spec.get_current_epoch(state))
|
||||
|
||||
yield 'pre', state
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
assert len(state.eth1_data_votes) == pre_eth1_votes + 1
|
||||
assert spec.get_block_root_at_slot(state, pre_slot) == signed_block.message.parent_root
|
||||
assert spec.get_randao_mix(state, spec.get_current_epoch(state)) != pre_mix
|
||||
|
||||
|
||||
def process_and_sign_block_without_header_validations(spec, state, block):
|
||||
|
@ -289,6 +317,26 @@ def test_empty_epoch_transition(spec, state):
|
|||
assert spec.get_block_root_at_slot(state, slot) == block.parent_root
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_test
|
||||
@with_custom_state(balances_fn=large_validator_set, threshold_fn=lambda spec: spec.EJECTION_BALANCE)
|
||||
@single_phase
|
||||
def test_empty_epoch_transition_large_validator_set(spec, state):
|
||||
pre_slot = state.slot
|
||||
yield 'pre', state
|
||||
|
||||
block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
|
||||
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
assert state.slot == block.slot
|
||||
for slot in range(pre_slot, state.slot):
|
||||
assert spec.get_block_root_at_slot(state, slot) == block.parent_root
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_empty_epoch_transition_not_finalizing(spec, state):
|
||||
|
|
|
@ -74,3 +74,16 @@ def test_get_start_shard_previous_slot(spec, state):
|
|||
- spec.get_committee_count_delta(state, start_slot=slot, stop_slot=current_epoch_start_slot)
|
||||
) % active_shard_count
|
||||
assert start_shard == expected_start_shard
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_get_start_shard_far_past_epoch(spec, state):
|
||||
initial_epoch = spec.get_current_epoch(state)
|
||||
initial_start_slot = spec.compute_start_slot_at_epoch(initial_epoch)
|
||||
initial_start_shard = state.current_epoch_start_shard
|
||||
|
||||
for _ in range(spec.MAX_SHARDS + 2):
|
||||
next_epoch(spec, state)
|
||||
|
||||
assert spec.get_start_shard(state, initial_start_slot) == initial_start_shard
|
||||
|
|
Loading…
Reference in New Issue