def get_eth2_details( beaconchain: 'BeaconChain', addresses: List[ChecksumEthAddress], ) -> List[ValidatorDetails]: """Go through the list of eth1 addresses and find all eth2 validators associated with them along with their details. Also returns the daily stats for each validator. May raise RemoteError due to beaconcha.in API""" indices = [] index_to_address = {} index_to_pubkey = {} assert beaconchain.db is not None, 'Beaconchain db should be populated' # and for each address get the validator info (to get the index) -- this could be avoided for address in addresses: validators = beaconchain.get_eth1_address_validators(address) for validator in validators: index_to_address[validator.validator_index] = address index_to_pubkey[validator.validator_index] = validator.public_key indices.append(validator.validator_index) # Get current balance of all validator indices result = [] performance_result = beaconchain.get_performance(list(indices)) for validator_index, entry in performance_result.items(): stats = get_validator_daily_stats( db=beaconchain.db, validator_index=validator_index, msg_aggregator=beaconchain.msg_aggregator, ) result.append(ValidatorDetails( validator_index=validator_index, public_key=index_to_pubkey[validator_index], eth1_depositor=index_to_address[validator_index], performance=entry, daily_stats=stats, )) # The performance call does not return validators that are not active and are still depositing depositing_indices = set(index_to_address.keys()) - set(performance_result.keys()) for index in depositing_indices: stats = get_validator_daily_stats( db=beaconchain.db, validator_index=index, msg_aggregator=beaconchain.msg_aggregator, ) result.append(ValidatorDetails( validator_index=index, public_key=index_to_pubkey[index], eth1_depositor=index_to_address[index], performance=DEPOSITING_VALIDATOR_PERFORMANCE, daily_stats=stats, )) return result
def test_get_eth2_details_validator_not_yet_active(beaconchain, inquirer, price_historian): # pylint: disable=unused-argument # noqa: E501 """Test that if a validator is detected but is not yet active the balance is shown properly Test for: https://github.com/rotki/rotki/issues/1888 """ with _create_beacon_mock(beaconchain): details = get_eth2_details(beaconchain=beaconchain, addresses=[ADDR1]) for idx in range(0, 2): # basic check about daily stats but then delete since this is not what this test checks assert len(details[idx].daily_stats) > 0 assert details[idx].daily_stats[0].timestamp < details[ idx].daily_stats[-1].timestamp details[idx] = details[idx]._replace(daily_stats=[]) expected_details = [ ValidatorDetails( validator_index=9, public_key= '0xb016e31f633a21fbe42a015152399361184f1e2c0803d89823c224994af74a561c4ad8cfc94b18781d589d03e952cd5b', # noqa: E501 eth1_depositor=ADDR1, performance=ValidatorPerformance( balance=32143716247, performance_1d=14437802, performance_1w=105960750, performance_1m=143716247, performance_1y=143716247, ), daily_stats=[], ), ValidatorDetails( validator_index=1507, public_key= '0x8b242e5cdb0a7740a605f3c39262253eb2b5e7ee514a544e823f996e8de9961db7d5264d08c5ce13a65efa82b868accc', # noqa: E501 eth1_depositor=ADDR1, performance=DEPOSITING_VALIDATOR_PERFORMANCE, daily_stats=[], ), ] assert details == expected_details
def get_details( self, addresses: List[ChecksumEthAddress], ) -> List[ValidatorDetails]: """Go through the list of eth1 addresses and find all eth2 validators associated with them along with their details. Also returns the daily stats for each validator. May raise RemoteError due to beaconcha.in API""" indices = [] index_to_address = {} index_to_pubkey = {} result = [] assert self.beaconchain.db is not None, 'Beaconchain db should be populated' for address in addresses: validators = self.beaconchain.get_eth1_address_validators(address) for validator in validators: if validator.validator_index is None: # for validators that are so early in the depositing queue that no # validator index is confirmed yet let's return only the most basic info result.append( ValidatorDetails( validator_index=None, public_key=validator.public_key, eth1_depositor=address, performance=DEPOSITING_VALIDATOR_PERFORMANCE, daily_stats=[], )) continue index_to_address[validator.validator_index] = address index_to_pubkey[ validator.validator_index] = validator.public_key indices.append(validator.validator_index) # Get current balance of all validator indices performance_result = self.beaconchain.get_performance(list(indices)) for validator_index, entry in performance_result.items(): stats = self.get_validator_daily_stats( validator_index=validator_index, msg_aggregator=self.msg_aggregator, ) result.append( ValidatorDetails( validator_index=validator_index, public_key=index_to_pubkey[validator_index], eth1_depositor=index_to_address[validator_index], performance=entry, daily_stats=stats, )) # Performance call does not return validators that are not active and are still depositing depositing_indices = set(index_to_address.keys()) - set( performance_result.keys()) for index in depositing_indices: stats = self.get_validator_daily_stats( validator_index=index, msg_aggregator=self.msg_aggregator, ) result.append( ValidatorDetails( validator_index=index, public_key=index_to_pubkey[index], eth1_depositor=index_to_address[index], performance=DEPOSITING_VALIDATOR_PERFORMANCE, daily_stats=stats, )) return result
def get_details( self, addresses: List[ChecksumEthAddress], ) -> List[ValidatorDetails]: """Go through the list of eth1 addresses and find all eth2 validators associated with them along with their details. May raise RemoteError due to beaconcha.in API""" indices = [] index_to_address = {} index_to_pubkey = {} pubkey_to_index = {} result = [] assert self.beaconchain.db is not None, 'Beaconchain db should be populated' address_validators = [] for address in addresses: validators = self.beaconchain.get_eth1_address_validators(address) for validator in validators: if validator.index is None: # for validators that are so early in the depositing queue that no # validator index is confirmed yet let's return only the most basic info result.append( ValidatorDetails( validator_index=None, public_key=validator.public_key, eth1_depositor=address, performance=DEPOSITING_VALIDATOR_PERFORMANCE, )) continue index_to_address[validator.index] = address address_validators.append( Eth2Validator(index=validator.index, public_key=validator.public_key, ownership_proportion=ONE)) # noqa: E501 # make sure all validators we deal with are saved in the DB dbeth2 = DBEth2(self.database) dbeth2.add_validators(address_validators) # Also get all manually input validators all_validators = dbeth2.get_validators() saved_deposits = dbeth2.get_eth2_deposits() pubkey_to_deposit = {x.pubkey: x for x in saved_deposits} validators_to_query_for_deposits = [] for v in all_validators: index_to_pubkey[v.index] = v.public_key pubkey_to_index[v.public_key] = v.index indices.append(v.index) depositor = index_to_address.get(v.index) if depositor is None: if v.public_key not in pubkey_to_deposit: validators_to_query_for_deposits.append(v.public_key) # Get new deposits if needed, and populate index_to_address new_deposits = self._query_and_save_deposits( dbeth2, validators_to_query_for_deposits) for deposit in saved_deposits + new_deposits: index = pubkey_to_index.get(Eth2PubKey(deposit.pubkey)) if index is None: # should never happen, unless returned data is off log.error( f'At eth2 staking details could not find index for pubkey ' f'{deposit.pubkey} at deposit {deposit}.', ) continue index_to_address[index] = deposit.from_address # Get current balance of all validator indices performance_result = self.beaconchain.get_performance(indices) for validator_index, entry in performance_result.items(): depositor = index_to_address.get(validator_index) if depositor is None: # should never happen, unless returned data is off log.error( f'At eth2 staking details could not find depositor for index ' f'{validator_index} at index_to_address', ) continue result.append( ValidatorDetails( validator_index=validator_index, public_key=index_to_pubkey[validator_index], eth1_depositor=depositor, performance=entry, )) # Performance call does not return validators that are not active and are still depositing depositing_indices = set(index_to_address.keys()) - set( performance_result.keys()) for index in depositing_indices: depositor = index_to_address.get(index) if depositor is None: # should never happen, unless returned data is off log.error( f'At eth2 staking details could not find depositor for index ' f'{index} at index_to_address for depositing indices', ) continue result.append( ValidatorDetails( validator_index=index, public_key=index_to_pubkey[index], eth1_depositor=depositor, performance=DEPOSITING_VALIDATOR_PERFORMANCE, )) return result