def process_passive_transfers_for_eon(operator_eon_number): checkpoint_created = RootCommitment.objects.filter( eon_number=operator_eon_number).exists() with transaction.atomic(): transfers = Transfer.objects\ .filter(processed=False, swap=False, passive=True)\ .select_for_update()\ .order_by('eon_number', 'id') for transfer in transfers: try: with transaction.atomic(): process_passive_transfer(transfer, operator_eon_number, checkpoint_created) except IntegrityError as e: send_admin_email(subject='Transfer Integrity Error', content='{}'.format(e)) logger.error(e)
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 broadcast_checkpoint(): """ References to this function is absent in program code """ with ContractState.global_lock(): latest_block = LocalViewInterface.latest() submitted = latest_block.is_checkpoint_submitted_for_current_eon current_eon, current_sub_block = latest_block.eon_number_and_sub_block() blocks_for_submission = LocalViewInterface.blocks_for_submission() if submitted: logger.warning("TokenCommitment already submitted") return elif current_sub_block < blocks_for_submission: logger.warning('Too early to submit checkpoint: {} blocks left'.format( blocks_for_submission - current_sub_block)) return elif current_sub_block > 150 and settings.DEBUG: logger.error("just let the damn tests pass..") # TODO: todo return elif latest_block.has_missed_checkpoint_submission: logger.error( 'The operator has missed a checkpoint submission. Cannot submit checkpoint.') send_admin_email( subject='The commit chain is halted.', content='Ouch.') return checkpoint = TokenCommitment.objects.get(eon_number=current_eon) if EthereumTransaction.objects.filter(tag=checkpoint.tag()).exists(): logger.warning("TokenCommitment already enqueued.") send_admin_email( subject='Soft Submission Error: TokenCommitment already enqueued.', content='This should eventually be resolved.') return managed_funds = NOCUSTContractInterface().get_managed_funds(checkpoint.eon_number) if checkpoint.upper_bound > managed_funds: logger.error( "TokenCommitment upper bound greater than managed funds.") send_admin_email( subject='HARD Submission Error: TokenCommitment upper bound greater than managed funds.', content='Created checkpoint for {} while managed funds are {}. Some withdrawals are possibly pending cancellation.'.format(checkpoint.upper_bound, managed_funds)) return NOCUSTContractInterface().queue_submit_checkpoint(checkpoint)
def create_token_commitment_for_eon(token: Token, eon_number): logger.info('Creating Token Commitment for {} at {}'.format( token.address, eon_number)) last_eon_number = eon_number - 1 with transaction.atomic(): wallets = Wallet.objects\ .filter( token=token, registration_operator_authorization__isnull=False, trail_identifier__isnull=False)\ .order_by('trail_identifier') new_balances = [] left, right = 0, 0 for wallet in wallets: with wallet.lock(auto_renewal=True): wallet_transfer_context = WalletTransferContext( wallet=wallet, transfer=None) last_transfer, last_transfer_is_outgoing = wallet_transfer_context.last_appended_active_transfer( eon_number=last_eon_number) last_transfer_active_state = None if last_transfer is not None and last_transfer.is_open_swap(): last_transfer.retire_swap() if last_transfer is not None: last_transfer_active_state = WalletTransferContext.appropriate_transfer_active_state( transfer=last_transfer, is_outgoing=last_transfer_is_outgoing) available_funds = wallet_transfer_context.available_funds_at_eon( eon_number=last_eon_number, only_appended=True) right = left + available_funds assert right >= left, 'Wallet {} Token {} Balance {}'.format( wallet.address, token.address, available_funds) passive_checksum, passive_amount, passive_marker = wallet_transfer_context.get_passive_values( eon_number=last_eon_number) new_balances.append({ 'contract': settings.HUB_LQD_CONTRACT_ADDRESS, 'token': token.address, 'wallet': wallet.address, 'left': left, 'right': right, 'active_state_checksum': last_transfer_active_state.checksum() if last_transfer_active_state is not None else b'\0'*32, 'active_state': last_transfer_active_state, 'passive_checksum': passive_checksum, 'passive_amount': passive_amount, 'passive_marker': passive_marker, }) left = right last_incoming_passive_transfer = wallet_transfer_context.last_appended_incoming_passive_transfer( eon_number=last_eon_number) if last_incoming_passive_transfer: wallet_transfer_context = WalletTransferContext( wallet=wallet, transfer=last_incoming_passive_transfer) passive_eon_transfers_list = wallet_transfer_context.incoming_passive_transfers_list( only_appended=True, force_append=False) passive_transfers_merkle_tree = wallet_transfer_context.incoming_passive_transfers_tree( only_appended=True, force_append=False) for index, incoming_passive_transfer in enumerate(passive_eon_transfers_list): final_transfer_index = index final_transfer_membership_proof = passive_transfers_merkle_tree.proof( final_transfer_index) final_transfer_membership_proof_chain = final_transfer_membership_proof.get( "chain") final_transfer_membership_proof_values = final_transfer_membership_proof.get( "values") assert incoming_passive_transfer.final_receipt_hashes is None assert incoming_passive_transfer.final_receipt_index is None assert incoming_passive_transfer.final_receipt_values is None incoming_passive_transfer.final_receipt_hashes = final_transfer_membership_proof_chain incoming_passive_transfer.final_receipt_index = final_transfer_index incoming_passive_transfer.final_receipt_values = final_transfer_membership_proof_values incoming_passive_transfer.save() if last_transfer_active_state is None: continue wallet_transfer_context = WalletTransferContext( wallet=wallet, transfer=last_transfer) starting_balance = int( wallet_transfer_context.starting_balance_in_eon(last_eon_number)) # if last active transfer is a multi eon swap # starting balance included in every tx checksum should be set to the cached starting balance # this way checkpoint state will match signed active state if last_transfer.is_swap() and not last_transfer.cancelled: if Transfer.objects.filter(eon_number=last_transfer.eon_number-1, tx_id=last_transfer.tx_id).exists(): matched_out, matched_in = last_transfer.matched_amounts( all_eons=True) current_matched_out, current_matched_in = last_transfer.matched_amounts( all_eons=False) if last_transfer_is_outgoing: sender_starting_balance = last_transfer.sender_starting_balance # current eon's starting balance should be equal to # cached starting balance - committed matched out amount in past rounds assert(starting_balance == sender_starting_balance - matched_out + current_matched_out) starting_balance = sender_starting_balance else: recipient_starting_balance = last_transfer.recipient_starting_balance # current eon's starting balance should be equal to # cached starting balance + committed matched in amount in past rounds assert( starting_balance == recipient_starting_balance + matched_in - current_matched_in) starting_balance = recipient_starting_balance confirmed_eon_transfers_list = wallet_transfer_context.authorized_transfers_list( only_appended=True, force_append=False) confirmed_eon_transfers_list_shorthand = wallet_transfer_context.authorized_transfers_list_shorthand( only_appended=True, force_append=False, last_transfer_is_finalized=False, starting_balance=starting_balance) transaction_merkle_tree = TransactionMerkleTree( confirmed_eon_transfers_list_shorthand) transaction_merkle_tree_root = hex_value( transaction_merkle_tree.root_hash()) assert transaction_merkle_tree_root == last_transfer_active_state.tx_set_hash,\ '{}/{}'.format(transaction_merkle_tree_root, last_transfer_active_state.tx_set_hash) for confirmed_incoming_transfer in confirmed_eon_transfers_list: if confirmed_incoming_transfer.recipient != wallet: continue final_transfer_index = transaction_merkle_tree.merkle_tree_nonce_map.get( confirmed_incoming_transfer.nonce) final_transfer_membership_proof_chain = transaction_merkle_tree.proof( final_transfer_index) assert confirmed_incoming_transfer.final_receipt_hashes is None assert confirmed_incoming_transfer.final_receipt_index is None confirmed_incoming_transfer.final_receipt_hashes = final_transfer_membership_proof_chain confirmed_incoming_transfer.final_receipt_index = final_transfer_index confirmed_incoming_transfer.save() managed_funds = 0 if eon_number > 1: last_eon = LocalViewInterface.confirmed(eon_number=last_eon_number) pending_withdrawals_until_last_eon = \ WithdrawalRequest.objects\ .filter(wallet__token=token, eon_number__lte=last_eon_number, slashed=False)\ .filter(Q(withdrawal__isnull=True) | Q(withdrawal__block__gt=last_eon.block)) if not pending_withdrawals_until_last_eon.exists(): last_eon_pending_withdrawals = 0 else: last_eon_pending_withdrawals = pending_withdrawals_until_last_eon\ .aggregate(Sum('amount')) \ .get('amount__sum') total_token_balance = last_eon.contractledgerstate_set.get( token=token).total_balance managed_funds = total_token_balance - last_eon_pending_withdrawals if right < managed_funds: logger.warning('UNCLAIMED FUNDS: {} in {}'.format( managed_funds - right, token.address)) send_admin_email( subject='Soft TokenCommitment Warning: Extra funds', content='There are some additional funds in the balance pool that belong to no one: {} of {}' .format(managed_funds - right, token.address)) altered_balances = new_balances + [{ 'contract': settings.HUB_LQD_CONTRACT_ADDRESS, 'token': token.address, 'wallet': settings.HUB_OWNER_ACCOUNT_ADDRESS, 'left': left, 'right': managed_funds, 'active_state_checksum': b'\0'*32, 'active_state': None, 'passive_checksum': b'\0'*32, 'passive_amount': 0, 'passive_marker': 0, }] new_merkle_tree = MerkleTree(altered_balances, managed_funds) right = managed_funds else: if right > managed_funds: logger.error('OVERCLAIMING FUNDS!! {} > {} in {}'.format( right, managed_funds, token.address)) send_admin_email( subject='HARD Checkpoint Error: OVERCLAIMING!', content='OVERCLAIMING FUNDS!! {} > {} in {}'.format(right, managed_funds, token.address)) new_merkle_tree = MerkleTree(new_balances, right) bulk_manager = BulkCreateManager(chunk_size=500) for index, balance in enumerate(new_balances): if not balance.get('wallet') or balance.get('wallet') == '0x0000000000000000000000000000000000000000': continue merkle_proof = new_merkle_tree.proof(index) wallet = Wallet.objects.get( token=token, address=remove_0x_prefix(balance.get('wallet'))) # TODO verify validity through RPC prior to insertion assert(wallet.trail_identifier == index) # create records in batches bulk_manager.add( ExclusiveBalanceAllotment( wallet=wallet, eon_number=eon_number, left=balance.get('left'), right=balance.get('right'), merkle_proof_hashes=merkle_proof.get('chain'), merkle_proof_values=merkle_proof.get('values'), merkle_proof_trail=index, active_state=balance.get('active_state') ) ) # make sure remaining batch is added bulk_manager.done() token_commitment = TokenCommitment.objects.create( token=token, merkle_root=hex_value(new_merkle_tree.root_hash()), upper_bound=right) return token_commitment
def concurrently_retrieve_state(contract_interface, contract_event_decoder, verbose): logger.info('Retrieve blocks.') latest_chain_block = contract_interface.current_block() - 1 if not settings.DEBUG: latest_chain_block += 1 logger.info('Latest chain block: {}'.format(latest_chain_block)) running_from = LocalViewInterface.latest_block() + 1 running_until = latest_chain_block if running_from > running_until: logger.info('No new blocks {}-{}.'.format(running_from, running_until)) return 0 confirm_from = LocalViewInterface.confirmed_block() + 1 confirm_until = running_until - contract_interface.get_blocks_for_confirmation( ) if confirm_from > confirm_until: logger.info('No new blocks to confirm.') update_from = min(running_from, confirm_from) update_until = min(update_from + 11, running_until + 1) skipped = running_until + 1 - update_until contract_state_tasks = [] contract_state_tasks_block_numbers = [] logger.info('Fetching [{},{})'.format(update_from, update_until)) for block_number in range(update_from, update_until): if confirm_from <= block_number and block_number <= confirm_until: contract_state_tasks.append( fetch_confirmed_block.delay(block_number=block_number)) contract_state_tasks_block_numbers.append(block_number) elif running_from <= block_number and block_number <= running_until: contract_state_tasks.append( fetch_running_block.delay(block_number=block_number)) contract_state_tasks_block_numbers.append(block_number) for task_index, contract_state_task in enumerate(contract_state_tasks): block_number = contract_state_tasks_block_numbers[task_index] try: task_result = contract_state_task.get( timeout=settings.HUB_BLOCK_FETCH_TIMEOUT, disable_sync_subtasks=False) except exceptions.TimeoutError: logger.error('Timed-out fetching block {}'.format(block_number)) for cleanup_index, task_to_clean_up in enumerate( contract_state_tasks): if cleanup_index >= task_index: try: task_to_clean_up.forget() except NotImplementedError as e: logger.error('Could not forget task results.') logger.error(e) break if confirm_from <= block_number and block_number <= confirm_until: confirmed_contract_state_dictionary, confirmed_contract_ledger_state_dictionaries, block_logs = task_result confirmed_contract_state = ContractState.from_dictionary_form( confirmed_contract_state_dictionary) confirmed_ledger_states = [ ContractLedgerState.from_dictionary_form( ledger_state, confirmed_contract_state) for ledger_state in confirmed_contract_ledger_state_dictionaries ] with transaction.atomic(): if running_from <= block_number and block_number <= running_until: confirmed_contract_state.save() for ledger_state in confirmed_ledger_states: ledger_state.contract_state = confirmed_contract_state ledger_state.save() logger.info('Decoding logs for block {}.'.format( confirmed_contract_state.block)) decoded_logs = contract_event_decoder.decode_many(block_logs) eon_number = confirmed_contract_state.eon_number() logger.info( "Processing decoded logs in block %d eon %s: %d logs" % (confirmed_contract_state.block, eon_number, len(decoded_logs))) for log in decoded_logs: if log.get(u'name') in event_interpreter_map: interpreter = event_interpreter_map.get( log.get(u'name')) interpreter.interpret( decoded_event=log.get('data'), txid=log.get('txid'), block_number=confirmed_contract_state.block, eon_number=eon_number, verbose=verbose) if interpreter else None else: logger.error('UNKNOWN EVENT LOG {} '.format(log)) send_admin_email( subject='Chain Sync Error: Unknown Log', content='{}'.format(log)) running_contract_state = LocalViewInterface.running( block_number=confirmed_contract_state.block) if running_contract_state.confirm(confirmed_contract_state, confirmed_ledger_states): logger.info('Block {} confirmed.'.format( confirmed_contract_state.block)) else: logger.error('Block {} failed to confirm.'.format( confirmed_contract_state.block)) send_admin_email( subject='Chain Sync Confirmation Failure {}'.format( confirmed_contract_state.block), content='{}'.format(confirmed_contract_state)) raise Exception() elif running_from <= block_number and block_number <= running_until: logger.info('Process running block {}'.format(block_number)) confirmed_contract_state_dictionary, confirmed_contract_ledger_state_dictionaries = task_result contract_state = ContractState.from_dictionary_form( confirmed_contract_state_dictionary) contract_state.save() ledger_states = [ ContractLedgerState.from_dictionary_form( ledger_state, contract_state) for ledger_state in confirmed_contract_ledger_state_dictionaries ] for ledger_state in ledger_states: ledger_state.save() logger.info('Running block {} stored.'.format( contract_state.block)) else: logger.info('Running from {} to {}.'.format( running_from, running_until)) logger.info('Confirm from {} to {}.'.format( confirm_from, confirm_until)) logger.info('Update from {} to {}.'.format(update_from, update_until)) logger.error('Unexpected block number {}'.format(block_number)) send_admin_email( subject='Chain Sync Unexpected Block {}'.format(block_number), content='Out of order processing.') raise Exception() return skipped
def respond_to_challenges(): contract_interface = NOCUSTContractInterface() contract_interface.get_current_eon_number() latest_root_commitment = RootCommitment.objects\ .all()\ .order_by('eon_number')\ .last() if latest_root_commitment is None: return with Challenge.global_lock(): challenges = Challenge.objects\ .filter(rebuted=False, eon_number__lte=latest_root_commitment.eon_number)\ .order_by('block') for challenge in challenges: token = challenge.wallet.token challenge_entry_token = token.address if challenge.wallet.token != challenge.recipient.token: try: tp = TokenPair.objects.get( token_from=challenge.wallet.token, token_to=challenge.recipient.token) except TokenPair.DoesNotExist: logger.warning( "Skipping challenge for {}. token pair not found!". format(challenge.wallet.address)) continue token = challenge.recipient.token challenge_entry_token = to_checksum_address(tp.conduit) challenge_entry = contract_interface.get_challenge_record( token_address=challenge_entry_token, recipient=challenge.recipient.address, sender=challenge.wallet.address) if not challenge_entry.challengeStage: logger.warning( "Skipping answered challenge for {}. Where is the answer tx_id?" .format(challenge.wallet.address)) continue try: recipient_balance = ExclusiveBalanceAllotment.objects.get( wallet=challenge.recipient, eon_number=challenge.eon_number) except ExclusiveBalanceAllotment.DoesNotExist: logger.error("Could not find balance for {} at eon {}.".format( challenge.wallet.address, challenge.eon_number)) send_admin_email(subject='DISPUTE! NO BALANCE!', content='{}'.format(challenge.wallet.address)) return token_commitment = TokenCommitment.objects.get( token=token, root_commitment__eon_number=challenge.eon_number) # state update challenge if challenge.wallet.token == challenge.recipient.token and challenge.wallet.address == challenge.recipient.address: v_0, r_0, s_0 = recipient_balance.wallet_v_r_s() v_1, r_1, s_1 = recipient_balance.operator_v_r_s() recipient_transfer_context = WalletTransferContext( wallet=challenge.recipient, transfer=None) passive_checksum, passive_amount, passive_marker = recipient_transfer_context.get_passive_values( eon_number=challenge_entry.initialStateEon) logger.info( "Answering challenge for {} with balance {}.".format( challenge.wallet.address, recipient_balance.amount())) logger.info("{}{}{}, {}{}{}".format(v_0, r_0, s_0, v_1, r_1, s_1)) send_admin_email(subject='DISPUTE! Sate Update.', content='{}'.format(challenge.wallet.address)) # TODO signal critical failure if this does not succeed! transaction = contract_interface.queue_answer_state_update_challenge( challenge=challenge, allotment_chain=[ crypto.zfill(crypto.decode_hex(checksum)) for checksum in long_string_to_list( recipient_balance.merkle_proof_hashes, 64) ], membership_chain=[ crypto.zfill(crypto.decode_hex(checksum)) for checksum in long_string_to_list( token_commitment.membership_hashes, 64) ], values=csf_to_list(recipient_balance.merkle_proof_values, int), l_r=[ int(recipient_balance.left), int(recipient_balance.right) ], tx_set_root=crypto.zfill( crypto.decode_hex( recipient_balance.transaction_set_root())), deltas=[d for d in recipient_balance.deltas()], r=[crypto.uint256(r_0), crypto.uint256(r_1)], s=[crypto.uint256(s_0), crypto.uint256(s_1)], v=[v_0, v_1], passive_checksum=passive_checksum, passive_amount=passive_amount, passive_marker=passive_marker) # transfer challenge elif challenge.wallet.token == challenge.recipient.token and challenge.wallet.address != challenge.recipient.address: try: transfer = Transfer.objects.get( recipient=challenge.recipient, eon_number=challenge_entry.initialStateEon, nonce=challenge_entry.deliveredTxNonce) except Transfer.DoesNotExist: logger.error( "Could not find transfer for {} at eon {} with nonce {}." .format(challenge.recipient.address, challenge.eon_number, challenge_entry.deliveredTxNonce)) send_admin_email( subject='DISPUTE! NO TRANSFER!', content= "Could not find transfer for {} at eon {} with nonce {}." .format(challenge.recipient.address, challenge.eon_number, challenge_entry.deliveredTxNonce)) return recipient_transfer_context = WalletTransferContext( wallet=challenge.recipient, transfer=None) transfers_list_nonce_index_map = {} transfers_list = recipient_transfer_context.authorized_transfers_list_shorthand( only_appended=True, force_append=False, eon_number=challenge_entry.initialStateEon, last_transfer_is_finalized=False, index_map=transfers_list_nonce_index_map) transfer_tree = TransactionMerkleTree(transfers_list) transfer_index = transfers_list_nonce_index_map.get( transfer.nonce) transfer_node = transfer_tree.merkle_tree_leaf_map.get( transfer_index) transfer_proof = [ node.get('hash') for node in calculate_merkle_proof( transfer_index, transfer_node) ] passive_checksum, passive_amount, passive_marker = recipient_transfer_context.get_passive_values( eon_number=challenge_entry.initialStateEon) send_admin_email(subject='DISPUTE! Transfer Delivery.', content='{} {} {}'.format( challenge.wallet.address, challenge.recipient.address, transfer_index)) # TODO signal critical failure if this does not succeed! transaction = contract_interface.queue_answer_delivery_challenge( challenge=challenge, tx_trail=transfer_index, allotment_chain=[ crypto.zfill(crypto.decode_hex(v)) for v in long_string_to_list( recipient_balance.merkle_proof_hashes, 64) ], membership_chain=[ crypto.zfill(crypto.decode_hex(checksum)) for checksum in long_string_to_list( token_commitment.membership_hashes, 64) ], values=csf_to_list(recipient_balance.merkle_proof_values, int), l_r=[ int(recipient_balance.left), int(recipient_balance.right) ], deltas=[d for d in recipient_balance.deltas()], tx_set_root=crypto.decode_hex( recipient_balance.transaction_set_root()), tx_chain=[crypto.zfill(x) for x in transfer_proof], passive_checksum=passive_checksum, passive_amount=passive_amount, passive_marker=passive_marker) # swap challenge elif challenge.wallet.token != challenge.recipient.token and challenge.wallet.address == challenge.recipient.address: try: transfer = Transfer.objects.get( recipient=challenge.recipient, eon_number=challenge_entry.initialStateEon, nonce=challenge_entry.deliveredTxNonce) except Transfer.DoesNotExist: logger.error( "Could not find transfer for {} at eon {} with nonce {}." .format(challenge.recipient.address, challenge.eon_number, challenge_entry.deliveredTxNonce)) send_admin_email( subject='DISPUTE! NO SWAP!', content= "Could not find transfer for {} at eon {} with nonce {}." .format(challenge.recipient.address, challenge.eon_number, challenge_entry.deliveredTxNonce)) return recipient_transfer_context = WalletTransferContext( wallet=challenge.recipient, transfer=None) # if not initial transfer in a multi eon swap # override starting balance to cached starting balance if Transfer.objects.filter(eon_number=transfer.eon_number - 1, tx_id=transfer.tx_id).exists(): starting_balance = int(transfer.recipient_starting_balance) else: starting_balance = int( recipient_transfer_context.starting_balance_in_eon( challenge_entry.initialStateEon)) transfers_list_nonce_index_map = {} transfers_list = recipient_transfer_context.authorized_transfers_list_shorthand( only_appended=True, force_append=False, eon_number=challenge_entry.initialStateEon, last_transfer_is_finalized=True, index_map=transfers_list_nonce_index_map, starting_balance=starting_balance) transfer_tree = TransactionMerkleTree(transfers_list) transfer_index = transfers_list_nonce_index_map.get( transfer.nonce) transfer_node = transfer_tree.merkle_tree_leaf_map.get( transfer_index) transfer_proof = [ node.get('hash') for node in calculate_merkle_proof( transfer_index, transfer_node) ] passive_checksum, passive_amount, passive_marker = recipient_transfer_context.get_passive_values( eon_number=challenge_entry.initialStateEon) send_admin_email(subject='DISPUTE! Swap Delivery.', content='{} {} {}'.format( challenge.wallet.address, challenge.recipient.address, transfer_index)) is_cancelled = transfer.cancelled and transfer.recipient_cancellation_active_state is not None if transfer.complete or is_cancelled: starting_balance = 2**256 - 1 # TODO signal critical failure if this does not succeed! transaction = contract_interface.queue_answer_swap_challenge( challenge=challenge, token_pair=[ challenge.wallet.token.address, challenge.recipient.token.address ], balance_at_start_of_eon=starting_balance, tx_trail=int(transfer_index), allotment_chain=[ crypto.zfill(crypto.decode_hex(v)) for v in long_string_to_list( recipient_balance.merkle_proof_hashes, 64) ], membership_chain=[ crypto.zfill(crypto.decode_hex(checksum)) for checksum in long_string_to_list( token_commitment.membership_hashes, 64) ], values=csf_to_list(recipient_balance.merkle_proof_values, int), l_r=[ int(recipient_balance.left), int(recipient_balance.right) ], deltas=[d for d in recipient_balance.deltas()], tx_set_root=crypto.zfill( crypto.decode_hex( recipient_balance.transaction_set_root())), tx_chain=[crypto.zfill(x) for x in transfer_proof], passive_checksum=passive_checksum, passive_amount=passive_amount, passive_marker=passive_marker) challenge.rebuted = True challenge.save() logger.warning(transaction)
def send_queued_transactions(): contract_interface = NOCUSTContractInterface() latest_block = LocalViewInterface.latest_block() with EthereumTransaction.global_lock(auto_renewal=True): pending_transactions = EthereumTransaction.objects.all().order_by('nonce') for transaction in pending_transactions: if transaction.ethereumtransactionattempt_set.filter(confirmed=True).exists(): continue if transaction.ethereumtransactionattempt_set.exists(): last_attempt = transaction.ethereumtransactionattempt_set.order_by( 'gas_price').last() else: initial_gas_price = contract_interface.web3.toWei( '100', 'gwei') signed_tx = contract_interface.sign_for_delivery_as_owner( transaction, initial_gas_price) try: logger.info('Publishing Signed TX: {}'.format(transaction)) hash = contract_interface.send_raw_transaction( signed_tx.rawTransaction) except ValueError as e: send_admin_email( subject='INITIAL TRANSACTION ATTEMPT ERROR', content='{}: {}'.format(transaction.tag, e)) continue last_attempt = EthereumTransactionAttempt.objects.create( transaction=transaction, block=latest_block, gas_price=initial_gas_price, signed_attempt=signed_tx.rawTransaction.hex(), hash=remove_0x_prefix(hash.hex()), mined=False, confirmed=False) if transaction.ethereumtransactionattempt_set.filter(mined=True).exists(): try: mined_transaction = transaction.ethereumtransactionattempt_set.get( mined=True) except EthereumTransactionAttempt.DoesNotExist: logger.error('Mined Transaction Attempt Inconsistency') continue receipt = contract_interface.get_transaction_receipt_hex( add_0x_prefix(mined_transaction.hash)) if receipt is not None: if receipt.get('blockNumber') - latest_block > settings.HUB_LQD_CONTRACT_CONFIRMATIONS: logger.info('Transaction confirmed! {}'.format( last_attempt.hash)) mined_transaction.confirmed = True mined_transaction.save() continue logger.warning( 'Transaction UNMINED: {}'.format(last_attempt.hash)) mined_transaction.mined = False mined_transaction.save() receipt = contract_interface.get_transaction_receipt_hex( add_0x_prefix(last_attempt.hash)) if receipt is not None: last_attempt.mined = True last_attempt.save() continue if latest_block - last_attempt.block > 10: new_gas_price = 2 * int(last_attempt.gas_price) signed_tx = contract_interface.sign_for_delivery_as_owner( transaction, new_gas_price) try: hash = contract_interface.send_raw_transaction( signed_tx.rawTransaction) except ValueError as e: send_admin_email( subject='TRANSACTION RE-ATTEMPT ERROR', content='{}: {}'.format(transaction.tag, e)) continue EthereumTransactionAttempt.objects.create( transaction=transaction, block=latest_block, gas_price=new_gas_price, signed_attempt=signed_tx.rawTransaction.hex(), hash=remove_0x_prefix(hash.hex()), mined=False, confirmed=False) send_admin_email( subject='Transaction Reattempt', content='{}: {}'.format(transaction.tag, new_gas_price))
def send_address_not_found_log(who, address): logger.error("CHALLENGE ISSUED AGAINST UNKNOWN {} {}".format(who, address)) send_admin_email( subject='DISPUTE! UNKNOWN {}!'.format(who), content='{}'.format(address))