def register_owner_account(token: Token): if Wallet.objects.filter(token=token, address__iexact=remove_0x_prefix(settings.HUB_OWNER_ACCOUNT_ADDRESS)).exists(): logger.error('Owner account already registered.') return if not LocalViewInterface.get_contract_parameters(): logger.error('Contract parameters not yet populated.') return logger.warning('Registering owner account: {}'.format( settings.HUB_OWNER_ACCOUNT_ADDRESS)) latest_eon_number = LocalViewInterface.latest().eon_number() authorization_digest = Wallet( token=token, address=settings.HUB_OWNER_ACCOUNT_ADDRESS).get_admission_hash(latest_eon_number) authorization = sign_message( authorization_digest, settings.HUB_OWNER_ACCOUNT_KEY) latest_tos_config = TOSConfig.objects.all().order_by('time').last() tos_signature = sign_message( hex_value(latest_tos_config.digest()), settings.HUB_OWNER_ACCOUNT_KEY) registration = AdmissionRequestSerializer(data={ 'token': token.address, 'address': remove_0x_prefix(settings.HUB_OWNER_ACCOUNT_ADDRESS), 'authorization': { 'value': encode_signature(authorization) }, 'tos_signature': { 'value': encode_signature(tos_signature) } }) registration.is_valid(raise_exception=True) registration.save() process_admissions()
def get_last_checkpoint(self, block_identifier='latest'): eon_number = self.get_last_checkpoint_submission_eon() eons_kept = LocalViewInterface.get_contract_parameters().eons_kept return self.contract\ .functions\ .getCheckpointAtSlot(eon_number % eons_kept)\ .call(block_identifier=block_identifier)
def register_owner_account(token: Token): if Wallet.objects.filter(token=token, address=remove_0x_prefix( settings.HUB_OWNER_ACCOUNT_ADDRESS)).exists(): return if not LocalViewInterface.get_contract_parameters(): return latest_eon_number = LocalViewInterface.latest().eon_number() authorization_digest = Wallet( token=token, address=settings.HUB_OWNER_ACCOUNT_ADDRESS).get_admission_hash( latest_eon_number) authorization = sign_message(authorization_digest, settings.HUB_OWNER_ACCOUNT_KEY) latest_tos_config = TOSConfig.objects.all().order_by('time').last() tos_signature = sign_message(decode_hex(latest_tos_config.digest()), settings.HUB_OWNER_ACCOUNT_KEY) registration = AdmissionRequestSerializer( data={ 'token': token.address, 'address': remove_0x_prefix(settings.HUB_OWNER_ACCOUNT_ADDRESS), 'authorization': { 'value': encode_signature(authorization) }, 'tos_signature': { 'value': encode_signature(tos_signature) } }) registration.is_valid(raise_exception=True) registration.save()
def process_admissions(): if not LocalViewInterface.get_contract_parameters(): logger.error('Contract parameters not yet populated.') return # This lock is needed because new wallets can be introduced, which would affect the checkpoint. with RootCommitment.global_lock(): process_admissions_for_latest_eon()
def get_deposits(self, token_address, eon_number, block_identifier='latest'): eons_kept = LocalViewInterface.get_contract_parameters().eons_kept aggregate_eon, aggregate_value = self.contract\ .functions\ .getDepositsAtSlot(add_0x_prefix(token_address), eon_number % eons_kept)\ .call(block_identifier=block_identifier) return aggregate_value if aggregate_eon == eon_number else 0
def cancel_finalize_swaps(): if not LocalViewInterface.get_contract_parameters(): logger.error('Contract parameters not yet populated.') return latest_eon_number = LocalViewInterface.latest().eon_number() # This lock is required because the ledger will be mutated as the swaps are processed with RootCommitment.global_lock(): cancel_finalize_swaps_for_eon(latest_eon_number)
def process_passive_transfers(): logger.info('Processing passive transfers') if not LocalViewInterface.get_contract_parameters(): logger.error('Contract parameters not yet populated.') return latest_eon_number = LocalViewInterface.latest().eon_number() # This lock is required because the ledger will be mutated as the transfers are processed with RootCommitment.global_lock(): logger.info('Start') process_passive_transfers_for_eon(latest_eon_number)
def create_checkpoint_for_eon(eon_number, latest_block_number): if RootCommitment.objects.filter(eon_number=eon_number).count() > 0: return False if eon_number > 1: last_eon_number = eon_number - 1 last_eon = LocalViewInterface.confirmed(eon_number=last_eon_number) if not last_eon: logger.error( 'Missing confirmed contract state for eon {}.'.format(last_eon_number)) send_admin_email( subject='Soft Checkpoint Error: Missing Contract State', content='Missing confirmed contract state for previous eon {}. We may not be in sync with the blockchain!'.format(last_eon_number)) return False last_confirmed_eon_number, last_confirmed_sub_block = last_eon.eon_number_and_sub_block() if last_confirmed_eon_number != last_eon_number: logger.error( 'Attempted to use confirmed state for eon {}. Expected {}.'.format(last_confirmed_eon_number, last_eon_number)) send_admin_email( subject='Soft Checkpoint Error: Wrong last eon #', content='Need to sync chain! Attempted to use confirmed state for eon {}. Expected {}!' .format(last_confirmed_eon_number, last_eon_number)) return False last_sub_block_number = LocalViewInterface.get_contract_parameters().blocks_per_eon - 1 if last_confirmed_sub_block != last_sub_block_number: logger.error( 'Attempted to use confirmed state for sub block {}. Expected {}.'.format(last_confirmed_sub_block, last_sub_block_number)) send_admin_email( subject='Soft Checkpoint Error: Wrong last Sub block #', content='Need to sync chain! Attempted to use confirmed state for sub block {}. Expected {}.' .format(last_confirmed_sub_block, last_sub_block_number)) return False # commitment write read lock makes sure transaction confirmation will not mutate ledger while checkpoint is being created with transaction.atomic(), RootCommitment.read_write_lock(suffix=eon_number-1, is_write=True, auto_renewal=True): # TODO parallelism token_commitments = [create_token_commitment_for_eon(token, eon_number) for token in Token.objects.all().order_by('trail')] root_commitment = create_root_commitment_for_eon( token_commitments, eon_number, latest_block_number) NOCUSTContractInterface().queue_submit_checkpoint(root_commitment) return True
def synchronize_contract_state(verbose=False): logger.info('Retrieving confirmed events') if not LocalViewInterface.get_contract_parameters(): logger.error('Contract parameters not yet populated.') return with ContractState.global_lock(): logger.info('Start..') contract_interface = NOCUSTContractInterface() logger.info('Interface acquired') contract_event_decoder = NOCUSTContractEventDecoder() logger.info('Decoder acquired') contract_interface.get_blocks_per_eon() return concurrently_retrieve_state(contract_interface, contract_event_decoder, verbose)
def create_checkpoint(): if not LocalViewInterface.get_contract_parameters(): logger.error('Contract parameters not yet populated.') return latest = LocalViewInterface.latest() latest_eon_number, latest_sub_block = latest.eon_number_and_sub_block() blocks_for_creation = LocalViewInterface.blocks_for_creation() confirmed_eon_number, confirmed_sub_block = LocalViewInterface.confirmed( ).eon_number_and_sub_block() if confirmed_eon_number < latest_eon_number: return if latest_sub_block < blocks_for_creation: return with RootCommitment.global_lock(): new_checkpoint = create_checkpoint_for_eon(latest_eon_number, latest.block) if new_checkpoint: operator_celery.send_task('auditor.tasks.broadcast_wallet_data')
def register_sla_recipient_account(): if same_hex_value(settings.SLA_RECIPIENT_ADDRESS, settings.HUB_OWNER_ACCOUNT_ADDRESS): logger.warning('Skipping registration: Hub Owner is SLA recipient.') return token = Token.objects.filter( address__iexact=remove_0x_prefix(settings.SLA_TOKEN_ADDRESS)) if not token.exists(): logger.error('SLA Payment Token not yet registered.') return if Wallet.objects.filter(token=token, address__iexact=remove_0x_prefix( settings.SLA_RECIPIENT_ADDRESS)).exists(): logger.error('Recipient account already registered.') return if not LocalViewInterface.get_contract_parameters(): logger.error('Contract parameters not yet populated.') return latest_eon = LocalViewInterface.latest().eon_number() authorization_digest = Wallet( token=token, address=settings.SLA_RECIPIENT_ADDRESS).get_admission_hash(latest_eon) authorization = sign_message(authorization_digest, settings.SLA_RECIPIENT_KEY) registration = AdmissionRequestSerializer( data={ 'token': token.address, 'address': remove_0x_prefix(settings.SLA_RECIPIENT_ADDRESS), 'authorization': encode_signature(authorization) }) registration.is_valid(raise_exception=True) registration.save() process_admissions()
def register_eth_token(): if Token.objects.filter(address__iexact=remove_0x_prefix( settings.HUB_LQD_CONTRACT_ADDRESS)).exists(): logger.error('ETH token already registered.') return if not LocalViewInterface.get_contract_parameters(): logger.error('Contract parameters not yet populated.') return logger.warning('Registering ETH Token') eth_token = register_token(token_address=settings.HUB_LQD_CONTRACT_ADDRESS, name='Ethereum', short_name='ETH', register_on_chain=False) ContractLedgerState.objects.create( contract_state=LocalViewInterface.genesis(), token=eth_token, pending_withdrawals=0, confirmed_withdrawals=0, deposits=0, total_balance=0)
def fetch_contract_state_at_block(self, block_number): try: local_params = LocalViewInterface.get_contract_parameters() current_eon = 1 + \ (block_number - local_params.genesis_block) // local_params.blocks_per_eon contract_state_variables = self.contract\ .functions\ .getServerContractStateVariables()\ .call(block_identifier=block_number) basis = contract_state_variables[0] last_checkpoint_submission_eon = contract_state_variables[1] last_checkpoint = contract_state_variables[2] is_checkpoint_submitted_for_current_eon = contract_state_variables[ 3] has_missed_checkpoint_submission = contract_state_variables[4] live_challenge_count = contract_state_variables[5] except Exception as exception: traceback.print_exc() logger.error('Could not query contract state: {}'.format( str(exception))) return None contract_state = ContractState( block=block_number, confirmed=False, basis=crypto.hex_value(basis), last_checkpoint_submission_eon=last_checkpoint_submission_eon, last_checkpoint=crypto.hex_value(last_checkpoint), is_checkpoint_submitted_for_current_eon= is_checkpoint_submitted_for_current_eon, has_missed_checkpoint_submission=has_missed_checkpoint_submission, live_challenge_count=live_challenge_count) contract_ledger_states = [] for token in Token.objects.all(): if token.block >= block_number: continue try: contract_state_ledger_variables = self.contract\ .functions\ .getServerContractLedgerStateVariables(current_eon, add_0x_prefix(token.address))\ .call(block_identifier=block_number) pending_withdrawals = contract_state_ledger_variables[0] confirmed_withdrawals = contract_state_ledger_variables[1] deposits = contract_state_ledger_variables[2] total_balance = contract_state_ledger_variables[3] contract_ledger_states.append( ContractLedgerState( token=token, pending_withdrawals=pending_withdrawals, confirmed_withdrawals=confirmed_withdrawals, deposits=deposits, total_balance=total_balance)) except Exception as exception: traceback.print_exc() logger.error( 'Could not query contract ledger state for {}: {}'.format( token.address, str(exception))) contract_ledger_states.append( ContractLedgerState(token=token, pending_withdrawals=0, confirmed_withdrawals=0, deposits=0, total_balance=0)) return contract_state, contract_ledger_states
def get_basis(self, eon_number, block_identifier='latest'): eons_kept = LocalViewInterface.get_contract_parameters().eons_kept return self.contract\ .functions\ .getParentChainAccumulatorAtSlot(eon_number % eons_kept)\ .call(block_identifier=block_identifier)