Merge branch 'dev' into container-cleanup
This commit is contained in:
commit
a6230425b8
|
@ -48,7 +48,7 @@ GENESIS_FORK_VERSION: 0x00000000
|
||||||
GENESIS_SLOT: 0
|
GENESIS_SLOT: 0
|
||||||
# 2**64 - 1
|
# 2**64 - 1
|
||||||
FAR_FUTURE_EPOCH: 18446744073709551615
|
FAR_FUTURE_EPOCH: 18446744073709551615
|
||||||
BLS_WITHDRAWAL_PREFIX_BYTE: 0x00
|
BLS_WITHDRAWAL_PREFIX: 0
|
||||||
|
|
||||||
|
|
||||||
# Time parameters
|
# Time parameters
|
||||||
|
@ -90,7 +90,7 @@ SLASHED_EXIT_LENGTH: 8192
|
||||||
# Reward and penalty quotients
|
# Reward and penalty quotients
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
# 2**5 (= 32)
|
# 2**5 (= 32)
|
||||||
BASE_REWARD_QUOTIENT: 32
|
BASE_REWARD_FACTOR: 32
|
||||||
# 2**9 (= 512)
|
# 2**9 (= 512)
|
||||||
WHISTLEBLOWING_REWARD_QUOTIENT: 512
|
WHISTLEBLOWING_REWARD_QUOTIENT: 512
|
||||||
# 2**3 (= 8)
|
# 2**3 (= 8)
|
||||||
|
|
|
@ -47,7 +47,7 @@ GENESIS_FORK_VERSION: 0x00000000
|
||||||
GENESIS_SLOT: 0
|
GENESIS_SLOT: 0
|
||||||
# 2**64 - 1
|
# 2**64 - 1
|
||||||
FAR_FUTURE_EPOCH: 18446744073709551615
|
FAR_FUTURE_EPOCH: 18446744073709551615
|
||||||
BLS_WITHDRAWAL_PREFIX_BYTE: 0x00
|
BLS_WITHDRAWAL_PREFIX: 0
|
||||||
|
|
||||||
|
|
||||||
# Time parameters
|
# Time parameters
|
||||||
|
@ -58,23 +58,23 @@ SECONDS_PER_SLOT: 6
|
||||||
MIN_ATTESTATION_INCLUSION_DELAY: 2
|
MIN_ATTESTATION_INCLUSION_DELAY: 2
|
||||||
# [customized] fast epochs
|
# [customized] fast epochs
|
||||||
SLOTS_PER_EPOCH: 8
|
SLOTS_PER_EPOCH: 8
|
||||||
# 2**0 (= 1) epochs 6.4 minutes
|
# 2**0 (= 1) epochs
|
||||||
MIN_SEED_LOOKAHEAD: 1
|
MIN_SEED_LOOKAHEAD: 1
|
||||||
# 2**2 (= 4) epochs 25.6 minutes
|
# 2**2 (= 4) epochs
|
||||||
ACTIVATION_EXIT_DELAY: 4
|
ACTIVATION_EXIT_DELAY: 4
|
||||||
# [customized] higher frequency new deposits from eth1 for testing
|
# [customized] higher frequency new deposits from eth1 for testing
|
||||||
SLOTS_PER_ETH1_VOTING_PERIOD: 16
|
SLOTS_PER_ETH1_VOTING_PERIOD: 16
|
||||||
# [customized] smaller state
|
# [customized] smaller state
|
||||||
SLOTS_PER_HISTORICAL_ROOT: 64
|
SLOTS_PER_HISTORICAL_ROOT: 64
|
||||||
# 2**8 (= 256) epochs ~27 hours
|
# 2**8 (= 256) epochs
|
||||||
MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256
|
MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256
|
||||||
# 2**11 (= 2,048) epochs 9 days
|
# 2**11 (= 2,048) epochs
|
||||||
PERSISTENT_COMMITTEE_PERIOD: 2048
|
PERSISTENT_COMMITTEE_PERIOD: 2048
|
||||||
# 2**6 (= 64) epochs ~7 hours
|
# 2**6 (= 64) epochs
|
||||||
MAX_EPOCHS_PER_CROSSLINK: 64
|
MAX_EPOCHS_PER_CROSSLINK: 64
|
||||||
# 2**2 (= 4) epochs 25.6 minutes
|
# 2**2 (= 4) epochs
|
||||||
MIN_EPOCHS_TO_INACTIVITY_PENALTY: 4
|
MIN_EPOCHS_TO_INACTIVITY_PENALTY: 4
|
||||||
# [customized] 2**12 (= 4,096) epochs 18 days
|
# [customized] 2**12 (= 4,096) epochs
|
||||||
EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS: 4096
|
EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS: 4096
|
||||||
|
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ SLASHED_EXIT_LENGTH: 64
|
||||||
# Reward and penalty quotients
|
# Reward and penalty quotients
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
# 2**5 (= 32)
|
# 2**5 (= 32)
|
||||||
BASE_REWARD_QUOTIENT: 32
|
BASE_REWARD_FACTOR: 32
|
||||||
# 2**9 (= 512)
|
# 2**9 (= 512)
|
||||||
WHISTLEBLOWING_REWARD_QUOTIENT: 512
|
WHISTLEBLOWING_REWARD_QUOTIENT: 512
|
||||||
# 2**3 (= 8)
|
# 2**3 (= 8)
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,140 +1,103 @@
|
||||||
MIN_DEPOSIT_AMOUNT: constant(uint256) = 1000000000 # Gwei
|
MIN_DEPOSIT_AMOUNT: constant(uint256) = 1000000000 # Gwei
|
||||||
FULL_DEPOSIT_AMOUNT: constant(uint256) = 32000000000 # Gwei
|
|
||||||
CHAIN_START_FULL_DEPOSIT_THRESHOLD: constant(uint256) = 65536 # 2**16
|
|
||||||
DEPOSIT_CONTRACT_TREE_DEPTH: constant(uint256) = 32
|
DEPOSIT_CONTRACT_TREE_DEPTH: constant(uint256) = 32
|
||||||
SECONDS_PER_DAY: constant(uint256) = 86400
|
MAX_DEPOSIT_COUNT: constant(uint256) = 4294967295 # 2**DEPOSIT_CONTRACT_TREE_DEPTH - 1
|
||||||
MAX_64_BIT_VALUE: constant(uint256) = 18446744073709551615 # 2**64 - 1
|
|
||||||
PUBKEY_LENGTH: constant(uint256) = 48 # bytes
|
PUBKEY_LENGTH: constant(uint256) = 48 # bytes
|
||||||
WITHDRAWAL_CREDENTIALS_LENGTH: constant(uint256) = 32 # bytes
|
WITHDRAWAL_CREDENTIALS_LENGTH: constant(uint256) = 32 # bytes
|
||||||
|
AMOUNT_LENGTH: constant(uint256) = 8 # bytes
|
||||||
SIGNATURE_LENGTH: constant(uint256) = 96 # bytes
|
SIGNATURE_LENGTH: constant(uint256) = 96 # bytes
|
||||||
MAX_DEPOSIT_COUNT: constant(uint256) = 4294967295 # 2**DEPOSIT_CONTRACT_TREE_DEPTH - 1
|
|
||||||
|
|
||||||
Deposit: event({
|
Deposit: event({
|
||||||
pubkey: bytes[48],
|
pubkey: bytes[48],
|
||||||
withdrawal_credentials: bytes[32],
|
withdrawal_credentials: bytes[32],
|
||||||
amount: bytes[8],
|
amount: bytes[8],
|
||||||
signature: bytes[96],
|
signature: bytes[96],
|
||||||
merkle_tree_index: bytes[8],
|
index: bytes[8],
|
||||||
})
|
})
|
||||||
Eth2Genesis: event({deposit_root: bytes32, deposit_count: bytes[8], time: bytes[8]})
|
|
||||||
|
|
||||||
zerohashes: bytes32[DEPOSIT_CONTRACT_TREE_DEPTH]
|
|
||||||
branch: bytes32[DEPOSIT_CONTRACT_TREE_DEPTH]
|
branch: bytes32[DEPOSIT_CONTRACT_TREE_DEPTH]
|
||||||
deposit_count: uint256
|
deposit_count: uint256
|
||||||
full_deposit_count: uint256
|
|
||||||
chainStarted: public(bool)
|
|
||||||
|
|
||||||
|
|
||||||
|
# Compute hashes in empty sparse Merkle tree
|
||||||
|
zero_hashes: bytes32[DEPOSIT_CONTRACT_TREE_DEPTH]
|
||||||
@public
|
@public
|
||||||
def __init__():
|
def __init__():
|
||||||
for i in range(DEPOSIT_CONTRACT_TREE_DEPTH - 1):
|
for i in range(DEPOSIT_CONTRACT_TREE_DEPTH - 1):
|
||||||
self.zerohashes[i+1] = sha256(concat(self.zerohashes[i], self.zerohashes[i]))
|
self.zero_hashes[i + 1] = sha256(concat(self.zero_hashes[i], self.zero_hashes[i]))
|
||||||
|
|
||||||
|
|
||||||
@public
|
@private
|
||||||
@constant
|
@constant
|
||||||
def to_little_endian_64(value: uint256) -> bytes[8]:
|
def to_little_endian_64(value: uint256) -> bytes[8]:
|
||||||
assert value <= MAX_64_BIT_VALUE
|
# Reversing bytes using bitwise uint256 manipulations
|
||||||
|
# Note: array accesses of bytes[] are not currently supported in Vyper
|
||||||
# array access for bytes[] not currently supported in vyper so
|
# Note: this function is only called when `value < 2**64`
|
||||||
# reversing bytes using bitwise uint256 manipulations
|
|
||||||
y: uint256 = 0
|
y: uint256 = 0
|
||||||
x: uint256 = value
|
x: uint256 = value
|
||||||
for i in range(8):
|
for _ in range(8):
|
||||||
y = shift(y, 8)
|
y = shift(y, 8)
|
||||||
y = y + bitwise_and(x, 255)
|
y = y + bitwise_and(x, 255)
|
||||||
x = shift(x, -8)
|
x = shift(x, -8)
|
||||||
|
|
||||||
return slice(convert(y, bytes32), start=24, len=8)
|
return slice(convert(y, bytes32), start=24, len=8)
|
||||||
|
|
||||||
|
|
||||||
@public
|
@public
|
||||||
@constant
|
@constant
|
||||||
def get_deposit_root() -> bytes32:
|
def get_deposit_root() -> bytes32:
|
||||||
root: bytes32 = 0x0000000000000000000000000000000000000000000000000000000000000000
|
node: bytes32 = 0x0000000000000000000000000000000000000000000000000000000000000000
|
||||||
size: uint256 = self.deposit_count
|
size: uint256 = self.deposit_count
|
||||||
for h in range(DEPOSIT_CONTRACT_TREE_DEPTH):
|
for height in range(DEPOSIT_CONTRACT_TREE_DEPTH):
|
||||||
if bitwise_and(size, 1) == 1:
|
if bitwise_and(size, 1) == 1: # More gas efficient than `size % 2 == 1`
|
||||||
root = sha256(concat(self.branch[h], root))
|
node = sha256(concat(self.branch[height], node))
|
||||||
else:
|
else:
|
||||||
root = sha256(concat(root, self.zerohashes[h]))
|
node = sha256(concat(node, self.zero_hashes[height]))
|
||||||
size /= 2
|
size /= 2
|
||||||
return root
|
return node
|
||||||
|
|
||||||
|
|
||||||
@public
|
@public
|
||||||
@constant
|
@constant
|
||||||
def get_deposit_count() -> bytes[8]:
|
def get_deposit_count() -> bytes[8]:
|
||||||
return self.to_little_endian_64(self.deposit_count)
|
return self.to_little_endian_64(self.deposit_count)
|
||||||
|
|
||||||
|
|
||||||
@payable
|
@payable
|
||||||
@public
|
@public
|
||||||
def deposit(pubkey: bytes[PUBKEY_LENGTH],
|
def deposit(pubkey: bytes[PUBKEY_LENGTH],
|
||||||
withdrawal_credentials: bytes[WITHDRAWAL_CREDENTIALS_LENGTH],
|
withdrawal_credentials: bytes[WITHDRAWAL_CREDENTIALS_LENGTH],
|
||||||
signature: bytes[SIGNATURE_LENGTH]):
|
signature: bytes[SIGNATURE_LENGTH]):
|
||||||
# Prevent edge case in computing `self.branch` when `self.deposit_count == MAX_DEPOSIT_COUNT`
|
# Avoid overflowing the Merkle tree (and prevent edge case in computing `self.branch`)
|
||||||
# NOTE: reaching this point with the constants as currently defined is impossible due to the
|
|
||||||
# uni-directional nature of transfers from eth1 to eth2 and the total ether supply (< 130M).
|
|
||||||
assert self.deposit_count < MAX_DEPOSIT_COUNT
|
assert self.deposit_count < MAX_DEPOSIT_COUNT
|
||||||
|
|
||||||
|
# Validate deposit data
|
||||||
|
deposit_amount: uint256 = msg.value / as_wei_value(1, "gwei")
|
||||||
|
assert deposit_amount >= MIN_DEPOSIT_AMOUNT
|
||||||
assert len(pubkey) == PUBKEY_LENGTH
|
assert len(pubkey) == PUBKEY_LENGTH
|
||||||
assert len(withdrawal_credentials) == WITHDRAWAL_CREDENTIALS_LENGTH
|
assert len(withdrawal_credentials) == WITHDRAWAL_CREDENTIALS_LENGTH
|
||||||
assert len(signature) == SIGNATURE_LENGTH
|
assert len(signature) == SIGNATURE_LENGTH
|
||||||
|
|
||||||
deposit_amount: uint256 = msg.value / as_wei_value(1, "gwei")
|
# Emit `Deposit` log
|
||||||
assert deposit_amount >= MIN_DEPOSIT_AMOUNT
|
|
||||||
amount: bytes[8] = self.to_little_endian_64(deposit_amount)
|
amount: bytes[8] = self.to_little_endian_64(deposit_amount)
|
||||||
|
log.Deposit(pubkey, withdrawal_credentials, amount, signature, self.to_little_endian_64(self.deposit_count))
|
||||||
|
|
||||||
index: uint256 = self.deposit_count
|
# Compute `DepositData` root
|
||||||
|
zero_bytes32: bytes32 = 0x0000000000000000000000000000000000000000000000000000000000000000
|
||||||
# add deposit to merkle tree
|
pubkey_root: bytes32 = sha256(concat(pubkey, slice(zero_bytes32, start=0, len=64 - PUBKEY_LENGTH)))
|
||||||
i: int128 = 0
|
|
||||||
size: uint256 = index + 1
|
|
||||||
for _ in range(DEPOSIT_CONTRACT_TREE_DEPTH):
|
|
||||||
if bitwise_and(size, 1) == 1:
|
|
||||||
break
|
|
||||||
i += 1
|
|
||||||
size /= 2
|
|
||||||
|
|
||||||
zero_bytes_32: bytes32
|
|
||||||
pubkey_root: bytes32 = sha256(concat(pubkey, slice(zero_bytes_32, start=0, len=16)))
|
|
||||||
signature_root: bytes32 = sha256(concat(
|
signature_root: bytes32 = sha256(concat(
|
||||||
sha256(slice(signature, start=0, len=64)),
|
sha256(slice(signature, start=0, len=64)),
|
||||||
sha256(concat(slice(signature, start=64, len=32), zero_bytes_32))
|
sha256(concat(slice(signature, start=64, len=SIGNATURE_LENGTH - 64), zero_bytes32)),
|
||||||
))
|
))
|
||||||
value: bytes32 = sha256(concat(
|
node: bytes32 = sha256(concat(
|
||||||
sha256(concat(pubkey_root, withdrawal_credentials)),
|
sha256(concat(pubkey_root, withdrawal_credentials)),
|
||||||
sha256(concat(
|
sha256(concat(amount, slice(zero_bytes32, start=0, len=32 - AMOUNT_LENGTH), signature_root)),
|
||||||
amount,
|
|
||||||
slice(zero_bytes_32, start=0, len=24),
|
|
||||||
signature_root,
|
|
||||||
))
|
|
||||||
))
|
))
|
||||||
for j in range(DEPOSIT_CONTRACT_TREE_DEPTH):
|
|
||||||
if j < i:
|
|
||||||
value = sha256(concat(self.branch[j], value))
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
self.branch[i] = value
|
|
||||||
|
|
||||||
|
# Add `DepositData` root to Merkle tree (update a single `branch` node)
|
||||||
self.deposit_count += 1
|
self.deposit_count += 1
|
||||||
log.Deposit(
|
size: uint256 = self.deposit_count
|
||||||
pubkey,
|
for height in range(DEPOSIT_CONTRACT_TREE_DEPTH):
|
||||||
withdrawal_credentials,
|
if bitwise_and(size, 1) == 1: # More gas efficient than `size % 2 == 1`
|
||||||
amount,
|
self.branch[height] = node
|
||||||
signature,
|
break
|
||||||
self.to_little_endian_64(index),
|
node = sha256(concat(self.branch[height], node))
|
||||||
)
|
size /= 2
|
||||||
|
|
||||||
if deposit_amount >= FULL_DEPOSIT_AMOUNT:
|
|
||||||
self.full_deposit_count += 1
|
|
||||||
if self.full_deposit_count == CHAIN_START_FULL_DEPOSIT_THRESHOLD:
|
|
||||||
timestamp_day_boundary: uint256 = (
|
|
||||||
as_unitless_number(block.timestamp) -
|
|
||||||
as_unitless_number(block.timestamp) % SECONDS_PER_DAY +
|
|
||||||
2 * SECONDS_PER_DAY
|
|
||||||
)
|
|
||||||
new_deposit_root: bytes32 = self.get_deposit_root()
|
|
||||||
log.Eth2Genesis(new_deposit_root,
|
|
||||||
self.to_little_endian_64(self.deposit_count),
|
|
||||||
self.to_little_endian_64(timestamp_day_boundary))
|
|
||||||
self.chainStarted = True
|
|
||||||
|
|
|
@ -26,7 +26,6 @@ from .utils import (
|
||||||
# Constants
|
# Constants
|
||||||
MIN_DEPOSIT_AMOUNT = 1000000000 # Gwei
|
MIN_DEPOSIT_AMOUNT = 1000000000 # Gwei
|
||||||
FULL_DEPOSIT_AMOUNT = 32000000000 # Gwei
|
FULL_DEPOSIT_AMOUNT = 32000000000 # Gwei
|
||||||
CHAIN_START_FULL_DEPOSIT_THRESHOLD = 65536 # 2**16
|
|
||||||
DEPOSIT_CONTRACT_TREE_DEPTH = 32
|
DEPOSIT_CONTRACT_TREE_DEPTH = 32
|
||||||
TWO_TO_POWER_OF_TREE_DEPTH = 2**DEPOSIT_CONTRACT_TREE_DEPTH
|
TWO_TO_POWER_OF_TREE_DEPTH = 2**DEPOSIT_CONTRACT_TREE_DEPTH
|
||||||
|
|
||||||
|
@ -63,45 +62,6 @@ def registration_contract(w3, tester):
|
||||||
return registration_deployed
|
return registration_deployed
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
|
||||||
def chain_start_full_deposit_thresholds():
|
|
||||||
return [randint(1, 5), randint(6, 10), randint(11, 15)]
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=[0, 1, 2])
|
|
||||||
def modified_registration_contract(
|
|
||||||
request,
|
|
||||||
w3,
|
|
||||||
tester,
|
|
||||||
chain_start_full_deposit_thresholds):
|
|
||||||
# Set CHAIN_START_FULL_DEPOSIT_THRESHOLD to different threshold t
|
|
||||||
registration_code = get_deposit_contract_code()
|
|
||||||
t = str(chain_start_full_deposit_thresholds[request.param])
|
|
||||||
modified_registration_code = re.sub(
|
|
||||||
r'CHAIN_START_FULL_DEPOSIT_THRESHOLD: constant\(uint256\) = [0-9]+',
|
|
||||||
'CHAIN_START_FULL_DEPOSIT_THRESHOLD: constant(uint256) = ' + t,
|
|
||||||
registration_code,
|
|
||||||
)
|
|
||||||
assert modified_registration_code != registration_code
|
|
||||||
contract_bytecode = compiler.compile_code(modified_registration_code)['bytecode']
|
|
||||||
contract_abi = compiler.mk_full_signature(modified_registration_code)
|
|
||||||
registration = w3.eth.contract(
|
|
||||||
abi=contract_abi,
|
|
||||||
bytecode=contract_bytecode)
|
|
||||||
tx_hash = registration.constructor().transact()
|
|
||||||
tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
|
|
||||||
registration_deployed = w3.eth.contract(
|
|
||||||
address=tx_receipt.contractAddress,
|
|
||||||
abi=contract_abi
|
|
||||||
)
|
|
||||||
setattr(
|
|
||||||
registration_deployed,
|
|
||||||
'chain_start_full_deposit_threshold',
|
|
||||||
chain_start_full_deposit_thresholds[request.param]
|
|
||||||
)
|
|
||||||
return registration_deployed
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def assert_tx_failed(tester):
|
def assert_tx_failed(tester):
|
||||||
def assert_tx_failed(function_to_test, exception=eth_tester.exceptions.TransactionFailed):
|
def assert_tx_failed(function_to_test, exception=eth_tester.exceptions.TransactionFailed):
|
||||||
|
|
|
@ -49,28 +49,6 @@ def deposit_input():
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
'value,success',
|
|
||||||
[
|
|
||||||
(0, True),
|
|
||||||
(10, True),
|
|
||||||
(55555, True),
|
|
||||||
(2**64 - 1, True),
|
|
||||||
(2**64, False),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
def test_to_little_endian_64(registration_contract, value, success, assert_tx_failed):
|
|
||||||
call = registration_contract.functions.to_little_endian_64(value)
|
|
||||||
|
|
||||||
if success:
|
|
||||||
little_endian_64 = call.call()
|
|
||||||
assert little_endian_64 == (value).to_bytes(8, 'little')
|
|
||||||
else:
|
|
||||||
assert_tx_failed(
|
|
||||||
lambda: call.call()
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'success,deposit_amount',
|
'success,deposit_amount',
|
||||||
[
|
[
|
||||||
|
@ -151,8 +129,7 @@ def test_deposit_log(registration_contract, a0, w3, deposit_input):
|
||||||
assert log['withdrawal_credentials'] == deposit_input[1]
|
assert log['withdrawal_credentials'] == deposit_input[1]
|
||||||
assert log['amount'] == deposit_amount_list[i].to_bytes(8, 'little')
|
assert log['amount'] == deposit_amount_list[i].to_bytes(8, 'little')
|
||||||
assert log['signature'] == deposit_input[2]
|
assert log['signature'] == deposit_input[2]
|
||||||
assert log['merkle_tree_index'] == i.to_bytes(8, 'little')
|
assert log['index'] == i.to_bytes(8, 'little')
|
||||||
|
|
||||||
|
|
||||||
def test_deposit_tree(registration_contract, w3, assert_tx_failed, deposit_input):
|
def test_deposit_tree(registration_contract, w3, assert_tx_failed, deposit_input):
|
||||||
log_filter = registration_contract.events.Deposit.createFilter(
|
log_filter = registration_contract.events.Deposit.createFilter(
|
||||||
|
@ -172,7 +149,7 @@ def test_deposit_tree(registration_contract, w3, assert_tx_failed, deposit_input
|
||||||
assert len(logs) == 1
|
assert len(logs) == 1
|
||||||
log = logs[0]['args']
|
log = logs[0]['args']
|
||||||
|
|
||||||
assert log["merkle_tree_index"] == i.to_bytes(8, 'little')
|
assert log["index"] == i.to_bytes(8, 'little')
|
||||||
|
|
||||||
deposit_data = DepositData(
|
deposit_data = DepositData(
|
||||||
pubkey=deposit_input[0],
|
pubkey=deposit_input[0],
|
||||||
|
@ -184,53 +161,3 @@ def test_deposit_tree(registration_contract, w3, assert_tx_failed, deposit_input
|
||||||
leaf_nodes.append(hash_tree_root_result)
|
leaf_nodes.append(hash_tree_root_result)
|
||||||
root = compute_merkle_root(leaf_nodes)
|
root = compute_merkle_root(leaf_nodes)
|
||||||
assert root == registration_contract.functions.get_deposit_root().call()
|
assert root == registration_contract.functions.get_deposit_root().call()
|
||||||
|
|
||||||
|
|
||||||
def test_chain_start(modified_registration_contract, w3, assert_tx_failed, deposit_input):
|
|
||||||
t = getattr(modified_registration_contract, 'chain_start_full_deposit_threshold')
|
|
||||||
# CHAIN_START_FULL_DEPOSIT_THRESHOLD is set to t
|
|
||||||
min_deposit_amount = MIN_DEPOSIT_AMOUNT * eth_utils.denoms.gwei # in wei
|
|
||||||
full_deposit_amount = FULL_DEPOSIT_AMOUNT * eth_utils.denoms.gwei
|
|
||||||
log_filter = modified_registration_contract.events.Eth2Genesis.createFilter(
|
|
||||||
fromBlock='latest',
|
|
||||||
)
|
|
||||||
|
|
||||||
index_not_full_deposit = randint(0, t - 1)
|
|
||||||
for i in range(t):
|
|
||||||
if i == index_not_full_deposit:
|
|
||||||
# Deposit with value below FULL_DEPOSIT_AMOUNT
|
|
||||||
modified_registration_contract.functions.deposit(
|
|
||||||
*deposit_input,
|
|
||||||
).transact({"value": min_deposit_amount})
|
|
||||||
logs = log_filter.get_new_entries()
|
|
||||||
# Eth2Genesis event should not be triggered
|
|
||||||
assert len(logs) == 0
|
|
||||||
else:
|
|
||||||
# Deposit with value FULL_DEPOSIT_AMOUNT
|
|
||||||
modified_registration_contract.functions.deposit(
|
|
||||||
*deposit_input,
|
|
||||||
).transact({"value": full_deposit_amount})
|
|
||||||
logs = log_filter.get_new_entries()
|
|
||||||
# Eth2Genesis event should not be triggered
|
|
||||||
assert len(logs) == 0
|
|
||||||
|
|
||||||
# Make 1 more deposit with value FULL_DEPOSIT_AMOUNT to trigger Eth2Genesis event
|
|
||||||
modified_registration_contract.functions.deposit(
|
|
||||||
*deposit_input,
|
|
||||||
).transact({"value": full_deposit_amount})
|
|
||||||
logs = log_filter.get_new_entries()
|
|
||||||
assert len(logs) == 1
|
|
||||||
timestamp = int(w3.eth.getBlock(w3.eth.blockNumber)['timestamp'])
|
|
||||||
timestamp_day_boundary = timestamp + (86400 - timestamp % 86400) + 86400
|
|
||||||
log = logs[0]['args']
|
|
||||||
assert log['deposit_root'] == modified_registration_contract.functions.get_deposit_root().call()
|
|
||||||
assert int.from_bytes(log['time'], byteorder='little') == timestamp_day_boundary
|
|
||||||
assert modified_registration_contract.functions.chainStarted().call() is True
|
|
||||||
|
|
||||||
# Make 1 deposit with value FULL_DEPOSIT_AMOUNT and
|
|
||||||
# check that Eth2Genesis event is not triggered
|
|
||||||
modified_registration_contract.functions.deposit(
|
|
||||||
*deposit_input,
|
|
||||||
).transact({"value": full_deposit_amount})
|
|
||||||
logs = log_filter.get_new_entries()
|
|
||||||
assert len(logs) == 0
|
|
||||||
|
|
|
@ -95,7 +95,7 @@
|
||||||
- [`initiate_validator_exit`](#initiate_validator_exit)
|
- [`initiate_validator_exit`](#initiate_validator_exit)
|
||||||
- [`slash_validator`](#slash_validator)
|
- [`slash_validator`](#slash_validator)
|
||||||
- [Genesis](#genesis)
|
- [Genesis](#genesis)
|
||||||
- [`Eth2Genesis`](#eth2genesis)
|
- [Genesis trigger](#genesis-trigger)
|
||||||
- [Genesis state](#genesis-state)
|
- [Genesis state](#genesis-state)
|
||||||
- [Genesis block](#genesis-block)
|
- [Genesis block](#genesis-block)
|
||||||
- [Beacon chain state transition function](#beacon-chain-state-transition-function)
|
- [Beacon chain state transition function](#beacon-chain-state-transition-function)
|
||||||
|
@ -941,6 +941,7 @@ def convert_to_indexed(state: BeaconState, attestation: Attestation) -> IndexedA
|
||||||
"""
|
"""
|
||||||
attesting_indices = get_attesting_indices(state, attestation.data, attestation.aggregation_bitfield)
|
attesting_indices = get_attesting_indices(state, attestation.data, attestation.aggregation_bitfield)
|
||||||
custody_bit_1_indices = get_attesting_indices(state, attestation.data, attestation.custody_bitfield)
|
custody_bit_1_indices = get_attesting_indices(state, attestation.data, attestation.custody_bitfield)
|
||||||
|
assert set(custody_bit_1_indices).issubset(attesting_indices)
|
||||||
custody_bit_0_indices = [index for index in attesting_indices if index not in custody_bit_1_indices]
|
custody_bit_0_indices = [index for index in attesting_indices if index not in custody_bit_1_indices]
|
||||||
|
|
||||||
return IndexedAttestation(
|
return IndexedAttestation(
|
||||||
|
@ -1106,20 +1107,45 @@ def slash_validator(state: BeaconState,
|
||||||
|
|
||||||
## Genesis
|
## Genesis
|
||||||
|
|
||||||
### `Eth2Genesis`
|
### Genesis trigger
|
||||||
|
|
||||||
When enough deposits of size `MAX_EFFECTIVE_BALANCE` have been made to the deposit contract an `Eth2Genesis` log is emitted triggering the genesis of the beacon chain. Let:
|
Before genesis has been triggered and whenever the deposit contract emits a `Deposit` log, call the function `is_genesis_trigger(deposits: List[Deposit], timestamp: uint64) -> bool` where:
|
||||||
|
|
||||||
* `eth2genesis` be the object corresponding to `Eth2Genesis`
|
* `deposits` is the list of all deposits, ordered chronologically, up to and including the deposit triggering the latest `Deposit` log
|
||||||
* `genesis_eth1_data` be object of type `Eth1Data` where
|
* `timestamp` is the Unix timestamp in the Ethereum 1.0 block that emitted the latest `Deposit` log
|
||||||
* `genesis_eth1_data.deposit_root = eth2genesis.deposit_root`
|
|
||||||
* `genesis_eth1_data.deposit_count = eth2genesis.deposit_count`
|
When `is_genesis_trigger(deposits, timestamp) is True` for the first time let:
|
||||||
* `genesis_eth1_data.block_hash` is the hash of the Ethereum 1.0 block that emitted the `Eth2Genesis` log
|
|
||||||
* `genesis_deposits` be the object of type `List[Deposit]` with deposits ordered chronologically up to and including the deposit that triggered the `Eth2Genesis` log
|
* `genesis_deposits = deposits`
|
||||||
|
* `genesis_time = timestamp - timestamp % SECONDS_PER_DAY + 2 * SECONDS_PER_DAY` where `SECONDS_PER_DAY = 86400`
|
||||||
|
* `genesis_eth1_data` be the object of type `Eth1Data` where:
|
||||||
|
* `genesis_eth1_data.block_hash` is the Ethereum 1.0 block hash that emitted the log for the last deposit in `deposits`
|
||||||
|
* `genesis_eth1_data.deposit_root` is the deposit root for the last deposit in `deposits`
|
||||||
|
* `genesis_eth1_data.deposit_count = len(genesis_deposits)`
|
||||||
|
|
||||||
|
*Note*: The function `is_genesis_trigger` has yet to be agreed by the community, and can be updated as necessary. We define the following testing placeholder:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def is_genesis_trigger(deposits: List[Deposit], timestamp: uint64) -> bool:
|
||||||
|
# Process deposits
|
||||||
|
state = BeaconState()
|
||||||
|
for deposit in deposits:
|
||||||
|
process_deposit(state, deposit)
|
||||||
|
|
||||||
|
# Count active validators at genesis
|
||||||
|
active_validator_count = 0
|
||||||
|
for validator in state.validator_registry:
|
||||||
|
if validator.effective_balance == MAX_EFFECTIVE_BALANCE:
|
||||||
|
active_validator_count += 1
|
||||||
|
|
||||||
|
# Check effective balance to trigger genesis
|
||||||
|
GENESIS_ACTIVE_VALIDATOR_COUNT = 2**16
|
||||||
|
return active_validator_count == GENESIS_ACTIVE_VALIDATOR_COUNT
|
||||||
|
```
|
||||||
|
|
||||||
### Genesis state
|
### Genesis state
|
||||||
|
|
||||||
Let `genesis_state = get_genesis_beacon_state(genesis_deposits, eth2genesis.genesis_time, genesis_eth1_data)`.
|
Let `genesis_state = get_genesis_beacon_state(genesis_deposits, genesis_time, genesis_eth1_data)`.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def get_genesis_beacon_state(deposits: List[Deposit], genesis_time: int, genesis_eth1_data: Eth1Data) -> BeaconState:
|
def get_genesis_beacon_state(deposits: List[Deposit], genesis_time: int, genesis_eth1_data: Eth1Data) -> BeaconState:
|
||||||
|
@ -1196,7 +1222,7 @@ def process_slot(state: BeaconState) -> None:
|
||||||
|
|
||||||
### Epoch processing
|
### Epoch processing
|
||||||
|
|
||||||
Note: the `# @LabelHere` lines below are placeholders to show that code will be inserted here in a future phase.
|
*Note*: the `# @LabelHere` lines below are placeholders to show that code will be inserted here in a future phase.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def process_epoch(state: BeaconState) -> None:
|
def process_epoch(state: BeaconState) -> None:
|
||||||
|
@ -1647,6 +1673,10 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
|
||||||
Process ``Attestation`` operation.
|
Process ``Attestation`` operation.
|
||||||
"""
|
"""
|
||||||
data = attestation.data
|
data = attestation.data
|
||||||
|
|
||||||
|
assert data.crosslink.shard < SHARD_COUNT
|
||||||
|
assert data.target_epoch in (get_previous_epoch(state), get_current_epoch(state))
|
||||||
|
|
||||||
attestation_slot = get_attestation_data_slot(state, data)
|
attestation_slot = get_attestation_data_slot(state, data)
|
||||||
assert attestation_slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= attestation_slot + SLOTS_PER_EPOCH
|
assert attestation_slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= attestation_slot + SLOTS_PER_EPOCH
|
||||||
|
|
||||||
|
@ -1657,7 +1687,6 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
|
||||||
proposer_index=get_beacon_proposer_index(state),
|
proposer_index=get_beacon_proposer_index(state),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert data.target_epoch in (get_previous_epoch(state), get_current_epoch(state))
|
|
||||||
if data.target_epoch == get_current_epoch(state):
|
if data.target_epoch == get_current_epoch(state):
|
||||||
ffg_data = (state.current_justified_epoch, state.current_justified_root, get_current_epoch(state))
|
ffg_data = (state.current_justified_epoch, state.current_justified_root, get_current_epoch(state))
|
||||||
parent_crosslink = state.current_crosslinks[data.crosslink.shard]
|
parent_crosslink = state.current_crosslinks[data.crosslink.shard]
|
||||||
|
|
|
@ -9,15 +9,12 @@
|
||||||
- [Table of contents](#table-of-contents)
|
- [Table of contents](#table-of-contents)
|
||||||
- [Introduction](#introduction)
|
- [Introduction](#introduction)
|
||||||
- [Constants](#constants)
|
- [Constants](#constants)
|
||||||
- [Gwei values](#gwei-values)
|
|
||||||
- [Contract](#contract)
|
- [Contract](#contract)
|
||||||
- [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract)
|
- [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract)
|
||||||
- [Arguments](#arguments)
|
- [`deposit` function](#deposit-function)
|
||||||
|
- [Deposit amount](#deposit-amount)
|
||||||
- [Withdrawal credentials](#withdrawal-credentials)
|
- [Withdrawal credentials](#withdrawal-credentials)
|
||||||
- [Amount](#amount)
|
- [`Deposit` log](#deposit-log)
|
||||||
- [Event logs](#event-logs)
|
|
||||||
- [`Deposit` logs](#deposit-logs)
|
|
||||||
- [`Eth2Genesis` log](#eth2genesis-log)
|
|
||||||
- [Vyper code](#vyper-code)
|
- [Vyper code](#vyper-code)
|
||||||
|
|
||||||
<!-- /TOC -->
|
<!-- /TOC -->
|
||||||
|
@ -28,66 +25,40 @@ This document represents the specification for the beacon chain deposit contract
|
||||||
|
|
||||||
## Constants
|
## Constants
|
||||||
|
|
||||||
### Gwei values
|
|
||||||
|
|
||||||
| Name | Value | Unit |
|
|
||||||
| - | - | - |
|
|
||||||
| `FULL_DEPOSIT_AMOUNT` | `32 * 10**9` | Gwei |
|
|
||||||
|
|
||||||
### Contract
|
### Contract
|
||||||
|
|
||||||
| Name | Value |
|
| Name | Value |
|
||||||
| - | - |
|
| - | - |
|
||||||
| `DEPOSIT_CONTRACT_ADDRESS` | **TBD** |
|
| `DEPOSIT_CONTRACT_ADDRESS` | **TBD** |
|
||||||
| `DEPOSIT_CONTRACT_TREE_DEPTH` | `2**5` (= 32) |
|
| `DEPOSIT_CONTRACT_TREE_DEPTH` | `2**5` (= 32) |
|
||||||
| `CHAIN_START_FULL_DEPOSIT_THRESHOLD` | `2**16` (= 65,536) |
|
|
||||||
|
|
||||||
## Ethereum 1.0 deposit contract
|
## Ethereum 1.0 deposit contract
|
||||||
|
|
||||||
The initial deployment phases of Ethereum 2.0 are implemented without consensus changes to Ethereum 1.0. A deposit contract at address `DEPOSIT_CONTRACT_ADDRESS` is added to Ethereum 1.0 for deposits of ETH to the beacon chain. Validator balances will be withdrawable to the shards in Phase 2 (i.e. when the EVM 2.0 is deployed and the shards have state).
|
The initial deployment phases of Ethereum 2.0 are implemented without consensus changes to Ethereum 1.0. A deposit contract at address `DEPOSIT_CONTRACT_ADDRESS` is added to Ethereum 1.0 for deposits of ETH to the beacon chain. Validator balances will be withdrawable to the shards in Phase 2 (i.e. when the EVM 2.0 is deployed and the shards have state).
|
||||||
|
|
||||||
### Arguments
|
### `deposit` function
|
||||||
|
|
||||||
The deposit contract has a `deposit` function which takes the amount in Ethereum 1.0 transaction, and arguments `pubkey: bytes[48], withdrawal_credentials: bytes[32], signature: bytes[96]` corresponding to `DepositData`.
|
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]` corresponding to a `DepositData` object.
|
||||||
|
|
||||||
|
#### Deposit amount
|
||||||
|
|
||||||
|
The amount of ETH (rounded down to the closest Gwei) sent to the deposit contract is the deposit amount, which must be of size at least `MIN_DEPOSIT_AMOUNT` Gwei. Note that ETH consumed by the deposit contract is no longer usable on Ethereum 1.0.
|
||||||
|
|
||||||
#### Withdrawal credentials
|
#### Withdrawal credentials
|
||||||
|
|
||||||
One of the `DepositData` fields is `withdrawal_credentials`. It is a commitment to credentials for withdrawals to shards. The first byte of `withdrawal_credentials` is a version number. As of now, the only expected format is as follows:
|
One of the `DepositData` fields is `withdrawal_credentials`. It is a commitment to credentials for withdrawing validator balance (e.g. to another validator, or to shards). The first byte of `withdrawal_credentials` is a version number. As of now, the only expected format is as follows:
|
||||||
|
|
||||||
* `withdrawal_credentials[:1] == BLS_WITHDRAWAL_PREFIX_BYTE`
|
* `withdrawal_credentials[:1] == BLS_WITHDRAWAL_PREFIX_BYTE`
|
||||||
* `withdrawal_credentials[1:] == hash(withdrawal_pubkey)[1:]` where `withdrawal_pubkey` is a BLS pubkey
|
* `withdrawal_credentials[1:] == hash(withdrawal_pubkey)[1:]` where `withdrawal_pubkey` is a BLS pubkey
|
||||||
|
|
||||||
The private key corresponding to `withdrawal_pubkey` will be required to initiate a withdrawal. It can be stored separately until a withdrawal is required, e.g. in cold storage.
|
The private key corresponding to `withdrawal_pubkey` will be required to initiate a withdrawal. It can be stored separately until a withdrawal is required, e.g. in cold storage.
|
||||||
|
|
||||||
#### Amount
|
#### `Deposit` log
|
||||||
|
|
||||||
* A valid deposit amount should be at least `MIN_DEPOSIT_AMOUNT` in Gwei.
|
Every Ethereum 1.0 deposit emits a `Deposit` 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.
|
||||||
* A deposit with an amount greater than or equal to `FULL_DEPOSIT_AMOUNT` in Gwei is considered as a full deposit.
|
|
||||||
|
|
||||||
## Event logs
|
|
||||||
|
|
||||||
### `Deposit` logs
|
|
||||||
|
|
||||||
Every Ethereum 1.0 deposit, of size at least `MIN_DEPOSIT_AMOUNT`, emits a `Deposit` 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.
|
|
||||||
|
|
||||||
### `Eth2Genesis` log
|
|
||||||
|
|
||||||
When `CHAIN_START_FULL_DEPOSIT_THRESHOLD` of full deposits have been made, the deposit contract emits the `Eth2Genesis` log. The beacon chain state may then be initialized by calling the `get_genesis_beacon_state` function (defined [here](./0_beacon-chain.md#genesis-state)) where:
|
|
||||||
|
|
||||||
* `genesis_time` equals `time` in the `Eth2Genesis` log
|
|
||||||
* `eth1_data.deposit_root` equals `deposit_root` in the `Eth2Genesis` log
|
|
||||||
* `eth1_data.deposit_count` equals `deposit_count` in the `Eth2Genesis` log
|
|
||||||
* `eth1_data.block_hash` equals the hash of the block that included the log
|
|
||||||
* `genesis_validator_deposits` is a list of `Deposit` objects built according to the `Deposit` logs up to the deposit that triggered the `Eth2Genesis` log, processed in the order in which they were emitted (oldest to newest)
|
|
||||||
|
|
||||||
## Vyper code
|
## Vyper code
|
||||||
|
|
||||||
The source for the Vyper contract lives [here](./../../deposit_contract/contracts/validator_registration.v.py).
|
The deposit contract source code, written in Vyper, is available [here](https://github.com/ethereum/eth2.0-specs/blob/dev/deposit_contract/contracts/validator_registration.v.py).
|
||||||
|
|
||||||
*Note*: To save ~10x on gas, this contract uses a somewhat unintuitive progressive Merkle root calculation algo that requires only O(log(n)) storage. See https://github.com/ethereum/research/blob/master/beacon_chain_impl/progressive_merkle_tree.py for an implementation of the same algo in Python tested for correctness.
|
*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.
|
||||||
|
|
||||||
For convenience, we provide the interface to the contract here:
|
|
||||||
|
|
||||||
* `__init__()`: initializes the contract
|
|
||||||
* `get_deposit_root() -> bytes32`: returns the current root of the deposit tree
|
|
||||||
* `deposit(pubkey: bytes[48], withdrawal_credentials: bytes[32], signature: bytes[96])`: adds a deposit instance to the deposit tree, incorporating the input arguments and the value transferred in the given call. *Note*: The amount of value transferred *must* be at least `MIN_DEPOSIT_AMOUNT`. Each of these constants are specified in units of Gwei.
|
|
||||||
|
|
|
@ -147,7 +147,7 @@ def get_committee_assignment(
|
||||||
|
|
||||||
committees_per_slot = get_epoch_committee_count(state, epoch) // SLOTS_PER_EPOCH
|
committees_per_slot = get_epoch_committee_count(state, epoch) // SLOTS_PER_EPOCH
|
||||||
epoch_start_slot = get_epoch_start_slot(epoch)
|
epoch_start_slot = get_epoch_start_slot(epoch)
|
||||||
for slot in range(epoch_start_slot, epoch_start_slot + SLOTS_PER_EPOCH)
|
for slot in range(epoch_start_slot, epoch_start_slot + SLOTS_PER_EPOCH):
|
||||||
offset = committees_per_slot * (slot % SLOTS_PER_EPOCH)
|
offset = committees_per_slot * (slot % SLOTS_PER_EPOCH)
|
||||||
slot_start_shard = (get_epoch_start_shard(state, epoch) + offset) % SHARD_COUNT
|
slot_start_shard = (get_epoch_start_shard(state, epoch) + offset) % SHARD_COUNT
|
||||||
for i in range(committees_per_slot):
|
for i in range(committees_per_slot):
|
||||||
|
@ -297,11 +297,11 @@ Set `attestation_data.beacon_block_root = signing_root(head_block)`.
|
||||||
* Set `attestation_data.source_epoch = head_state.current_justified_epoch`.
|
* Set `attestation_data.source_epoch = head_state.current_justified_epoch`.
|
||||||
* Set `attestation_data.source_root = head_state.current_justified_root`.
|
* Set `attestation_data.source_root = head_state.current_justified_root`.
|
||||||
* Set `attestation_data.target_epoch = get_current_epoch(head_state)`
|
* Set `attestation_data.target_epoch = get_current_epoch(head_state)`
|
||||||
* Set `attestation_data.target_root = signing_root(epoch_boundary_block)` where `epoch_boundary_block` is the block at the most recent epoch boundary.
|
* Set `attestation_data.target_root = epoch_boundary_block_root` where `epoch_boundary_block_root` is the root of block at the most recent epoch boundary.
|
||||||
|
|
||||||
*Note*: `epoch_boundary_block` can be looked up in the state using:
|
*Note*: `epoch_boundary_block_root` can be looked up in the state using:
|
||||||
* Let `epoch_start_slot = get_epoch_start_slot(get_current_epoch(head_state))`.
|
* Let `epoch_start_slot = get_epoch_start_slot(get_current_epoch(head_state))`.
|
||||||
* Let `epoch_boundary_block = head if epoch_start_slot == head_state.slot else get_block_root(state, epoch_start_slot)`.
|
* Let `epoch_boundary_block_root = signing_root(head_block) if epoch_start_slot == head_state.slot else get_block_root(state, epoch_start_slot)`.
|
||||||
|
|
||||||
##### Crosslink vote
|
##### Crosslink vote
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ from typing import Callable, Iterable
|
||||||
|
|
||||||
from eth2spec.phase0 import spec as spec_phase0
|
from eth2spec.phase0 import spec as spec_phase0
|
||||||
from eth2spec.phase1 import spec as spec_phase1
|
from eth2spec.phase1 import spec as spec_phase1
|
||||||
from eth2spec.test.epoch_processing import (
|
from eth2spec.test.phase_0.epoch_processing import (
|
||||||
test_process_crosslinks,
|
test_process_crosslinks,
|
||||||
test_process_registry_updates
|
test_process_registry_updates
|
||||||
)
|
)
|
||||||
|
@ -33,8 +33,10 @@ def create_suite(transition_name: str, config_name: str, get_cases: Callable[[],
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
gen_runner.run_generator("epoch_processing", [
|
gen_runner.run_generator("epoch_processing", [
|
||||||
create_suite('crosslinks', 'minimal', lambda: generate_from_tests(test_process_crosslinks)),
|
create_suite('crosslinks', 'minimal', lambda: generate_from_tests(test_process_crosslinks, 'phase0')),
|
||||||
create_suite('crosslinks', 'mainnet', lambda: generate_from_tests(test_process_crosslinks)),
|
create_suite('crosslinks', 'mainnet', lambda: generate_from_tests(test_process_crosslinks, 'phase0')),
|
||||||
create_suite('registry_updates', 'minimal', lambda: generate_from_tests(test_process_registry_updates)),
|
create_suite('registry_updates', 'minimal',
|
||||||
create_suite('registry_updates', 'mainnet', lambda: generate_from_tests(test_process_registry_updates)),
|
lambda: generate_from_tests(test_process_registry_updates, 'phase0')),
|
||||||
|
create_suite('registry_updates', 'mainnet',
|
||||||
|
lambda: generate_from_tests(test_process_registry_updates, 'phase0')),
|
||||||
])
|
])
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
from typing import Callable, Iterable
|
from typing import Callable, Iterable
|
||||||
|
|
||||||
from eth2spec.test.block_processing import (
|
from eth2spec.test.phase_0.block_processing import (
|
||||||
test_process_attestation,
|
test_process_attestation,
|
||||||
test_process_attester_slashing,
|
test_process_attester_slashing,
|
||||||
test_process_block_header,
|
test_process_block_header,
|
||||||
test_process_deposit,
|
test_process_deposit,
|
||||||
test_process_proposer_slashing,
|
test_process_proposer_slashing,
|
||||||
test_process_transfer,
|
test_process_transfer,
|
||||||
test_process_voluntary_exit
|
test_process_voluntary_exit,
|
||||||
)
|
)
|
||||||
|
|
||||||
from gen_base import gen_runner, gen_suite, gen_typing
|
from gen_base import gen_runner, gen_suite, gen_typing
|
||||||
|
@ -38,18 +38,18 @@ def create_suite(operation_name: str, config_name: str, get_cases: Callable[[],
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
gen_runner.run_generator("operations", [
|
gen_runner.run_generator("operations", [
|
||||||
create_suite('attestation', 'minimal', lambda: generate_from_tests(test_process_attestation)),
|
create_suite('attestation', 'minimal', lambda: generate_from_tests(test_process_attestation, 'phase0')),
|
||||||
create_suite('attestation', 'mainnet', lambda: generate_from_tests(test_process_attestation)),
|
create_suite('attestation', 'mainnet', lambda: generate_from_tests(test_process_attestation, 'phase0')),
|
||||||
create_suite('attester_slashing', 'minimal', lambda: generate_from_tests(test_process_attester_slashing)),
|
create_suite('attester_slashing', 'minimal', lambda: generate_from_tests(test_process_attester_slashing, 'phase0')),
|
||||||
create_suite('attester_slashing', 'mainnet', lambda: generate_from_tests(test_process_attester_slashing)),
|
create_suite('attester_slashing', 'mainnet', lambda: generate_from_tests(test_process_attester_slashing, 'phase0')),
|
||||||
create_suite('block_header', 'minimal', lambda: generate_from_tests(test_process_block_header)),
|
create_suite('block_header', 'minimal', lambda: generate_from_tests(test_process_block_header, 'phase0')),
|
||||||
create_suite('block_header', 'mainnet', lambda: generate_from_tests(test_process_block_header)),
|
create_suite('block_header', 'mainnet', lambda: generate_from_tests(test_process_block_header, 'phase0')),
|
||||||
create_suite('deposit', 'minimal', lambda: generate_from_tests(test_process_deposit)),
|
create_suite('deposit', 'minimal', lambda: generate_from_tests(test_process_deposit, 'phase0')),
|
||||||
create_suite('deposit', 'mainnet', lambda: generate_from_tests(test_process_deposit)),
|
create_suite('deposit', 'mainnet', lambda: generate_from_tests(test_process_deposit, 'phase0')),
|
||||||
create_suite('proposer_slashing', 'minimal', lambda: generate_from_tests(test_process_proposer_slashing)),
|
create_suite('proposer_slashing', 'minimal', lambda: generate_from_tests(test_process_proposer_slashing, 'phase0')),
|
||||||
create_suite('proposer_slashing', 'mainnet', lambda: generate_from_tests(test_process_proposer_slashing)),
|
create_suite('proposer_slashing', 'mainnet', lambda: generate_from_tests(test_process_proposer_slashing, 'phase0')),
|
||||||
create_suite('transfer', 'minimal', lambda: generate_from_tests(test_process_transfer)),
|
create_suite('transfer', 'minimal', lambda: generate_from_tests(test_process_transfer, 'phase0')),
|
||||||
create_suite('transfer', 'mainnet', lambda: generate_from_tests(test_process_transfer)),
|
create_suite('transfer', 'mainnet', lambda: generate_from_tests(test_process_transfer, 'phase0')),
|
||||||
create_suite('voluntary_exit', 'minimal', lambda: generate_from_tests(test_process_voluntary_exit)),
|
create_suite('voluntary_exit', 'minimal', lambda: generate_from_tests(test_process_voluntary_exit, 'phase0')),
|
||||||
create_suite('voluntary_exit', 'mainnet', lambda: generate_from_tests(test_process_voluntary_exit)),
|
create_suite('voluntary_exit', 'mainnet', lambda: generate_from_tests(test_process_voluntary_exit, 'phase0')),
|
||||||
])
|
])
|
||||||
|
|
|
@ -16,7 +16,7 @@ def create_suite(handler_name: str, config_name: str, get_cases: Callable[[], It
|
||||||
spec_phase0.apply_constants_preset(presets)
|
spec_phase0.apply_constants_preset(presets)
|
||||||
spec_phase1.apply_constants_preset(presets)
|
spec_phase1.apply_constants_preset(presets)
|
||||||
|
|
||||||
return ("%sanity_s_%s" % (handler_name, config_name), handler_name, gen_suite.render_suite(
|
return ("sanity_%s_%s" % (handler_name, config_name), handler_name, gen_suite.render_suite(
|
||||||
title="sanity testing",
|
title="sanity testing",
|
||||||
summary="Sanity test suite, %s type, generated from pytests" % handler_name,
|
summary="Sanity test suite, %s type, generated from pytests" % handler_name,
|
||||||
forks_timeline="testing",
|
forks_timeline="testing",
|
||||||
|
@ -30,8 +30,8 @@ def create_suite(handler_name: str, config_name: str, get_cases: Callable[[], It
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
gen_runner.run_generator("sanity", [
|
gen_runner.run_generator("sanity", [
|
||||||
create_suite('blocks', 'minimal', lambda: generate_from_tests(test_blocks)),
|
create_suite('blocks', 'minimal', lambda: generate_from_tests(test_blocks, 'phase0')),
|
||||||
create_suite('blocks', 'mainnet', lambda: generate_from_tests(test_blocks)),
|
create_suite('blocks', 'mainnet', lambda: generate_from_tests(test_blocks, 'phase0')),
|
||||||
create_suite('slots', 'minimal', lambda: generate_from_tests(test_slots)),
|
create_suite('slots', 'minimal', lambda: generate_from_tests(test_slots, 'phase0')),
|
||||||
create_suite('slots', 'mainnet', lambda: generate_from_tests(test_slots)),
|
create_suite('slots', 'mainnet', lambda: generate_from_tests(test_slots, 'phase0')),
|
||||||
])
|
])
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
from eth2spec.phase0 import spec as spec_phase0
|
from eth2spec.phase0 import spec as spec
|
||||||
from eth2spec.phase1 import spec as spec_phase1
|
|
||||||
from eth_utils import (
|
from eth_utils import (
|
||||||
to_dict, to_tuple
|
to_dict, to_tuple
|
||||||
)
|
)
|
||||||
|
@ -8,7 +7,7 @@ from preset_loader import loader
|
||||||
|
|
||||||
|
|
||||||
@to_dict
|
@to_dict
|
||||||
def shuffling_case(seed: spec.Bytes32, count: int):
|
def shuffling_case(seed, count):
|
||||||
yield 'seed', '0x' + seed.hex()
|
yield 'seed', '0x' + seed.hex()
|
||||||
yield 'count', count
|
yield 'count', count
|
||||||
yield 'shuffled', [spec.get_shuffled_index(i, count, seed) for i in range(count)]
|
yield 'shuffled', [spec.get_shuffled_index(i, count, seed) for i in range(count)]
|
||||||
|
@ -23,8 +22,7 @@ def shuffling_test_cases():
|
||||||
|
|
||||||
def mini_shuffling_suite(configs_path: str) -> gen_typing.TestSuiteOutput:
|
def mini_shuffling_suite(configs_path: str) -> gen_typing.TestSuiteOutput:
|
||||||
presets = loader.load_presets(configs_path, 'minimal')
|
presets = loader.load_presets(configs_path, 'minimal')
|
||||||
spec_phase0.apply_constants_preset(presets)
|
spec.apply_constants_preset(presets)
|
||||||
spec_phase1.apply_constants_preset(presets)
|
|
||||||
|
|
||||||
return ("shuffling_minimal", "core", gen_suite.render_suite(
|
return ("shuffling_minimal", "core", gen_suite.render_suite(
|
||||||
title="Swap-or-Not Shuffling tests with minimal config",
|
title="Swap-or-Not Shuffling tests with minimal config",
|
||||||
|
@ -39,8 +37,7 @@ def mini_shuffling_suite(configs_path: str) -> gen_typing.TestSuiteOutput:
|
||||||
|
|
||||||
def full_shuffling_suite(configs_path: str) -> gen_typing.TestSuiteOutput:
|
def full_shuffling_suite(configs_path: str) -> gen_typing.TestSuiteOutput:
|
||||||
presets = loader.load_presets(configs_path, 'mainnet')
|
presets = loader.load_presets(configs_path, 'mainnet')
|
||||||
spec_phase0.apply_constants_preset(presets)
|
spec.apply_constants_preset(presets)
|
||||||
spec_phase1.apply_constants_preset(presets)
|
|
||||||
|
|
||||||
return ("shuffling_full", "core", gen_suite.render_suite(
|
return ("shuffling_full", "core", gen_suite.render_suite(
|
||||||
title="Swap-or-Not Shuffling tests with mainnet config",
|
title="Swap-or-Not Shuffling tests with mainnet config",
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
from random import Random
|
from random import Random
|
||||||
|
|
||||||
|
from inspect import getmembers, isclass
|
||||||
|
|
||||||
from eth2spec.debug import random_value, encode
|
from eth2spec.debug import random_value, encode
|
||||||
from eth2spec.phase0 import spec
|
from eth2spec.phase0 import spec
|
||||||
|
from eth2spec.utils.ssz.ssz_typing import Container
|
||||||
from eth2spec.utils.ssz.ssz_impl import (
|
from eth2spec.utils.ssz.ssz_impl import (
|
||||||
hash_tree_root,
|
hash_tree_root,
|
||||||
signing_root,
|
signing_root,
|
||||||
|
@ -27,17 +30,23 @@ def create_test_case_contents(value, typ):
|
||||||
|
|
||||||
|
|
||||||
@to_dict
|
@to_dict
|
||||||
def create_test_case(rng: Random, name: str, mode: random_value.RandomizationMode, chaos: bool):
|
def create_test_case(rng: Random, name: str, typ, mode: random_value.RandomizationMode, chaos: bool):
|
||||||
typ = spec.get_ssz_type_by_name(name)
|
|
||||||
value = random_value.get_random_ssz_object(rng, typ, MAX_BYTES_LENGTH, MAX_LIST_LENGTH, mode, chaos)
|
value = random_value.get_random_ssz_object(rng, typ, MAX_BYTES_LENGTH, MAX_LIST_LENGTH, mode, chaos)
|
||||||
yield name, create_test_case_contents(value, typ)
|
yield name, create_test_case_contents(value, typ)
|
||||||
|
|
||||||
|
|
||||||
|
def get_spec_ssz_types():
|
||||||
|
return [
|
||||||
|
(name, value) for (name, value) in getmembers(spec, isclass)
|
||||||
|
if issubclass(value, Container) and value != Container # only the subclasses, not the imported base class
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@to_tuple
|
@to_tuple
|
||||||
def ssz_static_cases(rng: Random, mode: random_value.RandomizationMode, chaos: bool, count: int):
|
def ssz_static_cases(rng: Random, mode: random_value.RandomizationMode, chaos: bool, count: int):
|
||||||
for type_name in spec.ssz_types:
|
for (name, ssz_type) in get_spec_ssz_types():
|
||||||
for i in range(count):
|
for i in range(count):
|
||||||
yield create_test_case(rng, type_name, mode, chaos)
|
yield create_test_case(rng, name, ssz_type, mode, chaos)
|
||||||
|
|
||||||
|
|
||||||
def get_ssz_suite(seed: int, config_name: str, mode: random_value.RandomizationMode, chaos: bool, cases_if_random: int):
|
def get_ssz_suite(seed: int, config_name: str, mode: random_value.RandomizationMode, chaos: bool, cases_if_random: int):
|
||||||
|
@ -81,8 +90,6 @@ if __name__ == "__main__":
|
||||||
settings.append((seed, "mainnet", random_value.RandomizationMode.mode_random, False, 5))
|
settings.append((seed, "mainnet", random_value.RandomizationMode.mode_random, False, 5))
|
||||||
seed += 1
|
seed += 1
|
||||||
|
|
||||||
print("Settings: %d, SSZ-types: %d" % (len(settings), len(spec.ssz_types)))
|
|
||||||
|
|
||||||
gen_runner.run_generator("ssz_static", [
|
gen_runner.run_generator("ssz_static", [
|
||||||
get_ssz_suite(seed, config_name, mode, chaos, cases_if_random)
|
get_ssz_suite(seed, config_name, mode, chaos, cases_if_random)
|
||||||
for (seed, config_name, mode, chaos, cases_if_random) in settings
|
for (seed, config_name, mode, chaos, cases_if_random) in settings
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
from inspect import getmembers, isfunction
|
from inspect import getmembers, isfunction
|
||||||
|
|
||||||
def generate_from_tests(src, bls_active=True):
|
def generate_from_tests(src, phase, bls_active=True):
|
||||||
"""
|
"""
|
||||||
Generate a list of test cases by running tests from the given src in generator-mode.
|
Generate a list of test cases by running tests from the given src in generator-mode.
|
||||||
:param src: to retrieve tests from (discovered using inspect.getmembers)
|
:param src: to retrieve tests from (discovered using inspect.getmembers).
|
||||||
|
:param phase: to run tests against particular phase.
|
||||||
:param bls_active: optional, to override BLS switch preference. Defaults to True.
|
:param bls_active: optional, to override BLS switch preference. Defaults to True.
|
||||||
:return: the list of test cases.
|
:return: the list of test cases.
|
||||||
"""
|
"""
|
||||||
|
@ -16,7 +17,7 @@ def generate_from_tests(src, bls_active=True):
|
||||||
for name in fn_names:
|
for name in fn_names:
|
||||||
tfn = getattr(src, name)
|
tfn = getattr(src, name)
|
||||||
try:
|
try:
|
||||||
test_case = tfn(generator_mode=True, bls_active=bls_active)
|
test_case = tfn(generator_mode=True, phase=phase, bls_active=bls_active)
|
||||||
# If no test case data is returned, the test is ignored.
|
# If no test case data is returned, the test is ignored.
|
||||||
if test_case is not None:
|
if test_case is not None:
|
||||||
out.append(test_case)
|
out.append(test_case)
|
||||||
|
|
|
@ -116,12 +116,22 @@ def with_phases(phases):
|
||||||
def decorator(fn):
|
def decorator(fn):
|
||||||
def run_with_spec_version(spec, *args, **kw):
|
def run_with_spec_version(spec, *args, **kw):
|
||||||
kw['spec'] = spec
|
kw['spec'] = spec
|
||||||
fn(*args, **kw)
|
return fn(*args, **kw)
|
||||||
|
|
||||||
def wrapper(*args, **kw):
|
def wrapper(*args, **kw):
|
||||||
if 'phase0' in phases:
|
run_phases = phases
|
||||||
run_with_spec_version(spec_phase0, *args, **kw)
|
|
||||||
if 'phase1' in phases:
|
# limit phases if one explicitly specified
|
||||||
run_with_spec_version(spec_phase1, *args, **kw)
|
if 'phase' in kw:
|
||||||
|
phase = kw.pop('phase')
|
||||||
|
if phase not in phases:
|
||||||
|
return
|
||||||
|
run_phases = [phase]
|
||||||
|
|
||||||
|
if 'phase0' in run_phases:
|
||||||
|
ret = run_with_spec_version(spec_phase0, *args, **kw)
|
||||||
|
if 'phase1' in run_phases:
|
||||||
|
ret = run_with_spec_version(spec_phase1, *args, **kw)
|
||||||
|
return ret
|
||||||
return wrapper
|
return wrapper
|
||||||
return decorator
|
return decorator
|
||||||
|
|
|
@ -67,7 +67,7 @@ def prepare_state_and_deposit(spec, state, validator_index, amount, withdrawal_c
|
||||||
|
|
||||||
# insecurely use pubkey as withdrawal key if no credentials provided
|
# insecurely use pubkey as withdrawal key if no credentials provided
|
||||||
if withdrawal_credentials is None:
|
if withdrawal_credentials is None:
|
||||||
withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX_BYTE + spec.hash(pubkey)[1:]
|
withdrawal_credentials = spec.int_to_bytes(spec.BLS_WITHDRAWAL_PREFIX, length=1) + spec.hash(pubkey)[1:]
|
||||||
|
|
||||||
deposit, root, deposit_data_leaves = build_deposit(
|
deposit, root, deposit_data_leaves = build_deposit(
|
||||||
spec,
|
spec,
|
||||||
|
@ -77,7 +77,7 @@ def prepare_state_and_deposit(spec, state, validator_index, amount, withdrawal_c
|
||||||
privkey,
|
privkey,
|
||||||
amount,
|
amount,
|
||||||
withdrawal_credentials,
|
withdrawal_credentials,
|
||||||
signed
|
signed,
|
||||||
)
|
)
|
||||||
|
|
||||||
state.eth1_data.deposit_root = root
|
state.eth1_data.deposit_root = root
|
||||||
|
|
|
@ -5,7 +5,7 @@ from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
||||||
def build_mock_validator(spec, i: int, balance: int):
|
def build_mock_validator(spec, i: int, balance: int):
|
||||||
pubkey = pubkeys[i]
|
pubkey = pubkeys[i]
|
||||||
# insecurely use pubkey as withdrawal key as well
|
# insecurely use pubkey as withdrawal key as well
|
||||||
withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX_BYTE + spec.hash(pubkey)[1:]
|
withdrawal_credentials = spec.int_to_bytes(spec.BLS_WITHDRAWAL_PREFIX, length=1) + spec.hash(pubkey)[1:]
|
||||||
return spec.Validator(
|
return spec.Validator(
|
||||||
pubkey=pubkeys[i],
|
pubkey=pubkeys[i],
|
||||||
withdrawal_credentials=withdrawal_credentials,
|
withdrawal_credentials=withdrawal_credentials,
|
||||||
|
|
|
@ -32,7 +32,7 @@ def get_valid_transfer(spec, state, slot=None, sender_index=None, amount=None, f
|
||||||
|
|
||||||
# ensure withdrawal_credentials reproducible
|
# ensure withdrawal_credentials reproducible
|
||||||
state.validators[transfer.sender].withdrawal_credentials = (
|
state.validators[transfer.sender].withdrawal_credentials = (
|
||||||
spec.BLS_WITHDRAWAL_PREFIX_BYTE + spec.hash(transfer.pubkey)[1:]
|
spec.int_to_bytes(spec.BLS_WITHDRAWAL_PREFIX, length=1) + spec.hash(transfer.pubkey)[1:]
|
||||||
)
|
)
|
||||||
|
|
||||||
return transfer
|
return transfer
|
||||||
|
|
|
@ -101,7 +101,7 @@ def test_invalid_sig_top_up(spec, state):
|
||||||
def test_invalid_withdrawal_credentials_top_up(spec, state):
|
def test_invalid_withdrawal_credentials_top_up(spec, state):
|
||||||
validator_index = 0
|
validator_index = 0
|
||||||
amount = spec.MAX_EFFECTIVE_BALANCE // 4
|
amount = spec.MAX_EFFECTIVE_BALANCE // 4
|
||||||
withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX_BYTE + spec.hash(b"junk")[1:]
|
withdrawal_credentials = spec.int_to_bytes(spec.BLS_WITHDRAWAL_PREFIX, length=1) + spec.hash(b"junk")[1:]
|
||||||
deposit = prepare_state_and_deposit(
|
deposit = prepare_state_and_deposit(
|
||||||
spec,
|
spec,
|
||||||
state,
|
state,
|
||||||
|
|
|
@ -5,7 +5,7 @@ from eth2spec.utils.ssz.ssz_impl import signing_root
|
||||||
from eth2spec.utils.bls import bls_sign
|
from eth2spec.utils.bls import bls_sign
|
||||||
|
|
||||||
from eth2spec.test.helpers.state import get_balance
|
from eth2spec.test.helpers.state import get_balance
|
||||||
from eth2spec.test.helpers.transfers import get_valid_transfer
|
# from eth2spec.test.helpers.transfers import get_valid_transfer
|
||||||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block
|
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block
|
||||||
from eth2spec.test.helpers.keys import privkeys, pubkeys
|
from eth2spec.test.helpers.keys import privkeys, pubkeys
|
||||||
from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing
|
from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing
|
||||||
|
@ -303,38 +303,38 @@ def test_voluntary_exit(spec, state):
|
||||||
assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
|
assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
|
||||||
|
|
||||||
|
|
||||||
@with_all_phases
|
# @with_all_phases
|
||||||
@spec_state_test
|
# @spec_state_test
|
||||||
def test_transfer(spec, state):
|
# def test_transfer(spec, state):
|
||||||
# overwrite default 0 to test
|
# overwrite default 0 to test
|
||||||
spec.MAX_TRANSFERS = 1
|
# spec.MAX_TRANSFERS = 1
|
||||||
|
|
||||||
sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
# sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
||||||
amount = get_balance(state, sender_index)
|
# amount = get_balance(state, sender_index)
|
||||||
|
|
||||||
transfer = get_valid_transfer(spec, state, state.slot + 1, sender_index, amount, signed=True)
|
# transfer = get_valid_transfer(spec, state, state.slot + 1, sender_index, amount, signed=True)
|
||||||
recipient_index = transfer.recipient
|
# recipient_index = transfer.recipient
|
||||||
pre_transfer_recipient_balance = get_balance(state, recipient_index)
|
# pre_transfer_recipient_balance = get_balance(state, recipient_index)
|
||||||
|
|
||||||
# un-activate so validator can transfer
|
# un-activate so validator can transfer
|
||||||
state.validators[sender_index].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
|
# state.validators[sender_index].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
|
||||||
|
|
||||||
yield 'pre', state
|
# yield 'pre', state
|
||||||
|
|
||||||
# Add to state via block transition
|
# Add to state via block transition
|
||||||
block = build_empty_block_for_next_slot(spec, state)
|
# block = build_empty_block_for_next_slot(spec, state)
|
||||||
block.body.transfers.append(transfer)
|
# block.body.transfers.append(transfer)
|
||||||
sign_block(spec, state, block)
|
# sign_block(spec, state, block)
|
||||||
|
|
||||||
yield 'blocks', [block], List[spec.BeaconBlock]
|
# yield 'blocks', [block], List[spec.BeaconBlock]
|
||||||
|
|
||||||
spec.state_transition(state, block)
|
# spec.state_transition(state, block)
|
||||||
yield 'post', state
|
# yield 'post', state
|
||||||
|
|
||||||
sender_balance = get_balance(state, sender_index)
|
# sender_balance = get_balance(state, sender_index)
|
||||||
recipient_balance = get_balance(state, recipient_index)
|
# recipient_balance = get_balance(state, recipient_index)
|
||||||
assert sender_balance == 0
|
# assert sender_balance == 0
|
||||||
assert recipient_balance == pre_transfer_recipient_balance + amount
|
# assert recipient_balance == pre_transfer_recipient_balance + amount
|
||||||
|
|
||||||
|
|
||||||
@with_all_phases
|
@with_all_phases
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from typing import Dict, Any, Callable, Iterable
|
from typing import Dict, Any, Callable, Iterable
|
||||||
from eth2spec.debug.encode import encode
|
from eth2spec.debug.encode import encode
|
||||||
|
from eth2spec.utils.ssz.ssz_typing import Container
|
||||||
|
|
||||||
|
|
||||||
def spectest(description: str = None):
|
def spectest(description: str = None):
|
||||||
|
@ -30,9 +31,13 @@ def spectest(description: str = None):
|
||||||
else:
|
else:
|
||||||
# Otherwise, try to infer the type, but keep it as-is if it's not a SSZ container.
|
# Otherwise, try to infer the type, but keep it as-is if it's not a SSZ container.
|
||||||
(key, value) = data
|
(key, value) = data
|
||||||
if hasattr(value.__class__, 'fields'):
|
if isinstance(value, Container):
|
||||||
out[key] = encode(value, value.__class__)
|
out[key] = encode(value, value.__class__)
|
||||||
else:
|
else:
|
||||||
|
# not a ssz value.
|
||||||
|
# It could be vector or bytes still, but it is a rare case,
|
||||||
|
# and lists can't be inferred fully (generics lose element type).
|
||||||
|
# In such cases, explicitly state the type of the yielded value as a third yielded object.
|
||||||
out[key] = value
|
out[key] = value
|
||||||
if has_contents:
|
if has_contents:
|
||||||
return out
|
return out
|
||||||
|
|
|
@ -1,5 +1,28 @@
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
|
|
||||||
|
ZERO_BYTES32 = b'\x00' * 32
|
||||||
|
|
||||||
|
|
||||||
|
def _hash(x):
|
||||||
|
return sha256(x).digest()
|
||||||
|
|
||||||
|
|
||||||
|
# Minimal collection of (key, value) pairs, for fast hash-retrieval, to save on repetitive computation cost.
|
||||||
|
# Key = the hash input
|
||||||
|
# Value = the hash output
|
||||||
|
hash_cache = []
|
||||||
|
|
||||||
|
|
||||||
|
def add_zero_hashes_to_cache():
|
||||||
|
zerohashes = [(None, ZERO_BYTES32)]
|
||||||
|
for layer in range(1, 32):
|
||||||
|
k = zerohashes[layer - 1][1] + zerohashes[layer - 1][1]
|
||||||
|
zerohashes.append((k, _hash(k)))
|
||||||
|
hash_cache.extend(zerohashes[1:])
|
||||||
|
|
||||||
|
|
||||||
def hash(x):
|
def hash(x):
|
||||||
return sha256(x).digest()
|
for (k, h) in hash_cache:
|
||||||
|
if x == k:
|
||||||
|
return h
|
||||||
|
return _hash(x)
|
||||||
|
|
|
@ -513,13 +513,11 @@ def read_vector_elem_type(vector_typ: Type[Vector[T, L]]) -> T:
|
||||||
|
|
||||||
|
|
||||||
def read_elem_type(typ):
|
def read_elem_type(typ):
|
||||||
if typ == bytes:
|
if typ == bytes or (isinstance(typ, type) and issubclass(typ, bytes)): # bytes or bytesN
|
||||||
return byte
|
return byte
|
||||||
elif is_list_type(typ):
|
elif is_list_type(typ):
|
||||||
return read_list_elem_type(typ)
|
return read_list_elem_type(typ)
|
||||||
elif is_vector_type(typ):
|
elif is_vector_type(typ):
|
||||||
return read_vector_elem_type(typ)
|
return read_vector_elem_type(typ)
|
||||||
elif issubclass(typ, bytes): # bytes or bytesN
|
|
||||||
return byte
|
|
||||||
else:
|
else:
|
||||||
raise TypeError("Unexpected type: {}".format(typ))
|
raise TypeError("Unexpected type: {}".format(typ))
|
||||||
|
|
Loading…
Reference in New Issue