Heze -- Inclusion List
Note: This document is a work-in-progress for researchers and implementers.
Introduction
These are the inclusion list specifications to implement Heze.
Containers
New containers
InclusionListEntry
| @dataclass(eq=True, frozen=True)
class InclusionListEntry:
signed_inclusion_list: SignedInclusionList
is_timely: Boolean
|
InclusionListStore
| @dataclass
class InclusionListStore:
inclusion_lists: DefaultDict[Root, Dict[Root, InclusionListEntry]] = field(
default_factory=lambda: defaultdict(dict)
)
equivocators: DefaultDict[Root, Set[ValidatorIndex]] = field(
default_factory=lambda: defaultdict(set)
)
|
Helpers
New get_inclusion_list_store
| def get_inclusion_list_store() -> InclusionListStore:
# `cached_or_new_inclusion_list_store` is implementation and context dependent.
# It returns the cached `InclusionListStore`; if none exists,
# it initializes a new instance, caches it and returns it.
inclusion_list_store = cached_or_new_inclusion_list_store()
return inclusion_list_store
|
New process_inclusion_list
| def process_inclusion_list(
store: InclusionListStore,
signed_inclusion_list: SignedInclusionList,
inclusion_list_committee_root: Root,
is_timely: bool,
) -> None:
inclusion_list = signed_inclusion_list.message
key = inclusion_list_committee_root
# Ignore an inclusion list that has already been stored
inclusion_list_root = hash_tree_root(inclusion_list)
if inclusion_list_root in store.inclusion_lists[key]:
return
# Ignore inclusion lists from equivocators
if inclusion_list.validator_index in store.equivocators[key]:
return
# Mark the validator as an equivocator if it published a different inclusion list
for inclusion_list_entry in store.inclusion_lists[key].values():
stored_message = inclusion_list_entry.signed_inclusion_list.message
if stored_message.validator_index == inclusion_list.validator_index:
store.equivocators[key].add(inclusion_list.validator_index)
return
# Store the signed inclusion list and its timeliness
store.inclusion_lists[key][inclusion_list_root] = InclusionListEntry(
signed_inclusion_list=signed_inclusion_list,
is_timely=is_timely,
)
|
New get_inclusion_list_transactions
Note: get_inclusion_list_transactions returns a list of unique transactions
from all valid and non-equivocating InclusionLists for the given slot and for
which the inclusion_list_committee_root compatible with the dependent_root
in the InclusionList matches the one calculated from the given state. When
only_timely is True, only InclusionLists received in a timely manner on
the p2p network are considered; otherwise, timeliness is not considered.
Note: Inclusion lists MUST be retained for at least
MIN_SLOTS_FOR_INCLUSION_LISTS_REQUESTS slots beyond their slot, after which
they MAY be pruned.
| def get_inclusion_list_transactions(
store: InclusionListStore, state: BeaconState, slot: Slot, only_timely: bool = True
) -> Sequence[Transaction]:
committee = get_inclusion_list_committee(state, slot)
key = hash_tree_root(committee)
inclusion_lists = store.inclusion_lists[key]
equivocators = store.equivocators[key]
transactions: list[Transaction] = []
for inclusion_list_entry in inclusion_lists.values():
inclusion_list = inclusion_list_entry.signed_inclusion_list.message
# Ignore inclusion lists from equivocators
if inclusion_list.validator_index in equivocators:
continue
# Ignore untimely inclusion lists if only timely ones are requested
if only_timely and not inclusion_list_entry.is_timely:
continue
transactions.extend(inclusion_list.transactions)
# Deduplicate inclusion list transactions. Order does not need to be preserved.
return list(set(transactions))
|
New get_inclusion_list_bits
| def get_inclusion_list_bits(
store: InclusionListStore, state: BeaconState, slot: Slot, only_timely: bool = True
) -> InclusionListBits:
"""
Return a ``BitVector`` over inclusion list committee indices with bits set
for those who provided valid, non-equivocating inclusion lists for the given ``slot``.
"""
committee = get_inclusion_list_committee(state, slot)
key = hash_tree_root(committee)
inclusion_lists = store.inclusion_lists[key]
equivocators = store.equivocators[key]
validator_indices = []
for inclusion_list_entry in inclusion_lists.values():
inclusion_list = inclusion_list_entry.signed_inclusion_list.message
# Ignore inclusion lists from equivocators
if inclusion_list.validator_index in equivocators:
continue
# Ignore untimely inclusion lists if only timely ones are requested
if only_timely and not inclusion_list_entry.is_timely:
continue
validator_indices.append(inclusion_list.validator_index)
return InclusionListBits(validator_index in validator_indices for validator_index in committee)
|
New is_inclusion_list_bits_inclusive
| def is_inclusion_list_bits_inclusive(
store: InclusionListStore,
state: BeaconState,
slot: Slot,
inclusion_list_bits: InclusionListBits,
only_timely: bool = True,
) -> bool:
"""
Return ``True`` if and only if ``inclusion_list_bits`` has a bit set for
every bit set in the local inclusion list bits for the given ``slot``.
"""
local_inclusion_list_bits = get_inclusion_list_bits(store, state, slot, only_timely)
for i in range(INCLUSION_LIST_COMMITTEE_SIZE):
if local_inclusion_list_bits[i] and not inclusion_list_bits[i]:
return False
return True
|