Merge pull request #3467 from derekpierre/duration-sampling

Duration Sampling
v7.3.x
KPrasch 2024-04-29 16:31:39 +02:00 committed by GitHub
commit b5af1e2b8d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 89 additions and 36 deletions

View File

@ -0,0 +1 @@
Allow staking providers to be filtered/sampled based on an expectation for staking duration.

View File

@ -240,7 +240,7 @@ class StakerSamplingApplicationAgent(EthereumContractAgent):
@abstractmethod
def _get_active_staking_providers_raw(
self, start_index: int, max_results: int
self, start_index: int, max_results: int, duration: int
) -> Tuple[int, List[bytes]]:
raise NotImplementedError
@ -249,21 +249,21 @@ class StakerSamplingApplicationAgent(EthereumContractAgent):
raise NotImplementedError
def get_all_active_staking_providers(
self, pagination_size: Optional[int] = None
self, pagination_size: Optional[int] = None, duration: int = 0
) -> Tuple[types.TuNits, Dict[ChecksumAddress, types.TuNits]]:
n_tokens, staking_providers = self._get_active_stakers(
pagination_size=pagination_size
pagination_size=pagination_size, duration=duration
)
return n_tokens, staking_providers
@contract_api(CONTRACT_CALL)
def get_active_staking_providers(
self, start_index: int, max_results: int
self, start_index: int, max_results: int, duration: int = 0
) -> Tuple[types.TuNits, Dict[ChecksumAddress, types.TuNits]]:
(
total_authorized_tokens,
staking_providers_info,
) = self._get_active_staking_providers_raw(start_index, max_results)
) = self._get_active_staking_providers_raw(start_index, max_results, duration)
staking_providers = self._process_active_staker_info(staking_providers_info)
return types.TuNits(total_authorized_tokens), staking_providers
@ -272,10 +272,11 @@ class StakerSamplingApplicationAgent(EthereumContractAgent):
self,
without: Iterable[ChecksumAddress] = None,
pagination_size: Optional[int] = None,
duration: int = 0,
) -> "StakingProvidersReservoir":
# pagination_size = pagination_size or self.get_staking_providers_population()
n_tokens, stake_provider_map = self.get_all_active_staking_providers(
pagination_size=pagination_size
pagination_size=pagination_size, duration=duration
)
if n_tokens == 0:
@ -309,7 +310,9 @@ class StakerSamplingApplicationAgent(EthereumContractAgent):
return staking_providers
def _get_active_stakers(self, pagination_size: Optional[int] = None):
def _get_active_stakers(
self, pagination_size: Optional[int] = None, duration: int = 0
):
if pagination_size is None:
pagination_size = (
self.DEFAULT_PROVIDERS_PAGINATION_SIZE_LIGHT_NODE
@ -332,7 +335,9 @@ class StakerSamplingApplicationAgent(EthereumContractAgent):
(
batch_authorized_tokens,
batch_staking_providers,
) = self.get_active_staking_providers(start_index, pagination_size)
) = self.get_active_staking_providers(
start_index, pagination_size, duration
)
except Exception as e:
if "timeout" not in str(e):
# exception unrelated to pagination size and timeout
@ -428,7 +433,7 @@ class TACoChildApplicationAgent(StakerSamplingApplicationAgent):
@contract_api(CONTRACT_CALL)
def _get_active_staking_providers_raw(
self, start_index: int, max_results: int
self, start_index: int, max_results: int, duration: int
) -> Tuple[int, List[bytes]]:
get_active_providers_overloaded_function = (
self.contract.get_function_by_signature(
@ -436,7 +441,7 @@ class TACoChildApplicationAgent(StakerSamplingApplicationAgent):
)
)
active_staking_providers_info = get_active_providers_overloaded_function(
start_index, max_results, 0 # TODO address via #3458
start_index, max_results, duration
).call()
return active_staking_providers_info
@ -526,11 +531,11 @@ class TACoApplicationAgent(StakerSamplingApplicationAgent):
@contract_api(CONTRACT_CALL)
def _get_active_staking_providers_raw(
self, start_index: int, max_results: int
self, start_index: int, max_results: int, duration: int
) -> Tuple[int, List[bytes]]:
active_staking_providers_info = (
self.contract.functions.getActiveStakingProviders(
start_index, max_results, 0 # TODO address via #3458
start_index, max_results, duration
).call()
)
return active_staking_providers_info

View File

@ -13,6 +13,7 @@ def make_staking_provider_reservoir(
exclude_addresses: Optional[Iterable[ChecksumAddress]] = None,
include_addresses: Optional[Iterable[ChecksumAddress]] = None,
pagination_size: Optional[int] = None,
duration: Optional[int] = 0,
):
"""Get a sampler object containing the currently registered staking providers."""
@ -21,7 +22,9 @@ def make_staking_provider_reservoir(
include_addresses = include_addresses or ()
without_set = set(include_addresses) | set(exclude_addresses or ())
try:
reservoir = application_agent.get_staking_provider_reservoir(without=without_set, pagination_size=pagination_size)
reservoir = application_agent.get_staking_provider_reservoir(
without=without_set, pagination_size=pagination_size, duration=duration
)
except StakerSamplingApplicationAgent.NotEnoughStakingProviders:
# TODO: do that in `get_staking_provider_reservoir()`?
reservoir = StakingProvidersReservoir({})

View File

@ -16,7 +16,6 @@ from nucypher.crypto.powers import TransactingPower
def test_sampling_distribution(
testerchain,
taco_application_agent,
taco_child_application_agent, # TODO undo and fix as part of #3458
threshold_staking,
coordinator_agent,
deployer_account,
@ -62,7 +61,7 @@ def test_sampling_distribution(
sampled, failed = 0, 0
while sampled < SAMPLES:
try:
reservoir = taco_child_application_agent.get_staking_provider_reservoir()
reservoir = taco_application_agent.get_staking_provider_reservoir()
addresses = set(reservoir.draw(quantity))
addresses.discard(NULL_ADDRESS)
except taco_application_agent.NotEnoughStakingProviders:

View File

@ -2,7 +2,6 @@ import random
import pytest
from nucypher.blockchain.eth.agents import TACoApplicationAgent
from nucypher.blockchain.eth.constants import NULL_ADDRESS
from nucypher.blockchain.eth.signers.software import Web3Signer
from nucypher.crypto.powers import TransactingPower
@ -87,10 +86,11 @@ def test_get_staker_population(taco_application_agent, staking_providers):
)
# TODO #3458
@pytest.mark.skip("Skip until issue #3458 is addressed")
@pytest.mark.usefixtures("staking_providers", "ursulas")
def test_sample_staking_providers(taco_application_agent):
@pytest.mark.parametrize(
"duration", [0, 60 * 60 * 24, 60 * 60 * 24 * 182, 60 * 60 * 24 * 365]
)
def test_sample_staking_providers(taco_application_agent, duration):
all_staking_providers = list(taco_application_agent.get_staking_providers())
providers_population = taco_application_agent.get_staking_providers_population()
@ -101,13 +101,15 @@ def test_sample_staking_providers(taco_application_agent):
providers_population + 1
) # One more than we have deployed
providers = taco_application_agent.get_staking_provider_reservoir().draw(3)
providers = taco_application_agent.get_staking_provider_reservoir(
duration=duration
).draw(3)
assert len(providers) == 3 # Three...
assert len(set(providers)) == 3 # ...unique addresses
# Same but with pagination
providers = taco_application_agent.get_staking_provider_reservoir(
pagination_size=1
pagination_size=1, duration=duration
).draw(3)
assert len(providers) == 3
assert len(set(providers)) == 3
@ -116,7 +118,9 @@ def test_sample_staking_providers(taco_application_agent):
# repeat for opposite blockchain light setting
light = taco_application_agent.blockchain.is_light
taco_application_agent.blockchain.is_light = not light
providers = taco_application_agent.get_staking_provider_reservoir().draw(3)
providers = taco_application_agent.get_staking_provider_reservoir(
duration=duration
).draw(3)
assert len(providers) == 3
assert len(set(providers)) == 3
assert len(set(providers).intersection(all_staking_providers)) == 3
@ -134,17 +138,19 @@ def test_sample_staking_providers(taco_application_agent):
def test_get_staking_provider_info(
testerchain, taco_application_agent, get_random_checksum_address
taco_application_agent, ursulas, get_random_checksum_address
):
staking_provider_account, operator_account, *other = testerchain.unassigned_accounts
info: TACoApplicationAgent.StakingProviderInfo = (
taco_application_agent.get_staking_provider_info(
staking_provider=staking_provider_account
)
# existing staker
staking_provider, operator_address = (
ursulas[0].checksum_address,
ursulas[0].operator_address,
)
info = taco_application_agent.get_staking_provider_info(
staking_provider=staking_provider
)
assert info.operator_start_timestamp > 0
assert info.operator == operator_account
assert info.operator_confirmed is False
assert info.operator == operator_address
assert info.operator_confirmed is True
# non-existent staker
info = taco_application_agent.get_staking_provider_info(

View File

@ -101,7 +101,10 @@ def test_get_staker_population(taco_child_application_agent, staking_providers):
@pytest.mark.usefixtures("staking_providers", "ursulas")
def test_sample_staking_providers(taco_child_application_agent):
@pytest.mark.parametrize(
"duration", [0, 60 * 60 * 24, 60 * 60 * 24 * 182, 60 * 60 * 24 * 365]
)
def test_sample_staking_providers(taco_child_application_agent, duration):
all_staking_providers = list(taco_child_application_agent.get_staking_providers())
providers_population = (
taco_child_application_agent.get_staking_providers_population()
@ -110,18 +113,22 @@ def test_sample_staking_providers(taco_child_application_agent):
assert len(all_staking_providers) == providers_population
with pytest.raises(taco_child_application_agent.NotEnoughStakingProviders):
taco_child_application_agent.get_staking_provider_reservoir().draw(
taco_child_application_agent.get_staking_provider_reservoir(
duration=duration
).draw(
providers_population + 1
) # One more than we have deployed
providers = taco_child_application_agent.get_staking_provider_reservoir().draw(3)
providers = taco_child_application_agent.get_staking_provider_reservoir(
duration=duration
).draw(3)
assert len(providers) == 3 # Three...
assert len(set(providers)) == 3 # ...unique addresses
assert len(set(providers).intersection(all_staking_providers)) == 3
# Same but with pagination
providers = taco_child_application_agent.get_staking_provider_reservoir(
pagination_size=1
pagination_size=1, duration=duration
).draw(3)
assert len(providers) == 3
assert len(set(providers)) == 3
@ -130,7 +137,9 @@ def test_sample_staking_providers(taco_child_application_agent):
# repeat for opposite blockchain light setting
light = taco_child_application_agent.blockchain.is_light
taco_child_application_agent.blockchain.is_light = not light
providers = taco_child_application_agent.get_staking_provider_reservoir().draw(3)
providers = taco_child_application_agent.get_staking_provider_reservoir(
duration=duration
).draw(3)
assert len(providers) == 3
assert len(set(providers)) == 3
assert len(set(providers).intersection(all_staking_providers)) == 3
@ -145,3 +154,33 @@ def test_sample_staking_providers(taco_child_application_agent):
assert len(set(providers)) == 3
assert len(set(providers).intersection(all_staking_providers)) == 3
assert len(set(providers).intersection(exclude_providers)) == 0
def test_get_staking_provider_info(
taco_child_application_agent, ursulas, get_random_checksum_address
):
# existing staker
staking_provider, operator_address = (
ursulas[0].checksum_address,
ursulas[0].operator_address,
)
info = taco_child_application_agent.staking_provider_info(
staking_provider=staking_provider
)
assert info.operator == operator_address
assert info.authorized > taco_child_application_agent.get_min_authorization()
assert info.operator_confirmed is True
assert info.index == 1
assert info.deauthorizing == 0
assert info.end_deauthorization == 0
# non-existent staker
info = taco_child_application_agent.staking_provider_info(
get_random_checksum_address()
)
assert info.operator == NULL_ADDRESS
assert info.authorized == 0
assert info.operator_confirmed is False
assert info.index == 0
assert info.deauthorizing == 0
assert info.end_deauthorization == 0

View File

@ -801,7 +801,7 @@ def test_contract_condition_using_overloaded_function(
(
total_staked,
providers,
) = taco_child_application_agent._get_active_staking_providers_raw(0, 10)
) = taco_child_application_agent._get_active_staking_providers_raw(0, 10, 0)
expected_result = [
total_staked,
[