def process_passive_transfer(transfer, operator_eon_number, checkpoint_created): if transfer.wallet == transfer.recipient: logger.info('Voiding self transfer.') transfer.close(voided=True) return with transfer.lock(auto_renewal=True), transfer.wallet.lock( auto_renewal=True), transfer.recipient.lock(auto_renewal=True): wallet_view_context = WalletTransferContext(wallet=transfer.wallet, transfer=transfer) recipient_view_context = WalletTransferContext( wallet=transfer.recipient, transfer=transfer) if should_void_transfer(transfer, wallet_view_context, recipient_view_context, operator_eon_number, checkpoint_created): logger.info('Voiding transfer.') transfer.close(voided=True) return tx_set_tree = wallet_view_context.optimized_authorized_transfers_tree( only_appended=True) tx_set_hash = hex_value(tx_set_tree.root_hash()) highest_spendings, highest_gains = wallet_view_context.off_chain_actively_sent_received_amounts( eon_number=transfer.eon_number, only_appended=True) active_state = ActiveState( wallet=transfer.wallet, updated_spendings=transfer.sender_active_state.updated_spendings, updated_gains=highest_gains, tx_set_hash=tx_set_hash, eon_number=transfer.eon_number) raw_checksum = active_state.checksum() encoded_checksum = hex_value(raw_checksum) wallet_active_state = transfer.sender_active_state if wallet_active_state.wallet_signature.checksum != encoded_checksum: logger.error( 'Transfer {} invalid sender active state checksum for {}'. format(transfer.id, transfer.wallet.address)) transfer.close(voided=True) return try: wallet_active_state.operator_signature = wallet_active_state.sign_active_state( settings.HUB_OWNER_ACCOUNT_ADDRESS, settings.HUB_OWNER_ACCOUNT_KEY) except LookupError as e: logger.error(e) return transfer.sender_active_state.save() transfer.close(complete=True, appended=True) operator_celery.send_task('auditor.tasks.on_transfer_confirmation', args=[transfer.id]) logger.info('Passive transfer {} processed.'.format(transfer.id))
def create(self, validated_data): active_state_signature_data = validated_data.pop('debit_signature') wallet = validated_data.pop('wallet') recipient = validated_data.pop('recipient') # get current eon current_eon = LocalViewInterface.latest().eon_number() # transfer eon should be the current eon number if validated_data.pop('eon_number') != current_eon: raise serializers.ValidationError( detail='', code=ErrorCode.EON_NUMBER_OUT_OF_SYNC) # TODO refactor this such that the recipient is only locked after the sender's details are verified wallets = sorted([wallet, recipient], key=lambda w: w.trail_identifier) with RootCommitment.read_write_lock( suffix=current_eon, auto_renewal=False), wallets[0].lock( auto_renewal=False), wallets[1].lock(auto_renewal=False): if RootCommitment.objects.filter(eon_number=current_eon + 1).exists(): raise serializers.ValidationError( detail='', code=ErrorCode.EON_NUMBER_OUT_OF_SYNC) transfer = Transfer(wallet=wallet, amount=validated_data.pop('amount'), eon_number=current_eon, recipient=recipient, nonce=validated_data.pop('nonce'), passive=True) wallet_view_context = WalletTransferContext(wallet=wallet, transfer=transfer) recipient_view_context = WalletTransferContext(wallet=recipient, transfer=transfer) # Minimal SLA if not wallet.is_sla_exempt() and not recipient.is_sla_exempt(): if not wallet.has_valid_sla(): sender_transfers_list = wallet_view_context.authorized_transfers_list( only_appended=False, force_append=True) if len(sender_transfers_list) > settings.SLA_THRESHOLD: raise serializers.ValidationError( detail='', code=ErrorCode.DEBIT_WALLET_EXCEEDED_SLA) elif not recipient.has_valid_sla(): recipient_transfers_list = recipient_view_context.authorized_transfers_list( only_appended=False, force_append=True) if len(recipient_transfers_list) > settings.SLA_THRESHOLD: raise serializers.ValidationError( detail='', code=ErrorCode.CREDIT_WALLET_EXCEEDED_SLA) # Ensure sender log consistency can_append_to_sender_log = wallet_view_context.can_schedule_transfer( ) if can_append_to_sender_log is not True: raise serializers.ValidationError( detail='Sender: {}'.format(can_append_to_sender_log), code=ErrorCode.DEBIT_WALLET_CANNOT_ADD_TRANSACTION) # Ensure recipient log consistency can_append_to_recipient_log = recipient_view_context.can_schedule_transfer( ) if can_append_to_recipient_log is not True: raise serializers.ValidationError( detail='Recipient: {}'.format(can_append_to_recipient_log), code=ErrorCode.CREDIT_WALLET_CANNOT_ADD_TRANSACTION) # Ensure transfer consistency can_spend, currently_available_funds = wallet_view_context.can_send_transfer( current_eon_number=current_eon, using_only_appended_funds=False) if can_spend is not True: raise serializers.ValidationError( detail=can_spend, code=ErrorCode.DEBIT_WALLET_OVERSPENDING) # Validate data concise_balance_marker_signature_data = validated_data.pop( 'debit_balance_signature') concise_balance_marker_amount = validated_data.pop('debit_balance') if concise_balance_marker_amount > currently_available_funds - transfer.amount: raise serializers.ValidationError( detail='', code=ErrorCode.DEBIT_WALLET_BALANCE_MARKER_EXCEED_BALANCE) concise_balance_marker = MinimumAvailableBalanceMarker( wallet=wallet, eon_number=transfer.eon_number, amount=concise_balance_marker_amount) concise_balance_marker_checksum = hex_value( concise_balance_marker.checksum()) concise_balance_marker_signature = Signature( wallet=transfer.wallet, checksum=concise_balance_marker_checksum, value=concise_balance_marker_signature_data.get('value')) if not concise_balance_marker_signature.is_valid(): raise serializers.ValidationError( detail='', code=ErrorCode.INVALID_DEBIT_BALANCE_SIGNATURE) tx_set_tree = wallet_view_context.optimized_authorized_transfers_tree( ) tx_set_hash = hex_value(tx_set_tree.root_hash()) transfer_index = tx_set_tree.merkle_tree_nonce_map.get( transfer.nonce) transfer_proof = tx_set_tree.proof(transfer_index) highest_spendings, highest_gains = wallet_view_context.off_chain_actively_sent_received_amounts( eon_number=transfer.eon_number, only_appended=False) active_state = ActiveState(wallet=wallet, updated_spendings=highest_spendings + transfer.amount, updated_gains=highest_gains, tx_set_hash=tx_set_hash, tx_set_proof_hashes=transfer_proof, tx_set_index=transfer_index, eon_number=transfer.eon_number) checksum = hex_value(active_state.checksum()) active_state_signature = Signature( wallet=transfer.wallet, checksum=checksum, value=active_state_signature_data.get('value')) if not active_state_signature.is_valid(): raise serializers.ValidationError( detail='', code=ErrorCode.INVALID_DEBIT_SIGNATURE) transfer.position = recipient_view_context.off_chain_passively_received_amount( eon_number=transfer.eon_number, only_appended=False) # locking context covers saving the state as well to make sure checkpoint creation is consistent with transaction.atomic(): Signature.objects.bulk_create( [concise_balance_marker_signature, active_state_signature]) concise_balance_marker.signature = concise_balance_marker_signature concise_balance_marker.save() active_state.wallet_signature = active_state_signature active_state.operator_signature = active_state.sign_active_state( settings.HUB_OWNER_ACCOUNT_ADDRESS, settings.HUB_OWNER_ACCOUNT_KEY) active_state.save() transfer.sender_active_state = active_state transfer.sender_balance_marker = concise_balance_marker # cache transfer index in sender active set transfer.sender_merkle_index = transfer_index # transfer.sender_merkle_root_cache = tx_set_hash # cache active set merkle mountains height array and hash array for recipient active set transfer.sender_merkle_hash_cache, transfer.sender_merkle_height_cache = tx_set_tree.merkle_cache_stacks( ) transfer.complete = True transfer.appended = True transfer.processed = True transfer.save() if transfer.appended: operator_celery.send_task('auditor.tasks.on_transfer_confirmation', args=[transfer.id]) return transfer
def should_void_swap(swap: Transfer, wallet_view_context: WalletTransferContext, recipient_view_context: WalletTransferContext, operator_eon_number: int, is_checkpoint_created: bool): if not settings.SWAPS_ENABLED: logger.error('Swaps disabled. Voiding {}'.format(swap.id)) return True if swap.amount < 1: logger.error('Swap {} has less than 1 amount'.format(swap.id)) return True if swap.amount_swapped < 1: logger.error('Swap {} has less than 1 amount swapped'.format(swap.id)) return True # Unauthorized transfer if swap.sender_active_state is None: logger.error('Swap {} no authorization'.format(swap.id)) return True # Invalid signature by sender if not swap.sender_active_state.wallet_signature.is_valid(): logger.error('Swap {} invalid sender signature.'.format(swap.id)) return True # Unreceived transaction if swap.recipient_active_state is None: logger.error('Swap receipt for {} not provided.'.format(swap.id)) return True # Invalid signature by recipient if not swap.recipient_active_state.wallet_signature.is_valid(): logger.error('Swap {} invalid receipt signature.'.format(swap.id)) return True # Ensure log consistency can_append_to_sender_log = wallet_view_context.can_append_transfer() if can_append_to_sender_log is not True: logger.error('Sender: {}'.format(can_append_to_sender_log)) return True can_append_to_recipient_log = recipient_view_context.can_append_transfer() if can_append_to_recipient_log is not True: logger.error('Recipient: {}'.format(can_append_to_recipient_log)) return True # Skip consistency checks since they were done at least once before. if swap.appended: return False # Overspending sender_funds_remaining = wallet_view_context.loosely_available_funds_at_eon( eon_number=swap.eon_number, current_eon_number=operator_eon_number, is_checkpoint_created=is_checkpoint_created, only_appended=True) # sender remaining funds should be more than remaining amount in order matched_out, matched_in = swap.matched_amounts(all_eons=True) if sender_funds_remaining < swap.amount - matched_out: logger.error('Swap {} overspending.'.format(swap.id)) return True # Prevent future overdrawing # if swap.sender_balance_marker.amount > sender_funds_remaining - swap.amount: if swap.sender_balance_marker.amount != 0: logger.error('Swap {} invalid concise marker balance.'.format(swap.id)) return True concise_balance_marker = MinimumAvailableBalanceMarker( wallet=swap.wallet, eon_number=swap.eon_number, amount=swap.sender_balance_marker.amount) concise_balance_marker_checksum = crypto.hex_value( concise_balance_marker.checksum()) if swap.sender_balance_marker.signature.checksum != concise_balance_marker_checksum: logger.error('Swap {} invalid concise marker checksum for {}.'.format( swap.id, swap.sender_balance_marker.amount)) return True highest_spendings, highest_gains = wallet_view_context.off_chain_actively_sent_received_amounts( eon_number=swap.eon_number, only_appended=True) # if this is a multi eon swap if Transfer.objects.filter(eon_number=swap.eon_number - 1, tx_id=swap.tx_id).exists(): # set balances to initial fixed balances stored in transfer eon state sender_starting_balance = swap.sender_starting_balance recipient_starting_balance = swap.recipient_starting_balance # make sure this eon's starting balance is exactly the initial stored balance # when matched amount is taken into consideration for both sender and receiver if wallet_view_context.starting_balance_in_eon( swap.eon_number) != sender_starting_balance - matched_out: logger.error( 'Swap {} invalid sender starting balance of future state {} != {} - {}.' .format( swap.id, wallet_view_context.starting_balance_in_eon( swap.eon_number), sender_starting_balance, matched_out)) if recipient_view_context.starting_balance_in_eon( swap.eon_number) != recipient_starting_balance + matched_in: logger.error( 'Swap {} invalid recipient starting balance of future state {} != {} + {}.' .format( swap.id, recipient_view_context.starting_balance_in_eon( swap.eon_number), recipient_starting_balance, matched_out)) assert (wallet_view_context.starting_balance_in_eon( swap.eon_number) == sender_starting_balance - matched_out) assert (recipient_view_context.starting_balance_in_eon( swap.eon_number) == recipient_starting_balance + matched_in) else: sender_starting_balance = int( wallet_view_context.starting_balance_in_eon(swap.eon_number)) recipient_starting_balance = int( recipient_view_context.starting_balance_in_eon(swap.eon_number)) # Debit Authorization tx_set_tree = wallet_view_context.optimized_authorized_transfers_tree( only_appended=True, starting_balance=sender_starting_balance) tx_set_hash = crypto.hex_value(tx_set_tree.root_hash()) transfer_index = tx_set_tree.merkle_tree_nonce_map.get(swap.nonce) transfer_proof = tx_set_tree.proof(transfer_index) highest_spendings, highest_gains = wallet_view_context.off_chain_actively_sent_received_amounts( eon_number=swap.eon_number, only_appended=True) debiting_active_state = ActiveState(wallet=swap.wallet, updated_spendings=highest_spendings + swap.amount, updated_gains=highest_gains, tx_set_hash=tx_set_hash, tx_set_proof_hashes=transfer_proof, tx_set_index=transfer_index, eon_number=swap.eon_number) debiting_active_state_checksum = crypto.hex_value( debiting_active_state.checksum()) if swap.sender_active_state.wallet_signature.checksum != debiting_active_state_checksum: logger.error('Swap {} invalid debit active state checksum.'.format( swap.id)) return True # Credit Authorization tx_set_tree = recipient_view_context.optimized_authorized_transfers_tree( only_appended=True, starting_balance=recipient_starting_balance) tx_set_hash = crypto.hex_value(tx_set_tree.root_hash()) transfer_index = tx_set_tree.merkle_tree_nonce_map.get(swap.nonce) transfer_proof = tx_set_tree.proof(transfer_index) highest_spendings, highest_gains = recipient_view_context.off_chain_actively_sent_received_amounts( eon_number=swap.eon_number, only_appended=True) crediting_active_state = ActiveState(wallet=swap.recipient, updated_spendings=highest_spendings, updated_gains=highest_gains, tx_set_hash=tx_set_hash, tx_set_proof_hashes=transfer_proof, tx_set_index=transfer_index, eon_number=swap.eon_number) crediting_active_state_checksum = crypto.hex_value( crediting_active_state.checksum()) if swap.recipient_active_state.wallet_signature.checksum != crediting_active_state_checksum: logger.error('Swap {} invalid credit active state checksum.'.format( swap.id)) return True # Finality Authorization swap.complete = True tx_set_tree = recipient_view_context.optimized_authorized_transfers_tree( only_appended=True, starting_balance=recipient_starting_balance) swap.complete = False tx_set_hash = crypto.hex_value(tx_set_tree.root_hash()) transfer_index = tx_set_tree.merkle_tree_nonce_map.get(swap.nonce) transfer_proof = tx_set_tree.proof(transfer_index) recipient_fulfillment_active_state = ActiveState( wallet=swap.recipient, updated_spendings=highest_spendings, updated_gains=highest_gains + swap.amount_swapped, tx_set_hash=tx_set_hash, tx_set_proof_hashes=transfer_proof, tx_set_index=transfer_index, eon_number=swap.eon_number) recipient_fulfillment_active_state_checksum = crypto.hex_value( recipient_fulfillment_active_state.checksum()) if swap.recipient_fulfillment_active_state.wallet_signature.checksum != recipient_fulfillment_active_state_checksum: logger.error( 'Swap {} invalid finalization active state checksum.'.format( swap.id)) return True return False
def check_active_state_signature(swap, wallet, active_state_signature_data, is_future_state, starting_balance, highest_spendings, highest_gains, signature_type=None): wallet_view_context = WalletTransferContext(wallet=wallet, transfer=swap) # fulfillment active state if signature_type == SignatureType.FULFILLMENT: swap.processed, swap.complete = True, True if is_future_state: # done for all future eons # assumes this is the only TX in set tx_set_tree = WalletTransferContext.optimized_authorized_transfers_tree_from_list( [ swap.shorthand(wallet_view_context, is_last_transfer=True, starting_balance=starting_balance) ]) else: # done once for current eon tx_set_tree = wallet_view_context.optimized_authorized_transfers_tree() # fulfillment active state if signature_type == SignatureType.FULFILLMENT: swap.processed, swap.complete = False, False tx_set_hash = crypto.hex_value(tx_set_tree.root_hash()) transfer_index = tx_set_tree.merkle_tree_nonce_map.get(swap.nonce) transfer_proof = tx_set_tree.proof(transfer_index) # debit active state if signature_type == SignatureType.DEBIT: updated_spendings = highest_spendings + swap.amount updated_gains = highest_gains state_name = "Debit" # credit active state elif signature_type == SignatureType.CREDIT: updated_spendings = highest_spendings updated_gains = highest_gains state_name = "Credit" # fulfillment active state elif signature_type == SignatureType.FULFILLMENT: updated_spendings = highest_spendings updated_gains = highest_gains + swap.amount_swapped state_name = "Fulfillment" active_state = ActiveState(wallet=wallet, updated_spendings=updated_spendings, updated_gains=updated_gains, tx_set_hash=tx_set_hash, tx_set_proof_hashes=transfer_proof, tx_set_index=transfer_index, eon_number=swap.eon_number) active_state_checksum = crypto.hex_value(active_state.checksum()) active_state_signature = Signature( wallet=wallet, checksum=active_state_checksum, value=active_state_signature_data.get('value')) if not active_state_signature.is_valid(): error_code = None if signature_type == SignatureType.CREDIT: error_code = ErrorCode.INVALID_FUTURE_CREDIT_SIGNATURE if is_future_state else ErrorCode.INVALID_CREDIT_SIGNATURE elif signature_type == SignatureType.DEBIT: error_code = ErrorCode.INVALID_FUTURE_DEBIT_SIGNATURE if is_future_state else ErrorCode.INVALID_DEBIT_SIGNATURE elif signature_type == SignatureType.FULFILLMENT: error_code = ErrorCode.INVALID_FUTURE_CREDIT_FULFILLMENT_SIGNATURE if is_future_state else ErrorCode.INVALID_CREDIT_FULFILLMENT_SIGNATURE raise serializers.ValidationError( 'Active state signature failed for eon {}'.format(swap.eon_number), code=error_code) return active_state, active_state_signature, transfer_index, tx_set_tree.merkle_cache_stacks( )
def update(self, swap, validated_data): current_swap = None is_swap_finalized = False with transaction.atomic(): current_eon = LocalViewInterface.latest().eon_number() swap_set = Transfer.objects.select_for_update().filter( tx_id=swap.tx_id, eon_number__gte=current_eon, swap=True).order_by('eon_number') current_swap = swap_set[0] if not current_swap.complete: raise serializers.ValidationError( detail='', code=ErrorCode.SWAP_NOT_FULFILLED) elif current_swap.cancelled: raise serializers.ValidationError( detail='', code=ErrorCode.SWAP_ALREADY_FROZEN) elif current_swap.voided: raise serializers.ValidationError( detail='', code=ErrorCode.SWAP_ALREADY_VOIDED) elif current_swap.processed: raise serializers.ValidationError( detail='', code=ErrorCode.SWAP_ALREADY_CLOSED) elif current_swap.recipient_finalization_active_state is not None: raise serializers.ValidationError( detail='', code=ErrorCode.SWAP_ALREADY_FINALIZED) finalization_signatures = validated_data.pop( 'finalization_signature') # state to save finalization_active_state_signature_records = [] finalization_active_state_records = [] if swap_set.count() != len(finalization_signatures): raise serializers.ValidationError( detail= 'Wrong number of finalization signatures, expected {} but got {}' .format(swap_set.count(), len(finalization_signatures)), code=ErrorCode.WRONG_NUMBER_OF_CREDIT_SIGNATURES) recipient_view_context = WalletTransferContext( wallet=current_swap.recipient, transfer=current_swap) tx_set_tree = recipient_view_context.optimized_authorized_transfers_tree( ) tx_set_hash = crypto.hex_value(tx_set_tree.root_hash()) transfer_index = tx_set_tree.merkle_tree_nonce_map.get( current_swap.nonce) transfer_proof = tx_set_tree.proof(transfer_index) highest_spendings, highest_gains = recipient_view_context.off_chain_actively_sent_received_amounts( eon_number=current_swap.eon_number, only_appended=False) finalization_active_state = ActiveState( wallet=current_swap.recipient, updated_spendings=highest_spendings + current_swap.amount_swapped, updated_gains=highest_gains + current_swap.amount_swapped, tx_set_hash=tx_set_hash, tx_set_proof_hashes=transfer_proof, tx_set_index=transfer_index, eon_number=current_swap.eon_number) finalization_active_state_signature_data = finalization_signatures[ 0] finalization_active_state_checksum = crypto.hex_value( finalization_active_state.checksum()) finalization_active_state_signature = Signature( wallet=current_swap.recipient, checksum=finalization_active_state_checksum, value=finalization_active_state_signature_data.get('value')) if not finalization_active_state_signature.is_valid(): raise serializers.ValidationError( detail='', code=ErrorCode.INVALID_CREDIT_SIGNATURE) finalization_active_state_signature_records.append( finalization_active_state_signature) finalization_active_state_records.append(finalization_active_state) # calculate future spent, gained, empty tx set future_spent_gained = max(highest_spendings, highest_gains) + swap.amount_swapped + 1 empty_tx_set_hash = crypto.hex_value(NODE_CACHE[0]['hash']) for index in range(1, len(swap_set)): future_swap = swap_set[index] finalization_active_state = ActiveState( wallet=future_swap.recipient, updated_spendings=future_spent_gained, updated_gains=future_spent_gained, tx_set_hash=empty_tx_set_hash, # any dummy value tx_set_proof_hashes='', # any dummy value tx_set_index=0, eon_number=future_swap.eon_number) finalization_active_state_checksum = crypto.hex_value( finalization_active_state.checksum()) finalization_active_state_signature = Signature( wallet=swap.recipient, checksum=finalization_active_state_checksum, value=finalization_signatures[index].get('value')) if not finalization_active_state_signature.is_valid(): raise serializers.ValidationError( detail='', code=ErrorCode.INVALID_FUTURE_CREDIT_SIGNATURE) finalization_active_state_signature_records.append( finalization_active_state_signature) finalization_active_state_records.append( finalization_active_state) Signature.objects.bulk_create( finalization_active_state_signature_records) with current_swap.lock( auto_renewal=False), current_swap.wallet.lock( auto_renewal=False), current_swap.recipient.lock( auto_renewal=False): for index in range(len(finalization_active_state_records)): finalization_active_state_records[ index].wallet_signature = finalization_active_state_signature_records[ index] ActiveState.objects.bulk_create( finalization_active_state_records) for index in range(len(swap_set)): swap_set[ index].recipient_finalization_active_state = finalization_active_state_records[ index] if index > 0: swap_set[index].voided = True swap_set[index].appended = False swap_set[index].processed = True Transfer.objects.bulk_update(swap_set, [ 'recipient_finalization_active_state', 'voided', 'appended', 'processed' ]) swap_set[0].sign_swap_finalization( settings.HUB_OWNER_ACCOUNT_ADDRESS, settings.HUB_OWNER_ACCOUNT_KEY) swap_set[0].close(complete=True, appended=True) current_swap = swap_set[0] is_swap_finalized = True if is_swap_finalized: operator_celery.send_task('auditor.tasks.on_swap_finalization', args=[current_swap.id]) return current_swap
def update(self, swap, validated_data): current_swap = None is_swap_cancelled = False with transaction.atomic(): current_eon = LocalViewInterface.latest().eon_number() swap_set = Transfer.objects.select_for_update().filter( tx_id=swap.tx_id, eon_number__gte=current_eon, swap=True).order_by('eon_number') current_swap = swap_set[0] if not current_swap.cancelled: raise serializers.ValidationError( detail='', code=ErrorCode.SWAP_NOT_FROZEN) elif current_swap.processed: raise serializers.ValidationError( detail='', code=ErrorCode.SWAP_ALREADY_CLOSED) elif current_swap.swap_freezing_signature is None: raise serializers.ValidationError( detail='', code=ErrorCode.MISSING_FREEZING_SIGNATURE) elif None not in [ current_swap.sender_cancellation_active_state, current_swap.recipient_cancellation_active_state ]: raise serializers.ValidationError( detail='', code=ErrorCode.SWAP_ALREADY_CANCELLED) sender_cancellation_signatures = validated_data.get( 'sender_cancellation_signature') recipient_cancellation_signatures = validated_data.get( 'recipient_cancellation_signature') # state to save sender_cancellation_active_state_signature_records = [] sender_cancellation_active_state_records = [] recipient_cancellation_active_state_signature_records = [] recipient_cancellation_active_state_records = [] # make sure appropriate number of signatures was provided if swap_set.count() != len(sender_cancellation_signatures): raise serializers.ValidationError( detail= 'Wrong number of sender cancellation signatures, expected {} but got {}' .format(swap_set.count(), len(sender_cancellation_signatures)), code=ErrorCode.WRONG_NUMBER_OF_DEBIT_SIGNATURES) if swap_set.count() != len(recipient_cancellation_signatures): raise serializers.ValidationError( detail= 'Wrong number of recipient cancellation signatures, expected {} but got {}' .format(swap_set.count(), len(recipient_cancellation_signatures)), code=ErrorCode.WRONG_NUMBER_OF_CREDIT_SIGNATURES) sender_view_context = WalletTransferContext( wallet=current_swap.wallet, transfer=current_swap) debit_tx_set_tree = sender_view_context.optimized_authorized_transfers_tree( force_append=False, assume_active_state_exists=True) tx_set_hash = crypto.hex_value(debit_tx_set_tree.root_hash()) transfer_index = debit_tx_set_tree.merkle_tree_nonce_map.get( current_swap.nonce) transfer_proof = debit_tx_set_tree.proof(transfer_index) sender_highest_spendings, sender_highest_gains = sender_view_context.off_chain_actively_sent_received_amounts( eon_number=current_swap.eon_number, only_appended=False) matched_out, _ = current_swap.matched_amounts() sender_highest_gains += current_swap.amount - matched_out sender_cancellation_active_state = ActiveState( wallet=current_swap.wallet, updated_spendings=sender_highest_spendings, updated_gains=sender_highest_gains, tx_set_hash=tx_set_hash, tx_set_proof_hashes=transfer_proof, tx_set_index=transfer_index, eon_number=current_swap.eon_number) sender_cancellation_active_state_checksum = crypto.hex_value( sender_cancellation_active_state.checksum()) sender_cancellation_active_state_signature = Signature( wallet=current_swap.wallet, checksum=sender_cancellation_active_state_checksum, value=sender_cancellation_signatures[0].get('value')) if not sender_cancellation_active_state_signature.is_valid(): raise serializers.ValidationError( detail='', code=ErrorCode.INVALID_DEBIT_SIGNATURE) sender_cancellation_active_state_records.append( sender_cancellation_active_state) sender_cancellation_active_state_signature_records.append( sender_cancellation_active_state_signature) recipient_view_context = WalletTransferContext( wallet=current_swap.recipient, transfer=current_swap) credit_tx_set_tree = recipient_view_context.optimized_authorized_transfers_tree( force_append=False, assume_active_state_exists=True) tx_set_hash = crypto.hex_value(credit_tx_set_tree.root_hash()) transfer_index = credit_tx_set_tree.merkle_tree_nonce_map.get( current_swap.nonce) transfer_proof = credit_tx_set_tree.proof(transfer_index) recipient_highest_spendings, recipient_highest_gains = recipient_view_context.off_chain_actively_sent_received_amounts( eon_number=current_swap.eon_number, only_appended=False) recipient_cancellation_active_state = ActiveState( wallet=current_swap.recipient, updated_spendings=recipient_highest_spendings + current_swap.amount_swapped, updated_gains=recipient_highest_gains + current_swap.amount_swapped, tx_set_hash=tx_set_hash, tx_set_proof_hashes=transfer_proof, tx_set_index=transfer_index, eon_number=current_swap.eon_number) recipient_cancellation_active_state_checksum = crypto.hex_value( recipient_cancellation_active_state.checksum()) recipient_cancellation_active_state_signature = Signature( wallet=current_swap.recipient, checksum=recipient_cancellation_active_state_checksum, value=recipient_cancellation_signatures[0].get('value')) if not recipient_cancellation_active_state_signature.is_valid(): raise serializers.ValidationError( detail='', code=ErrorCode.INVALID_CREDIT_SIGNATURE) recipient_cancellation_active_state_records.append( recipient_cancellation_active_state) recipient_cancellation_active_state_signature_records.append( recipient_cancellation_active_state_signature) # calculate future spent, gained, empty tx set empty_tx_set_hash = crypto.hex_value(NODE_CACHE[0]['hash']) sender_future_spent_gained = max(sender_highest_spendings, sender_highest_gains) + 1 recipient_future_spent_gained = max( recipient_highest_spendings, recipient_highest_gains) + current_swap.amount_swapped + 1 for index in range(1, len(swap_set)): future_swap = swap_set[index] sender_cancellation_active_state = ActiveState( wallet=future_swap.wallet, updated_spendings=sender_future_spent_gained, updated_gains=sender_future_spent_gained, tx_set_hash=empty_tx_set_hash, # any dummy value tx_set_proof_hashes='', # any dummy value tx_set_index=0, eon_number=future_swap.eon_number) sender_cancellation_active_state_checksum = crypto.hex_value( sender_cancellation_active_state.checksum()) sender_cancellation_active_state_signature = Signature( wallet=future_swap.recipient, checksum=sender_cancellation_active_state_checksum, value=sender_cancellation_signatures[index].get('value')) if not sender_cancellation_active_state_signature.is_valid(): raise serializers.ValidationError( detail='', code=ErrorCode.INVALID_FUTURE_DEBIT_SIGNATURE) sender_cancellation_active_state_signature_records.append( sender_cancellation_active_state_signature) sender_cancellation_active_state_records.append( sender_cancellation_active_state) recipient_cancellation_active_state = ActiveState( wallet=future_swap.recipient, updated_spendings=recipient_future_spent_gained, updated_gains=recipient_future_spent_gained, tx_set_hash=empty_tx_set_hash, # any dummy value tx_set_proof_hashes='', # any dummy value tx_set_index=0, eon_number=future_swap.eon_number) recipient_cancellation_active_state_checksum = crypto.hex_value( recipient_cancellation_active_state.checksum()) recipient_cancellation_active_state_signature = Signature( wallet=future_swap.recipient, checksum=recipient_cancellation_active_state_checksum, value=recipient_cancellation_signatures[index].get( 'value')) if not recipient_cancellation_active_state_signature.is_valid( ): raise serializers.ValidationError( detail='', code=ErrorCode.INVALID_FUTURE_CREDIT_SIGNATURE) recipient_cancellation_active_state_signature_records.append( recipient_cancellation_active_state_signature) recipient_cancellation_active_state_records.append( recipient_cancellation_active_state) assert (len(swap_set) == len( sender_cancellation_active_state_signature_records)) assert (len(swap_set) == len( recipient_cancellation_active_state_signature_records)) Signature.objects.bulk_create( sender_cancellation_active_state_signature_records + recipient_cancellation_active_state_signature_records) with current_swap.lock( auto_renewal=False), current_swap.wallet.lock( auto_renewal=False), current_swap.recipient.lock( auto_renewal=False): for index in range(len(swap_set)): sender_cancellation_active_state_records[ index].wallet_signature = sender_cancellation_active_state_signature_records[ index] recipient_cancellation_active_state_records[ index].wallet_signature = recipient_cancellation_active_state_signature_records[ index] ActiveState.objects.bulk_create( sender_cancellation_active_state_records + recipient_cancellation_active_state_records) for index in range(len(swap_set)): swap_set[ index].sender_cancellation_active_state = sender_cancellation_active_state_records[ index] swap_set[ index].recipient_cancellation_active_state = recipient_cancellation_active_state_records[ index] if index > 0: swap_set[index].voided = True swap_set[index].appended = False swap_set[index].processed = True Transfer.objects.bulk_update(swap_set, [ 'sender_cancellation_active_state', 'recipient_cancellation_active_state', 'voided', 'appended', 'processed' ]) swap_set[0].sign_swap_cancellation( settings.HUB_OWNER_ACCOUNT_ADDRESS, settings.HUB_OWNER_ACCOUNT_KEY) swap_set[0].close(cancelled=True, appended=True) current_swap = swap_set[0] is_swap_cancelled = True if is_swap_cancelled: operator_celery.send_task('auditor.tasks.on_swap_cancellation', args=[current_swap.id]) return current_swap