diff --git a/.gitignore b/.gitignore index da932c0..4827896 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ venv test-reports/ notes.txt .mypy_cache -.eggs/ \ No newline at end of file +.eggs/ +*.egg-info/ \ No newline at end of file diff --git a/README.md b/README.md index d5b3e67..56e9590 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ > Implements a proof of concept beacon chain for a sharded pos ethereum 2.0. Spec in progress can be found [here](https://notes.ethereum.org/s/Syj3QZSxm). ## Installation -Using a python3 environment, run the following to install required libraries: +Using a python3.6.* environment, run the following to install required libraries: ``` pip install -e .[dev] ``` @@ -27,5 +27,5 @@ make deploy --- -# Simple Serialization -[here](https://github.com/ethereum/beacon_chain/tree/master/ssz) \ No newline at end of file +# Simple Serialize +[here](https://github.com/ethereum/beacon_chain/tree/master/ssz) diff --git a/beacon_chain/state/active_state.py b/beacon_chain/state/active_state.py index 9918544..f679084 100644 --- a/beacon_chain/state/active_state.py +++ b/beacon_chain/state/active_state.py @@ -4,6 +4,7 @@ ) from .attestation_record import AttestationRecord +from .chain import Chain class ActiveState(): @@ -31,6 +32,13 @@ def __init__(self, **kwargs): else: self.block_vote_cache = {} + # chain is not part of protocol state + # is used as helper class to aid in doing state transition + if 'chain' in kwargs: + self.chain = kwargs['chain'] + else: + self.chain = Chain() + def __setattr__(self, name: str, value: Any) -> None: super().__setattr__(name, value) diff --git a/beacon_chain/state/block.py b/beacon_chain/state/block.py index 4064d80..ec2499d 100644 --- a/beacon_chain/state/block.py +++ b/beacon_chain/state/block.py @@ -8,6 +8,9 @@ ) from beacon_chain.utils.blake import blake +from beacon_chain.state.constants import ( + ZERO_HASH32, +) from .attestation_record import AttestationRecord @@ -31,13 +34,13 @@ class Block(): } # type: Dict[str, Any] defaults = { - 'parent_hash': b'\x00'*32, + 'parent_hash': ZERO_HASH32, 'slot_number': 0, - 'randao_reveal': b'\x00'*32, + 'randao_reveal': ZERO_HASH32, 'attestations': [], - 'pow_chain_ref': b'\x00'*32, - 'active_state_root': b'\x00'*32, - 'crystallized_state_root': b'\x00'*32, + 'pow_chain_ref': ZERO_HASH32, + 'active_state_root': ZERO_HASH32, + 'crystallized_state_root': ZERO_HASH32, } def __init__(self, **kwargs): diff --git a/beacon_chain/state/chain.py b/beacon_chain/state/chain.py new file mode 100644 index 0000000..7c0a251 --- /dev/null +++ b/beacon_chain/state/chain.py @@ -0,0 +1,49 @@ +from typing import ( + List, + TYPE_CHECKING, +) + +if TYPE_CHECKING: + from .block import Block # noqa: F401 + + +class Chain(): + # Note, this is not an object defined in the v2.1 spec + # this is a helper object to mask complexity in tracking + # blocks + + def __init__(self, head: 'Block'=None, blocks: List['Block']=[]) -> None: + self.head = head + self.blocks = blocks + self.chain = [] # type: List['Block'] + + # temp helper + all_blocks_by_hash = { + block.hash: block + for block in self.blocks + } + + if self.head: + tmp = self.head + self.chain.append(tmp) + while all_blocks_by_hash.get(tmp.parent_hash, None): + tmp = all_blocks_by_hash[tmp.parent_hash] + self.chain.append(tmp) + + self.block_by_hash = { + block.hash: block + for block in self.chain + } + self.block_by_slot_number = { + block.slot_number: block + for block in self.chain + } + + def __contains__(self, block: 'Block') -> bool: + return bool(self.get_block_by_hash(block.hash)) + + def get_block_by_slot_number(self, slot_number: int) -> 'Block': + return self.block_by_slot_number.get(slot_number, None) + + def get_block_by_hash(self, block_hash: bytes) -> 'Block': + return self.block_by_hash.get(block_hash, None) diff --git a/beacon_chain/state/config.py b/beacon_chain/state/config.py index aa35a48..e239747 100644 --- a/beacon_chain/state/config.py +++ b/beacon_chain/state/config.py @@ -1,14 +1,22 @@ +from beacon_chain.state.constants import ( + WEI_PER_ETH, +) + + +BASE_REWARD_QUOTIENT = 2**15 DEFAULT_END_DYNASTY = 9999999999999999999 -DEPOSIT_SIZE = 32 # ETH +DEPOSIT_SIZE = 32 * WEI_PER_ETH # WEI CYCLE_LENGTH = 64 # slots MAX_VALIDATOR_COUNT = 2**22 # validators MIN_COMMITTEE_SIZE = 128 # validators MIN_DYNASTY_LENGTH = 256 # slots SHARD_COUNT = 1024 # shards SLOT_DURATION = 8 # seconds +SQRT_E_DROP_TIME = 2**20 # seconds def generate_config(*, + base_reward_quotient=BASE_REWARD_QUOTIENT, default_end_dynasty=DEFAULT_END_DYNASTY, deposit_size=DEPOSIT_SIZE, cycle_length=CYCLE_LENGTH, @@ -16,8 +24,10 @@ def generate_config(*, min_committee_size=MIN_COMMITTEE_SIZE, min_dynasty_length=MIN_DYNASTY_LENGTH, shard_count=SHARD_COUNT, - slot_duration=SLOT_DURATION): + slot_duration=SLOT_DURATION, + sqrt_e_drop_time=SQRT_E_DROP_TIME): return { + 'base_reward_quotient': base_reward_quotient, 'default_end_dynasty': default_end_dynasty, 'deposit_size': deposit_size, 'cycle_length': cycle_length, @@ -25,7 +35,8 @@ def generate_config(*, 'min_committee_size': min_committee_size, 'min_dynasty_length': min_dynasty_length, 'shard_count': shard_count, - 'slot_duration': slot_duration + 'slot_duration': slot_duration, + 'sqrt_e_drop_time': sqrt_e_drop_time } diff --git a/beacon_chain/state/constants.py b/beacon_chain/state/constants.py index fb92988..375fb9d 100644 --- a/beacon_chain/state/constants.py +++ b/beacon_chain/state/constants.py @@ -1,6 +1,9 @@ +from eth_utils import denoms + from beacon_chain.beacon_typing.custom import ( Hash32, ) +WEI_PER_ETH = denoms.ether ZERO_HASH32 = Hash32(32 * b'\x00') diff --git a/beacon_chain/state/crystallized_state.py b/beacon_chain/state/crystallized_state.py index 482bef8..16efcd1 100644 --- a/beacon_chain/state/crystallized_state.py +++ b/beacon_chain/state/crystallized_state.py @@ -1,12 +1,15 @@ from typing import ( # noqa: F401 Any, Dict, + List, ) from .crosslink_record import CrosslinkRecord from .shard_and_committee import ShardAndCommittee from .validator_record import ValidatorRecord +from .helpers import get_active_validator_indices + class CrystallizedState(): fields = { @@ -44,7 +47,6 @@ class CrystallizedState(): 'last_finalized_slot': 0, 'current_dynasty': 0, 'crosslink_records': [], - 'total_deposits': 0, 'dynasty_seed': b'\x00'*32, 'dynasty_start': 0, } # type: Dict[str, Any] @@ -60,6 +62,22 @@ def __setattr__(self, name: str, value: Any) -> None: def __getattribute__(self, name: str) -> Any: return super().__getattribute__(name) + @property + def active_validator_indices(self) -> List[int]: + return get_active_validator_indices( + self.current_dynasty, + self.validators + ) + + @property + def total_deposits(self) -> int: + return sum( + map( + lambda index: self.validators[index].balance, + self.active_validator_indices + ) + ) + @property def num_validators(self) -> int: return len(self.validators) diff --git a/beacon_chain/state/genesis_helpers.py b/beacon_chain/state/genesis_helpers.py index 8a658d8..c8ce40b 100644 --- a/beacon_chain/state/genesis_helpers.py +++ b/beacon_chain/state/genesis_helpers.py @@ -12,6 +12,7 @@ from .active_state import ActiveState from .block import Block +from .chain import Chain from .constants import ( ZERO_HASH32, ) @@ -30,7 +31,8 @@ def get_genesis_active_state(config: Dict[str, Any]) -> ActiveState: return ActiveState( pending_attestations=[], - recent_block_hashes=recent_block_hashes + recent_block_hashes=recent_block_hashes, + chain=Chain() ) @@ -52,8 +54,6 @@ def get_genesis_crystallized_state( # concatenate with itself to span 2*CYCLE_LENGTH shard_and_committee_for_slots = shard_and_committee_for_slots + shard_and_committee_for_slots - total_deposits = config['deposit_size'] * len(validators) - return CrystallizedState( validators=validators, last_state_recalc=0, @@ -67,7 +67,6 @@ def get_genesis_crystallized_state( for i in range(config['shard_count']) ], - total_deposits=total_deposits, dynasty_seed=init_shuffling_seed, dynasty_start=0, ) diff --git a/beacon_chain/state/state_transition.py b/beacon_chain/state/state_transition.py index 8c743b8..7833123 100644 --- a/beacon_chain/state/state_transition.py +++ b/beacon_chain/state/state_transition.py @@ -1,3 +1,5 @@ +from math import sqrt + from typing import ( Any, Dict, @@ -31,6 +33,12 @@ from .active_state import ( ActiveState, ) +from .chain import ( + Chain, +) +from .constants import ( + WEI_PER_ETH, +) from .crosslink_record import ( CrosslinkRecord, ) @@ -48,6 +56,7 @@ if TYPE_CHECKING: from .attesation_record import AttestationRecord # noqa: F401 from .block import Block # noqa: F401 + from .validator_record import ValidatorRecord # noqa: F401 def validate_block(block: 'Block') -> bool: @@ -185,11 +194,16 @@ def process_block(crystallized_state: CrystallizedState, ) new_attestations = active_state.pending_attestations + block.attestations + new_chain = Chain( + head=block, + blocks=active_state.chain.blocks + [block] + ) new_active_state = ActiveState( pending_attestations=new_attestations, recent_block_hashes=active_state.recent_block_hashes[:], - block_vote_cache=new_block_vote_cache + block_vote_cache=new_block_vote_cache, + chain=new_chain ) return new_active_state @@ -246,6 +260,9 @@ def initialize_new_cycle(crystallized_state: CrystallizedState, last_justified_slot = crystallized_state.last_justified_slot last_finalized_slot = crystallized_state.last_finalized_slot justified_streak = crystallized_state.justified_streak + + total_deposits = crystallized_state.total_deposits + # walk through slots last_state_recalc - CYCLE_LENGTH ... last_state_recalc - 1 # and check for justification, streaks, and finality for i in range(cycle_length): @@ -257,7 +274,7 @@ def initialize_new_cycle(crystallized_state: CrystallizedState, else: vote_balance = 0 - if 3 * vote_balance >= 2 * crystallized_state.total_deposits: + if 3 * vote_balance >= 2 * total_deposits: last_justified_slot = max(last_justified_slot, slot) justified_streak += 1 else: @@ -279,16 +296,18 @@ def initialize_new_cycle(crystallized_state: CrystallizedState, if a.slot >= last_state_recalc ] - dynasty = crystallized_state.current_dynasty # STUB - dynasty_seed = crystallized_state.dynasty_seed # STUB - dynasty_start = crystallized_state.dynasty_start - validators = deepcopy(crystallized_state.validators) # STUB + validators = apply_rewards_and_penalties( + crystallized_state, + active_state, + block, + config=config + ) + shard_and_committee_for_slots = ( crystallized_state.shard_and_committee_for_slots[cycle_length:] + # this is a stub and will be addressed by shuffling at dynasty change crystallized_state.shard_and_committee_for_slots[cycle_length:] ) - active_validator_indices = get_active_validator_indices(dynasty, validators) new_crystallized_state = CrystallizedState( validators=validators, @@ -299,9 +318,8 @@ def initialize_new_cycle(crystallized_state: CrystallizedState, last_finalized_slot=last_finalized_slot, current_dynasty=crystallized_state.current_dynasty, crosslink_records=crosslink_records, - total_deposits=sum(map(lambda i: validators[i].balance, active_validator_indices)), - dynasty_seed=dynasty_seed, - dynasty_start=dynasty_start + dynasty_seed=crystallized_state.dynasty_seed, + dynasty_start=crystallized_state.dynasty_start ) new_active_state = ActiveState( @@ -330,26 +348,120 @@ def fill_recent_block_hashes(active_state: ActiveState, ) -def compute_cycle_transitions( - crystallized_state: CrystallizedState, - active_state: ActiveState, - block: 'Block', - config: Dict[str, Any]=DEFAULT_CONFIG) -> Tuple[CrystallizedState, ActiveState]: - while block.slot_number >= crystallized_state.last_state_recalc + config['cycle_length']: - crystallized_state, active_state = initialize_new_cycle( - crystallized_state, - active_state, - block, - config=config, +def calculate_ffg_rewards(crystallized_state: CrystallizedState, + active_state: ActiveState, + block: 'Block', + config: Dict[str, Any]=DEFAULT_CONFIG) -> List[int]: + validators = crystallized_state.validators + active_validator_indices = get_active_validator_indices( + crystallized_state.current_dynasty, + validators + ) + rewards_and_penalties = [0 for _ in validators] # type: List[int] + + time_since_finality = block.slot_number - crystallized_state.last_finalized_slot + total_deposits = crystallized_state.total_deposits + total_deposits_in_ETH = total_deposits // WEI_PER_ETH + reward_quotient = config['base_reward_quotient'] * int(sqrt(total_deposits_in_ETH)) + quadratic_penalty_quotient = int(sqrt(config['sqrt_e_drop_time'] / config['slot_duration'])) + + last_state_recalc = crystallized_state.last_state_recalc + block_vote_cache = active_state.block_vote_cache + + for slot in range(last_state_recalc - config['cycle_length'], last_state_recalc): + block = active_state.chain.get_block_by_slot_number(slot) + if block: + block_hash = block.hash + total_participated_deposits = block_vote_cache[block_hash]['total_voter_deposits'] + voter_indices = block_vote_cache[block_hash]['total_voter_deposits'] + else: + total_participated_deposits = 0 + voter_indices = set() + + participating_validator_indices = filter( + lambda index: index in voter_indices, + active_validator_indices ) - if ready_for_dynasty_transition(crystallized_state, block, config): - crystallized_state = compute_dynasty_transition( - crystallized_state, - block, - config - ) + non_participating_validator_indices = filter( + lambda index: index not in voter_indices, + active_validator_indices + ) + # finalized recently? + if time_since_finality <= 2 * config['cycle_length']: + for index in participating_validator_indices: + rewards_and_penalties[index] += ( + validators[index].balance // + reward_quotient * + (2 * total_participated_deposits - total_deposits) // + total_deposits + ) + for index in non_participating_validator_indices: + rewards_and_penalties[index] -= ( + validators[index].balance // + reward_quotient + ) + else: + for index in non_participating_validator_indices: + rewards_and_penalties[index] = ( + validators[index].balance // + reward_quotient + + validators[index].balance * + time_since_finality // + quadratic_penalty_quotient + ) + + return rewards_and_penalties - return crystallized_state, active_state + +def calculate_crosslink_rewards(crystallized_state: CrystallizedState, + active_state: ActiveState, + block: 'Block', + config: Dict[str, Any]=DEFAULT_CONFIG) -> List[int]: + validators = crystallized_state.validators + rewards_and_penalties = [0 for _ in validators] # type: List[int] + + # + # STUB + # Still need clarity in spec to properly fill these calculations + # + + return rewards_and_penalties + + +def apply_rewards_and_penalties(crystallized_state: CrystallizedState, + active_state: ActiveState, + block: 'Block', + config: Dict[str, Any]=DEFAULT_CONFIG) -> List['ValidatorRecord']: + # FFG Rewards + ffg_rewards = calculate_ffg_rewards( + crystallized_state, + active_state, + block, + config=config + ) + + # Crosslink Rewards + crosslink_rewards = calculate_crosslink_rewards( + crystallized_state, + active_state, + block, + config=config + ) + + updated_validators = deepcopy(crystallized_state.validators) + active_validator_indices = get_active_validator_indices( + crystallized_state.current_dynasty, + crystallized_state.validators + ) + + # apply rewards and penalties + for index in active_validator_indices: + updated_validators[index].balance += ( + ffg_rewards[index] + + crosslink_rewards[index] + ) + + return updated_validators def ready_for_dynasty_transition(crystallized_state: CrystallizedState, @@ -401,6 +513,29 @@ def compute_dynasty_transition(crystallized_state: CrystallizedState, return crystallized_state +def compute_cycle_transitions( + crystallized_state: CrystallizedState, + active_state: ActiveState, + block: 'Block', + config: Dict[str, Any]=DEFAULT_CONFIG) -> Tuple[CrystallizedState, ActiveState]: + while block.slot_number >= crystallized_state.last_state_recalc + config['cycle_length']: + crystallized_state, active_state = initialize_new_cycle( + crystallized_state, + active_state, + block, + config=config + ) + + if ready_for_dynasty_transition(crystallized_state, block, config): + crystallized_state = compute_dynasty_transition( + crystallized_state, + block, + config=config + ) + + return crystallized_state, active_state + + def compute_state_transition( parent_state: Tuple[CrystallizedState, ActiveState], parent_block: 'Block', diff --git a/ssz/README.md b/ssz/README.md index e0bb587..3d53214 100644 --- a/ssz/README.md +++ b/ssz/README.md @@ -13,13 +13,13 @@ int8: 5 --> b’\x05’ bytes: b'cow' --> b'\x00\x00\x00\x03cow' address: b'\x35'*20 --> b’55555555555555555555’ hash32: b'\x35'*32 --> b’55555555555555555555555555555555’ -array: [3, 4, 5] --> b'\x00\x00\x00\x03\x03\x04\x05' +list['int8']: [3, 4, 5] --> b'\x00\x00\x00\x03\x03\x04\x05' ``` ## Python Usage -Serialize() takes 1 to 2 arguments. Serialize() can minimally take a value and perform standard operation, or serialize() can take a value and explicit type to shorten length of serialization. If type is not an explicitly supported type, output data will default to 4 bytes. +```serialize(val, typ)``` takes 1 to 2 arguments. ```serialize(val, typ)``` can minimally take a value and perform the standard operation, or ```serialize(val, typ)``` can take a value and explicit type to shorten the length of serialization. If ```typ``` is not an explicitly supported type, output data will default to 4 bytes. -Deserialize() takes 2 arguments: data and type and deserializes into previously serialized value +```deserialize(data, typ)``` takes 2 arguments: data and type and deserializes into the previously serialized value ### Example diff --git a/tests/state/conftest.py b/tests/state/conftest.py index 93ebdf0..6d2a97c 100644 --- a/tests/state/conftest.py +++ b/tests/state/conftest.py @@ -6,6 +6,7 @@ ) from beacon_chain.state.config import ( + BASE_REWARD_QUOTIENT, DEFAULT_END_DYNASTY, DEPOSIT_SIZE, CYCLE_LENGTH, @@ -14,6 +15,7 @@ MIN_DYNASTY_LENGTH, SHARD_COUNT, SLOT_DURATION, + SQRT_E_DROP_TIME, generate_config, ) from beacon_chain.state.attestation_record import ( @@ -116,7 +118,6 @@ def sample_crystallized_state_params(): 'last_finalized_slot': 70, 'current_dynasty': 4, 'crosslink_records': [], - 'total_deposits': 10000, 'dynasty_seed': b'\x55'*32, 'dynasty_start': 3, } @@ -158,6 +159,11 @@ def init_randao(): return DEFAULT_RANDAO +@pytest.fixture +def base_reward_quotient(): + return BASE_REWARD_QUOTIENT + + @pytest.fixture def default_end_dynasty(): return DEFAULT_END_DYNASTY @@ -199,15 +205,23 @@ def slot_duration(): @pytest.fixture -def config(default_end_dynasty, +def sqrt_e_drop_time(): + return SQRT_E_DROP_TIME + + +@pytest.fixture +def config(base_reward_quotient, + default_end_dynasty, deposit_size, cycle_length, max_validator_count, min_committee_size, min_dynasty_length, shard_count, - slot_duration): + slot_duration, + sqrt_e_drop_time): return generate_config( + base_reward_quotient=base_reward_quotient, default_end_dynasty=default_end_dynasty, deposit_size=deposit_size, cycle_length=cycle_length, @@ -215,7 +229,8 @@ def config(default_end_dynasty, min_committee_size=min_committee_size, min_dynasty_length=min_dynasty_length, shard_count=shard_count, - slot_duration=slot_duration + slot_duration=slot_duration, + sqrt_e_drop_time=sqrt_e_drop_time ) diff --git a/tests/state/test_chain.py b/tests/state/test_chain.py new file mode 100644 index 0000000..19e7b2d --- /dev/null +++ b/tests/state/test_chain.py @@ -0,0 +1,59 @@ +import pytest + +from beacon_chain.state.block import ( + Block, +) +from beacon_chain.state.chain import ( + Chain, +) +from beacon_chain.state.constants import ( + ZERO_HASH32, +) + + +def test_head(): + block = Block() + chain = Chain(head=block) + + assert chain.head == block + + +def test_block_by_hash(): + block = Block() + chain = Chain(head=block, blocks=[block]) + + assert chain.get_block_by_hash(block.hash) == block + assert chain.get_block_by_hash(b'\x35'*32) is None + + +@pytest.mark.parametrize( + 'slot_number', + [(1), (10), (1000)] +) +def test_block_by_slot_number(slot_number): + block = Block(slot_number=slot_number) + chain = Chain(head=block, blocks=[block]) + + assert chain.get_block_by_slot_number(block.slot_number) == block + assert chain.get_block_by_slot_number(block.slot_number + 1) is None + assert chain.get_block_by_slot_number(-1) is None + + +def test_chain(): + block = None + parent_hash = ZERO_HASH32 + blocks = [] + for slot_number in range(1, 10): + block = Block( + slot_number=slot_number, + parent_hash=parent_hash + ) + blocks.append(block) + parent_hash = block.hash + + extra_block = Block(slot_number=1000000) + chain = Chain(head=block, blocks=blocks + [extra_block]) + assert len(chain.chain) == len(blocks) + for block in blocks: + assert block in chain + assert extra_block not in chain diff --git a/tests/state/test_crystallized_state.py b/tests/state/test_crystallized_state.py index 2bd72ce..fe4b8c4 100644 --- a/tests/state/test_crystallized_state.py +++ b/tests/state/test_crystallized_state.py @@ -23,7 +23,6 @@ ('last_finalized_slot', 0), ('current_dynasty', 0), ('crosslink_records', []), - ('total_deposits', 0), ('dynasty_seed', b'\x00'*32), ('dynasty_start', 0), ] @@ -59,3 +58,33 @@ def test_num_crosslink_records(expected): ) assert crystallized_state.num_crosslink_records == expected + + +@pytest.mark.parametrize( + 'num_active_validators', + [ + (0), + (1), + (5), + (20), + ] +) +def test_total_deposits(num_active_validators, config): + start_dynasty = 10 + active_validators = [ + mock_validator_record(pubkey, start_dynasty=start_dynasty) + for pubkey in range(num_active_validators) + ] + non_active_validators = [ + mock_validator_record(pubkey, start_dynasty=start_dynasty+1) + for pubkey in range(4) + ] + crystallized_state = CrystallizedState( + validators=active_validators + non_active_validators, + current_dynasty=start_dynasty + ) + + assert len(crystallized_state.active_validator_indices) == len(active_validators) + + expected_total_deposits = config['deposit_size'] * num_active_validators + assert crystallized_state.total_deposits == expected_total_deposits diff --git a/tests/state/test_genesis_helpers.py b/tests/state/test_genesis_helpers.py index d247374..86a8ca0 100644 --- a/tests/state/test_genesis_helpers.py +++ b/tests/state/test_genesis_helpers.py @@ -18,6 +18,8 @@ def test_get_genesis_active_state(config): active_state = get_genesis_active_state(config) assert active_state.num_pending_attestations == 0 assert active_state.num_recent_block_hashes == config['cycle_length'] * 2 + assert len(active_state.chain.blocks) == 0 + assert active_state.chain.head is None def test_get_genesis_crystallized_state(genesis_validators, diff --git a/tests/state/test_state_transition.py b/tests/state/test_state_transition.py index 8592560..4db0543 100644 --- a/tests/state/test_state_transition.py +++ b/tests/state/test_state_transition.py @@ -48,21 +48,20 @@ def test_validate_attestation_aggregate_sig(): 'last_justified_slot,' 'justified_streak,' 'last_finalized_slot,' - 'crystallized_state_total_deposits,' - 'block_vote_cache_total_deposits,' + 'fraction_voted,' 'result_last_state_recalc,' 'result_justified_streak,' 'result_last_finalized_slot' ), [ # 2/3 attestations - (64, 0, 0, 0, 0, 3, 2, 64, 0+64, 0), + (64, 0, 0, 0, 0, 2/3.0, 64, 0+64, 0), # 1/3 attestations - (64, 0, 0, 0, 0, 3, 1, 64, 0+0, 0), + (64, 0, 0, 0, 0, 1/3.0, 64, 0+0, 0), # 2/3 attestations, last_finalized_slot = slot - cycle_length - 1 - (64, 128, 128, 64, 0, 3, 2, 128+64, 64+64, 127-64-1), + (64, 128, 128, 64, 0, 2/3.0, 128+64, 64+64, 127-64-1), # 2/3 attestations, last_finalized_slot = last_finalized_slot - (64, 128, 128, 128, 128, 3, 2, 128+64, 128+64, 128), + (64, 128, 128, 128, 128, 2/3.0, 128+64, 128+64, 128), ], ) def test_initialize_new_cycle(genesis_crystallized_state, @@ -72,8 +71,7 @@ def test_initialize_new_cycle(genesis_crystallized_state, last_justified_slot, justified_streak, last_finalized_slot, - crystallized_state_total_deposits, - block_vote_cache_total_deposits, + fraction_voted, result_last_state_recalc, result_justified_streak, result_last_finalized_slot, @@ -84,7 +82,6 @@ def test_initialize_new_cycle(genesis_crystallized_state, parent_crystallized_state.last_justified_slot = last_justified_slot parent_crystallized_state.justified_streak = justified_streak parent_crystallized_state.last_finalized_slot = last_finalized_slot - parent_crystallized_state.total_deposits = crystallized_state_total_deposits parent_active_state = genesis_active_state @@ -96,10 +93,12 @@ def test_initialize_new_cycle(genesis_crystallized_state, active_state = fill_recent_block_hashes( parent_active_state, parent_block, block ) + + fraction_voted *= 1.01 # add margin for rounding error # Fill the total_voter_deposits to simulate the different committee results active_state.block_vote_cache[block.parent_hash] = { 'voter_indices': set(), - 'total_voter_deposits': block_vote_cache_total_deposits, + 'total_voter_deposits': int(parent_crystallized_state.total_deposits * fraction_voted) } crystallized_state, active_state = initialize_new_cycle(