diff --git a/beacon_chain/consensus_object_pools/blockchain_dag.nim b/beacon_chain/consensus_object_pools/blockchain_dag.nim index a49d53397..8fdd22bf8 100644 --- a/beacon_chain/consensus_object_pools/blockchain_dag.nim +++ b/beacon_chain/consensus_object_pools/blockchain_dag.nim @@ -100,19 +100,18 @@ func parentOrSlot*(bs: BlockSlot): BlockSlot = else: BlockSlot(blck: bs.blck, slot: bs.slot - 1) -func get_effective_balances*(state: BeaconState): seq[Gwei] = +func get_effective_balances(validators: openArray[Validator], epoch: Epoch): + seq[Gwei] = ## Get the balances from a state as counted for fork choice - result.newSeq(state.validators.len) # zero-init - - let epoch = state.get_current_epoch() + result.newSeq(validators.len) # zero-init for i in 0 ..< result.len: # All non-active validators have a 0 balance - let validator = unsafeAddr state.validators[i] + let validator = unsafeAddr validators[i] if validator[].is_active_validator(epoch): result[i] = validator[].effective_balance -proc init*( +func init( T: type EpochRef, state: StateData, cache: var StateCache, prevEpoch: EpochRef): T = let @@ -181,8 +180,8 @@ proc init*( epochRef.effective_balances_bytes = snappyEncode(SSZ.encode( - List[Gwei, Limit VALIDATOR_REGISTRY_LIMIT]( - get_effective_balances(state.data.data)))) + List[Gwei, Limit VALIDATOR_REGISTRY_LIMIT](get_effective_balances( + getStateField(state, validators).asSeq, get_current_epoch(state))))) epochRef @@ -256,7 +255,7 @@ func atEpochStart*(blck: BlockRef, epoch: Epoch): BlockSlot = ## Return the BlockSlot corresponding to the first slot in the given epoch atSlot(blck, epoch.compute_start_slot_at_epoch) -func atEpochEnd*(blck: BlockRef, epoch: Epoch): BlockSlot = +func atEpochEnd(blck: BlockRef, epoch: Epoch): BlockSlot = ## Return the BlockSlot corresponding to the last slot in the given epoch atSlot(blck, (epoch + 1).compute_start_slot_at_epoch - 1) @@ -285,7 +284,7 @@ func findEpochRef*( return nil -proc loadStateCache*( +func loadStateCache( dag: ChainDAGRef, cache: var StateCache, blck: BlockRef, epoch: Epoch) = # When creating a state cache, we want the current and the previous epoch # information to be preloaded as both of these are used in state transition @@ -467,7 +466,7 @@ proc init*(T: type ChainDAGRef, res -proc getEpochRef*( +func getEpochRef*( dag: ChainDAGRef, state: StateData, cache: var StateCache): EpochRef = let blck = state.blck @@ -575,7 +574,7 @@ proc getState(dag: ChainDAGRef, state: var StateData, bs: BlockSlot): bool = false -proc putState*(dag: ChainDAGRef, state: var StateData) = +proc putState(dag: ChainDAGRef, state: var StateData) = # Store a state and its root logScope: blck = shortLog(state.blck) @@ -671,12 +670,6 @@ func getBlockBySlot*(dag: ChainDAGRef, slot: Slot): BlockRef = ## with slot number less or equal to `slot`. dag.head.atSlot(slot).blck -func getBlockByPreciseSlot*(dag: ChainDAGRef, slot: Slot): BlockRef = - ## Retrieves a block from the canonical chain with a slot - ## number equal to `slot`. - let found = dag.getBlockBySlot(slot) - if found.slot != slot: found else: nil - proc get*(dag: ChainDAGRef, blck: BlockRef): BlockData = ## Retrieve the associated block body of a block reference doAssert (not blck.isNil), "Trying to get nil BlockRef" @@ -1160,7 +1153,7 @@ proc preInit*( db.putStateRoot(genesisBlock.root, GENESIS_SLOT, genesisBlock.message.state_root) db.putGenesisBlockRoot(genesisBlock.root) -proc setTailState*(dag: ChainDAGRef, +func setTailState*(dag: ChainDAGRef, checkpointState: BeaconState, checkpointBlock: TrustedSignedBeaconBlock) = # TODO(zah) @@ -1171,7 +1164,7 @@ proc setTailState*(dag: ChainDAGRef, proc getGenesisBlockData*(dag: ChainDAGRef): BlockData = dag.get(dag.genesis) -proc getGenesisBlockSlot*(dag: ChainDAGRef): BlockSlot = +func getGenesisBlockSlot*(dag: ChainDAGRef): BlockSlot = BlockSlot(blck: dag.genesis, slot: GENESIS_SLOT) proc getProposer*( diff --git a/beacon_chain/spec/datatypes/altair.nim b/beacon_chain/spec/datatypes/altair.nim index 1089f19c8..67dbb04ce 100644 --- a/beacon_chain/spec/datatypes/altair.nim +++ b/beacon_chain/spec/datatypes/altair.nim @@ -46,6 +46,9 @@ const PROPOSER_WEIGHT* = 8 WEIGHT_DENOMINATOR* = 64 + PARTICIPATION_FLAG_WEIGHTS* = + [TIMELY_SOURCE_WEIGHT, TIMELY_TARGET_WEIGHT, TIMELY_HEAD_WEIGHT] + # https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.6/specs/altair/validator.md#misc TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE* = 4 SYNC_COMMITTEE_SUBNET_COUNT* = 4 @@ -73,8 +76,6 @@ let 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) - PARTICIPATION_FLAG_WEIGHTS* = - [TIMELY_SOURCE_WEIGHT, TIMELY_TARGET_WEIGHT, TIMELY_HEAD_WEIGHT] type ### New types diff --git a/beacon_chain/spec/state_transition_epoch.nim b/beacon_chain/spec/state_transition_epoch.nim index 74468aea3..052f19e49 100644 --- a/beacon_chain/spec/state_transition_epoch.nim +++ b/beacon_chain/spec/state_transition_epoch.nim @@ -582,59 +582,68 @@ func get_base_reward( increments * get_base_reward_per_increment(state, total_active_balance) # https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.6/specs/altair/beacon-chain.md#get_flag_index_deltas -proc get_flag_index_deltas( +iterator get_flag_index_deltas( state: altair.BeaconState, flag_index: int, total_active_balance: Gwei): - (seq[Gwei], seq[Gwei]) = + (ValidatorIndex, Gwei, Gwei) = ## Return the deltas for a given ``flag_index`` by scanning through the ## participation flags. - var - rewards = repeat(Gwei(0), len(state.validators)) - penalties = repeat(Gwei(0), len(state.validators)) let previous_epoch = get_previous_epoch(state) - unslashed_participating_indices = get_unslashed_participating_indices(state, flag_index, previous_epoch) + unslashed_participating_indices = + get_unslashed_participating_indices(state, flag_index, previous_epoch) weight = PARTICIPATION_FLAG_WEIGHTS[flag_index].uint64 # safe - unslashed_participating_balance = get_total_balance(state, unslashed_participating_indices) - unslashed_participating_increments = unslashed_participating_balance div EFFECTIVE_BALANCE_INCREMENT + unslashed_participating_balance = + get_total_balance(state, unslashed_participating_indices) + unslashed_participating_increments = + unslashed_participating_balance div EFFECTIVE_BALANCE_INCREMENT active_increments = total_active_balance div EFFECTIVE_BALANCE_INCREMENT for index in 0 ..< state.validators.len: # TODO Obviously not great let v = state.validators[index] - if not (is_active_validator(v, previous_epoch) or (v.slashed and previous_epoch + 1 < v.withdrawable_epoch)): + if not (is_active_validator(v, previous_epoch) or + (v.slashed and previous_epoch + 1 < v.withdrawable_epoch)): continue - let base_reward = get_base_reward(state, index.ValidatorIndex, total_active_balance) - if index.ValidatorIndex in unslashed_participating_indices: - if not is_in_inactivity_leak(state): - let reward_numerator = base_reward * weight * unslashed_participating_increments - rewards[index] += Gwei(reward_numerator div (active_increments * WEIGHT_DENOMINATOR)) - elif flag_index != TIMELY_HEAD_FLAG_INDEX: - penalties[index] += Gwei(base_reward * weight div WEIGHT_DENOMINATOR) - (rewards, penalties) + template vidx: ValidatorIndex = index.ValidatorIndex + let base_reward = get_base_reward(state, vidx, total_active_balance) + yield + if vidx in unslashed_participating_indices: + if not is_in_inactivity_leak(state): + let reward_numerator = + base_reward * weight * unslashed_participating_increments + (vidx, reward_numerator div (active_increments * WEIGHT_DENOMINATOR), 0.Gwei) + else: + (vidx, 0.Gwei, 0.Gwei) + elif flag_index != TIMELY_HEAD_FLAG_INDEX: + (vidx, 0.Gwei, base_reward * weight div WEIGHT_DENOMINATOR) + else: + (vidx, 0.Gwei, 0.Gwei) # https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.6/specs/altair/beacon-chain.md#modified-get_inactivity_penalty_deltas -func get_inactivity_penalty_deltas(state: altair.BeaconState): (seq[Gwei], seq[Gwei]) = +iterator get_inactivity_penalty_deltas(state: altair.BeaconState): + (ValidatorIndex, Gwei) = ## Return the inactivity penalty deltas by considering timely target ## participation flags and inactivity scores. - var - rewards = repeat(Gwei(0), len(state.validators)) - penalties = repeat(Gwei(0), len(state.validators)) let previous_epoch = get_previous_epoch(state) - matching_target_indices = get_unslashed_participating_indices(state, TIMELY_TARGET_FLAG_INDEX, previous_epoch) + matching_target_indices = + get_unslashed_participating_indices(state, TIMELY_TARGET_FLAG_INDEX, previous_epoch) for index in 0 ..< state.validators.len: # get_eligible_validator_indices() let v = state.validators[index] - if not (is_active_validator(v, previous_epoch) or (v.slashed and previous_epoch + 1 < v.withdrawable_epoch)): + if not (is_active_validator(v, previous_epoch) or + (v.slashed and previous_epoch + 1 < v.withdrawable_epoch)): continue - if not (index.ValidatorIndex in matching_target_indices): + template vidx: untyped = index.ValidatorIndex + if not (vidx in matching_target_indices): + const penalty_denominator = + INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_ALTAIR let - penalty_numerator = state.validators[index].effective_balance * state.inactivity_scores[index] - penalty_denominator = uint64(INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_ALTAIR) - penalties[index] += Gwei(penalty_numerator div penalty_denominator) - (rewards, penalties) + penalty_numerator = state.validators[index].effective_balance * + state.inactivity_scores[index] + yield (vidx, Gwei(penalty_numerator div penalty_denominator)) # https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#process_rewards_and_penalties func process_rewards_and_penalties( @@ -664,14 +673,27 @@ proc process_rewards_and_penalties( return # TODO assess relevance of missing phase0 optimizations - var deltas = mapIt( - 0 ..< PARTICIPATION_FLAG_WEIGHTS.len, - get_flag_index_deltas(state, it, total_active_balance)) - deltas.add get_inactivity_penalty_deltas(state) - for (rewards, penalties) in deltas: - for index in 0 ..< len(state.validators): - increase_balance(state, ValidatorIndex(index), rewards[index]) - decrease_balance(state, ValidatorIndex(index), penalties[index]) + # TODO probably both of these aren't necessary, but need to verify + # commutativity & associativity. Probably, since active validators + # get ejected at 16 Gwei, either it avoids over or underflow there + # or doesn't receive rewards or penalties so both are 0. But start + # with this. + var + rewards = newSeq[Gwei](state.validators.len) + penalties = newSeq[Gwei](state.validators.len) + + for flag_index in 0 ..< PARTICIPATION_FLAG_WEIGHTS.len: + for validator_index, reward, penalty in get_flag_index_deltas( + state, flag_index, total_active_balance): + rewards[validator_index] += reward + penalties[validator_index] += penalty + + for validator_index, penalty in get_inactivity_penalty_deltas(state): + penalties[validator_index] += penalty + + for index in 0 ..< len(state.validators): + increase_balance(state, ValidatorIndex(index), rewards[index]) + decrease_balance(state, ValidatorIndex(index), penalties[index]) # https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#slashings # https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.6/specs/altair/beacon-chain.md#slashings diff --git a/docs/rayonism_merge.md b/docs/rayonism_merge.md index f13e35588..3b242741e 100644 --- a/docs/rayonism_merge.md +++ b/docs/rayonism_merge.md @@ -1,6 +1,6 @@ # How to run Catalyst -- Clone Geth master into ~/client/catalyst: `git clone https://github.com/ethereum/go-ethereum ~/client/catalyst` +- Clone Geth master into ~/client/catalyst: `git clone https://github.com/ethereum/go-ethereum ~/clients/catalyst` - Build Geth and Catalyst with `go build -o ./build/bin/catalyst ./cmd/geth` - Run `scripts/run-catalyst.sh` to run Catalyst. It listens on port 8545. diff --git a/scripts/run-catalyst.sh b/scripts/run-catalyst.sh index 3ecd3b96d..98492b3ab 100755 --- a/scripts/run-catalyst.sh +++ b/scripts/run-catalyst.sh @@ -3,7 +3,7 @@ # https://github.com/prysmaticlabs/bazel-go-ethereum/blob/catalyst/run-catalyst.sh # To increase verbosity: debug.verbosity(4) -# MetaMask seed phrase for account with balance is: +# MetaMask seed phrase for address with balance is: # lecture manual soon title cloth uncle gesture cereal common fruit tooth crater echo \{ \ diff --git a/tests/official/phase0/test_fixture_sanity_blocks.nim b/tests/official/phase0/test_fixture_sanity_blocks.nim index a9d161605..3e49a8899 100644 --- a/tests/official/phase0/test_fixture_sanity_blocks.nim +++ b/tests/official/phase0/test_fixture_sanity_blocks.nim @@ -74,6 +74,5 @@ suite "Official - Phase 0 - Sanity - Blocks " & preset(): runTest("Official - Phase 0 - Sanity - Blocks", SanityBlocksDir, path) suite "Official - Phase 0 - Finality " & preset(): - # these seem to only exist in minimal presets for kind, path in walkDir(FinalityDir, true): runTest("Official - Phase 0 - Finality", FinalityDir, path)