mirror of https://github.com/nucypher/nucypher.git
commit
b5af1e2b8d
|
@ -0,0 +1 @@
|
|||
Allow staking providers to be filtered/sampled based on an expectation for staking duration.
|
|
@ -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
|
||||
|
|
|
@ -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({})
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
[
|
||||
|
|
Loading…
Reference in New Issue