-
Notifications
You must be signed in to change notification settings - Fork 145
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't gone through the tests yet.
a | ||
for a in current_epoch_attestations | ||
if a.data.shard == shard | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
39597e3
to
c780e68
Compare
@hwwhww Rebased and updated with PR feedbacks. |
total_attesting_balance = sum( | ||
get_effective_balance(state.validator_balances, i, config.MAX_DEPOSIT) | ||
for i in attesting_validators_indices | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
total_attesting_balance
was calculated in get_winning_root
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it was.
Now optimized by returning winning_balance
along with winning_root
in get_winning_root
so we don't have to calculate the attesting balance of the winning_root
again.
epoch_length=config.EPOCH_LENGTH, | ||
target_committee_size=config.TARGET_COMMITTEE_SIZE, | ||
shard_count=config.SHARD_COUNT, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
attesting_validators_indices
was calculated in get_winning_root
?
eth2/beacon/helpers.py
Outdated
winning_root_balance = 0 | ||
visited_shard_block_root: List[Hash32] = [] | ||
for a in attestations: | ||
if a.data.shard_block_root in visited_shard_block_root: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of casting to set inline in this conditional which will make a full cop of the object see my other comment about just using a set
for this data structure.
state=state, | ||
shard=shard, | ||
attestations=_get_attestations_by_shard(current_epoch_attestations, shard), | ||
epoch_length=config.EPOCH_LENGTH, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not for this PR but an idea I've had a few times.
I think it is good that each of these nice functional utilities takes only the set of config values that it needs. This helps make it easy to see what config values each function depends on and keeps the implementations decoupled from the config object.
however, it's also really unfortunate how verbose some of this code ends up because you end up having to explicitly provide each parameter...
So I was thinking about an API that allowed you to take a config
and auto-magically partial out all of these helper functions. Here's some pseudo code to illustrate.
class BeaconHelperAPI:
def __init__(self, config):
# option 1: do it dynamically in constructor (con: hard to maintain type safety)
self.get_attesting_validator_indices = functools.partial(
get_attesting_validator_indices,
epoch_length=config.EPOCH_LENGTH ,
...
)
def get_attesting_validator_indices(state, attestations, shard, shard_block_root):
# option 2: mirror the API (con: heavy on boilerplate, duplicates all function signatures)
return get_attesting_validator_indices(..., epoch_length=self.config.EPOCH_LENGTH, ...)
I'm sure there are some other options for how it could be implemented as well. Here's an overly clever one that could be really good if done well but also might be too clever and end up bad.
class Wrapper:
def __init__(self, fn):
self.fn = fn
def __call__(self, *args, **kwargs):
return fn(*args, **kwargs)
def partial(self, config):
kwargs = {kw: getattr(config, kw.upper()) for kw in keywords_to_partial}
return functools.partial(self.fn, **kwargs)
def configurable(*keywords_to_partial):
def outer(fn):
return Wrapper(fn)
return outer
@configurable('epoch_length', 'target_committee_size', 'shard_count')
def get_attesting_validator_indices(stat, attestations, shard, shard_block_root, epoch_length, target_committee_size, shard_count):
...
# using it in code
get_attestating_validator_indices.partial(config)(state, attestations, shard, shard_block_root)
Anyways, some ideas on how you might make calling these helpers a little less cumbersome, but I'd advocate for ensuring that whatever solution you end up using, that you ensure that you maintain type safety.
cc @jannikluhn @hwwhww @ralexstokes (and anyone else I'm forgetting).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Something like this would be really cool. However, I'm not sure if type safety can be kept, because we have to dynamically extract arguments from the config tuple (and, according to a quick google search, mypy doesn't seem to understand decorators if they change the function signature.
for i in crosslink_committee | ||
) | ||
if 3 * total_attesting_balance >= 2 * total_balance: | ||
latest_crosslinks = update_tuple_item( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using a new variable name like updated_latest_crosslinks
(and you can just do updated_latest_crosslinks = latest_crosslinks
in an else
clause) as to not overwrite the local variable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We only update the state.latest_crosslinks
all at once at the end of the function:
state = state.copy(
latest_crosslinks= updated_latest_crosslinks,
)
so this updated_latest_crosslinks
must have all the updated crosslinks and it would be equivalent to just using latest_crosslinks
.
1464088
to
3310c95
Compare
@hwwhww Rebased and apply the PR feedbacks. Now working on the |
docstring, format, naming, turn mutable into tuple, relocate functions, remove inner list declaration in `sum`
add comment, f-string format, make calculating attesting balance a helper function, raise exception when no winning root found
2746c14
to
41ed7b4
Compare
tests/eth2/beacon/state_machines/forks/test_serenity_epoch_processing.py
Outdated
Show resolved
Hide resolved
tests/eth2/beacon/state_machines/forks/test_serenity_epoch_processing.py
Outdated
Show resolved
Hide resolved
@djrtwo I'm going ahead to merge this. Please let me know if there's anything still needs to be corrected and I will fix them in another PR. |
looks good! |
What was wrong?
Fix #147
How was it fixed?
NOTE: Epoch transition rule is still subject to change in ethereum/consensus-specs#492.
get_current_epoch_attestations
/get_previous_epoch_attestations
get_attesting_validator_indices
get_winning_root
process_crosslinks
process_crosslinks
get_attestation_participants
Cute Animal Picture