Exemplo n.º 1
0
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))
Exemplo n.º 2
0
    def test_checkpoint_creation(self):
        self.eth_token = Token.objects.first()

        lqd_token_address = deploy_new_test_token(test_case=self)

        distribute_token_balance_to_addresses(
            test_case=self,
            token_address=lqd_token_address,
            recipients=testrpc_accounts.accounts)

        self.lqd_token = register_token(token_address=lqd_token_address,
                                        name='Liquidity',
                                        short_name='LQD',
                                        register_on_chain=True)

        send_queued_transactions()

        self.tokens = [self.eth_token, self.lqd_token]

        TokenPair.objects.create(token_from=self.eth_token,
                                 token_to=self.lqd_token)
        TokenPair.objects.create(token_from=self.lqd_token,
                                 token_to=self.eth_token)

        self.registered_accounts = {
            self.eth_token: register_testrpc_accounts(self,
                                                      token=self.eth_token),
            self.lqd_token: register_testrpc_accounts(self,
                                                      token=self.lqd_token)
        }

        for token in self.tokens:
            self.assertEqual(
                self.contract_interface.get_unmanaged_funds(token.address, 1),
                0)
            self.assertEqual(
                self.contract_interface.get_managed_funds(token.address, 1), 0)

        for token in self.tokens:
            create_deposits(self, testrpc_accounts.accounts, token)

        for token in self.tokens:
            self.assertEqual(
                self.contract_interface.get_unmanaged_funds(token.address, 1),
                self.contract_interface.get_total_balance(token.address))
            self.assertEqual(
                self.contract_interface.get_managed_funds(token.address, 1), 0)

        for account in testrpc_accounts.accounts[1:5]:
            eth_wallet = Wallet.objects.get(address=remove_0x_prefix(
                account.get('address')),
                                            token=self.eth_token)
            lqd_wallet = Wallet.objects.get(address=remove_0x_prefix(
                account.get('address')),
                                            token=self.lqd_token)

            eth_wallet_context = WalletTransferContext(wallet=eth_wallet,
                                                       transfer=None)
            lqd_wallet_context = WalletTransferContext(wallet=lqd_wallet,
                                                       transfer=None)

            account[
                'reduced_eth_balance'] = eth_wallet_context.available_funds_at_eon(
                    eon_number=1, only_appended=False) - 1
            account[
                'reduced_lqd_balance'] = lqd_wallet_context.available_funds_at_eon(
                    eon_number=1, only_appended=False)

            send_swap(  # Buy LQD at 0.5 ETH
                test_case=self,
                eon_number=1,
                account=account,
                token=self.eth_token,
                token_swapped=self.lqd_token,
                amount=1,
                amount_swapped=2,
                nonce=random.randint(1, 999999))

        for account in testrpc_accounts.accounts[5:9]:
            eth_wallet = Wallet.objects.get(address=remove_0x_prefix(
                account.get('address')),
                                            token=self.eth_token)
            lqd_wallet = Wallet.objects.get(address=remove_0x_prefix(
                account.get('address')),
                                            token=self.lqd_token)

            eth_wallet_context = WalletTransferContext(wallet=eth_wallet,
                                                       transfer=None)
            lqd_wallet_context = WalletTransferContext(wallet=lqd_wallet,
                                                       transfer=None)

            account[
                'reduced_eth_balance'] = eth_wallet_context.available_funds_at_eon(
                    eon_number=1, only_appended=False)
            account[
                'reduced_lqd_balance'] = lqd_wallet_context.available_funds_at_eon(
                    eon_number=1, only_appended=False) - 2

            send_swap(  # Sell LQD at 0.5 ETH
                test_case=self,
                eon_number=1,
                account=account,
                token=self.lqd_token,
                token_swapped=self.eth_token,
                amount=2,
                amount_swapped=1,
                nonce=random.randint(1, 999999))

        confirm_swaps_for_eon(operator_eon_number=1)
        cancel_finalize_swaps_for_eon(operator_eon_number=1)
        process_swaps_for_eon(operator_eon_number=1)
        for account in testrpc_accounts.accounts[1:5]:
            finalize_last_swap(test_case=self,
                               token=self.eth_token,
                               token_swapped=self.lqd_token,
                               account=account)
        for account in testrpc_accounts.accounts[5:9]:
            finalize_last_swap(test_case=self,
                               token=self.lqd_token,
                               token_swapped=self.eth_token,
                               account=account)
        confirm_swaps_for_eon(operator_eon_number=1)
        cancel_finalize_swaps_for_eon(operator_eon_number=1)
        process_swaps_for_eon(operator_eon_number=1)

        buyers_sellers = [
            testrpc_accounts.accounts[1:5], testrpc_accounts.accounts[5:9]
        ]
        for i in range(0, 2):
            for account in buyers_sellers[i]:
                eth_wallet = Wallet.objects.get(address=remove_0x_prefix(
                    account.get('address')),
                                                token=self.eth_token)
                lqd_wallet = Wallet.objects.get(address=remove_0x_prefix(
                    account.get('address')),
                                                token=self.lqd_token)

                eth_wallet_context = WalletTransferContext(wallet=eth_wallet,
                                                           transfer=None)
                lqd_wallet_context = WalletTransferContext(wallet=lqd_wallet,
                                                           transfer=None)

                eth_out, eth_in = eth_wallet_context.off_chain_actively_sent_received_amounts(
                    eon_number=1, only_appended=False)
                lqd_out, lqd_in = lqd_wallet_context.off_chain_actively_sent_received_amounts(
                    eon_number=1, only_appended=False)

                eth_out -= account['reduced_eth_balance']
                lqd_out -= account['reduced_lqd_balance']

                if i == 0:  # LQD buyers
                    assert eth_out == 1 and eth_in == 0, '{}/{}'.format(
                        eth_out, eth_in)
                    assert lqd_out - 2 == 0 and lqd_in - 2 == 2, '{}/{}'.format(
                        lqd_out, lqd_in)
                else:  # LQD sellers
                    assert lqd_out == 2 and lqd_in == 0, '{}/{}'.format(
                        lqd_out, lqd_in)
                    assert eth_out - 1 == 0 and eth_in - 1 == 1, '{}/{}'.format(
                        eth_out, eth_in)

        # Verify transfers were complete
        swaps = Transfer.objects.filter(swap=True)
        for transfer in swaps:
            self.assertTrue(transfer.is_fulfilled_swap())

        commit_eon(test_case=self, eon_number=1)

        advance_to_next_eon(test_case=self, eon_number=1)

        commit_eon(test_case=self, eon_number=2)
Exemplo n.º 3
0
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
Exemplo n.º 4
0
    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
Exemplo n.º 5
0
def finalize_swap(test_case: RPCTestCase,
                  swap: Transfer,
                  account,
                  expected_status=status.HTTP_200_OK,
                  eon_count=1):
    print('FINALIZING {} ({}/{})'.format(swap.id, int(swap.amount),
                                         int(swap.amount_swapped)))

    finalization_authorizations = []
    test_case.assertTrue(swap.complete)

    recipient_view_context = WalletTransferContext(wallet=swap.recipient,
                                                   transfer=swap)

    tx_set_tree = recipient_view_context.authorized_transfers_tree(
        only_appended=False, force_append=True)
    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=False)

    print("Finalize spent {} gained {}".format(highest_spendings,
                                               highest_gains))

    for state in ActiveState.objects.filter(wallet=swap.recipient,
                                            eon_number=swap.eon_number):
        print(state.id)
        print("Finalize spent {} gained {}".format(state.updated_spendings,
                                                   state.updated_gains))

    finalization_active_state = ActiveState(
        wallet=swap.recipient,
        updated_spendings=highest_spendings + swap.amount_swapped,
        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)

    finalization_authorizations.append({
        'value':
        encode_signature(
            sign_message(finalization_active_state.checksum(),
                         account.get('pk')))
    })

    for i in range(1, eon_count):
        future_spent_gained = max(highest_spendings,
                                  highest_gains) + swap.amount_swapped + 1
        empty_tx_set_hash = crypto.hex_value(NODE_CACHE[0]['hash'])
        finalization_active_state = ActiveState(
            wallet=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=swap.eon_number + i)

        finalization_authorizations.append({
            'value':
            encode_signature(
                sign_message(finalization_active_state.checksum(),
                             account.get('pk')))
        })

    # Make API Request
    url = reverse('finalize-swap-endpoint', kwargs={'pk': swap.id})
    data = {'finalization_signature': finalization_authorizations}

    # Send tx to server
    x = datetime.now()
    response = test_case.client.put(url, data, format='json')
    y = datetime.now()
    delta = y - x

    # Ensure the transaction was recorded
    test_case.assertEqual(response.status_code, expected_status,
                          response.content)

    print('FINALIZE Time: {}s'.format(delta))

    # Log time delta
    return delta
Exemplo n.º 6
0
def cancel_swap(test_case: RPCTestCase,
                swap: Transfer,
                account,
                expected_status=status.HTTP_200_OK,
                eon_count=1):
    sender_cancellation_authorizations = []
    recipient_cancellation_authorizations = []

    sender_view_context = WalletTransferContext(wallet=swap.wallet,
                                                transfer=swap)

    tx_set_tree = sender_view_context.authorized_transfers_tree(
        only_appended=False,
        force_append=False,
        assume_active_state_exists=True)
    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)

    sender_highest_spendings, sender_highest_gains = sender_view_context.off_chain_actively_sent_received_amounts(
        eon_number=swap.eon_number, only_appended=False)

    matched_out, _ = swap.matched_amounts()

    sender_highest_gains += swap.amount - matched_out

    sender_cancellation_active_state = ActiveState(
        wallet=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=swap.eon_number)

    recipient_view_context = WalletTransferContext(wallet=swap.recipient,
                                                   transfer=swap)

    tx_set_tree = recipient_view_context.authorized_transfers_tree(
        only_appended=False,
        force_append=False,
        assume_active_state_exists=True)
    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_highest_spendings, recipient_highest_gains = recipient_view_context.off_chain_actively_sent_received_amounts(
        eon_number=swap.eon_number, only_appended=False)

    recipient_cancellation_active_state = ActiveState(
        wallet=swap.recipient,
        updated_spendings=recipient_highest_spendings + swap.amount_swapped,
        updated_gains=recipient_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)

    sender_cancellation_authorizations.append({
        'value':
        encode_signature(
            sign_message(sender_cancellation_active_state.checksum(),
                         account.get('pk')))
    })
    recipient_cancellation_authorizations.append({
        'value':
        encode_signature(
            sign_message(recipient_cancellation_active_state.checksum(),
                         account.get('pk')))
    })

    for i in range(1, eon_count):
        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) + swap.amount_swapped + 1

        sender_cancellation_active_state = ActiveState(
            wallet=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=swap.eon_number + i)

        recipient_cancellation_active_state = ActiveState(
            wallet=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=swap.eon_number + i)

        sender_cancellation_authorizations.append({
            'value':
            encode_signature(
                sign_message(sender_cancellation_active_state.checksum(),
                             account.get('pk')))
        })
        recipient_cancellation_authorizations.append({
            'value':
            encode_signature(
                sign_message(recipient_cancellation_active_state.checksum(),
                             account.get('pk')))
        })

    # Make API Request
    url = reverse('cancel-swap-endpoint', kwargs={'pk': swap.id})
    data = {
        'sender_cancellation_signature': sender_cancellation_authorizations,
        'recipient_cancellation_signature':
        recipient_cancellation_authorizations
    }

    # Send tx to server
    x = datetime.now()
    response = test_case.client.put(url, data, format='json')
    y = datetime.now()
    delta = y - x

    # Ensure the transaction was recorded
    test_case.assertEqual(response.status_code, expected_status,
                          response.content)

    print('CANCEL Time: {}s'.format(delta))

    # Log time delta
    return delta
Exemplo n.º 7
0
def send_swap(test_case: RPCTestCase,
              eon_number,
              account,
              token,
              token_swapped,
              amount,
              amount_swapped,
              nonce,
              expected_status=status.HTTP_201_CREATED,
              eon_count=1,
              sell_order=True):
    # Sender account
    sender_wallet = Wallet.objects.get(address=remove_0x_prefix(
        account.get('address')),
                                       token=token)
    # Recipient account
    recipient_wallet = Wallet.objects.get(address=remove_0x_prefix(
        account.get('address')),
                                          token=token_swapped)

    sender_wallet_context = WalletTransferContext(wallet=sender_wallet,
                                                  transfer=None)
    recipient_wallet_context = WalletTransferContext(wallet=recipient_wallet,
                                                     transfer=None)

    initial_sender_balance = sender_wallet_context.available_funds_at_eon(
        eon_number=eon_number, only_appended=False)
    initial_recipient_balance = recipient_wallet_context.available_funds_at_eon(
        eon_number=eon_number, only_appended=False)

    if initial_sender_balance < amount:
        send_transaction(test_case=test_case,
                         eon_number=eon_number,
                         sender=testrpc_accounts.accounts[0],
                         recipient=account,
                         amount=amount - initial_sender_balance,
                         nonce=random.randint(1, 999999),
                         token=token)
    if initial_sender_balance > amount:
        # clear sender account
        send_transaction(test_case=test_case,
                         eon_number=eon_number,
                         sender=account,
                         recipient=testrpc_accounts.accounts[0],
                         amount=initial_sender_balance - amount,
                         nonce=random.randint(1, 999999),
                         token=token)
    if initial_recipient_balance > 0:
        # clear recipient account
        send_transaction(test_case=test_case,
                         eon_number=eon_number,
                         sender=account,
                         recipient=testrpc_accounts.accounts[0],
                         amount=initial_recipient_balance,
                         nonce=random.randint(1, 999999),
                         token=token_swapped)

    sender_balance = sender_wallet_context.available_funds_at_eon(
        eon_number=eon_number, only_appended=False)
    recipient_balance = recipient_wallet_context.available_funds_at_eon(
        eon_number=eon_number, only_appended=False)

    test_case.assertEqual(sender_balance, amount)
    test_case.assertEqual(recipient_balance, 0)

    debit_balance_signatures = []
    debit_signatures = []
    credit_balance_signatures = []
    credit_signatures = []
    fulfillment_signatures = []

    for i in range(eon_count):
        swap = Transfer(wallet=sender_wallet,
                        amount=amount,
                        eon_number=eon_number + i,
                        recipient=recipient_wallet,
                        amount_swapped=amount_swapped,
                        nonce=nonce,
                        processed=False,
                        complete=False,
                        swap=True)

        sender_wallet_context = WalletTransferContext(wallet=sender_wallet,
                                                      transfer=swap)
        recipient_wallet_context = WalletTransferContext(
            wallet=recipient_wallet, transfer=swap)

        sender_highest_spent, sender_highest_gained = sender_wallet_context.off_chain_actively_sent_received_amounts(
            eon_number=eon_number + i, only_appended=False)

        if i == 0:
            tx_set_tree = sender_wallet_context.authorized_transfers_tree(
                only_appended=False, force_append=True)
        else:
            tx_set_tree = WalletTransferContext.authorized_transfers_tree_from_list(
                [
                    swap.shorthand(sender_wallet_context,
                                   is_last_transfer=True,
                                   starting_balance=sender_balance)
                ])

        tx_set_hash = hex_value(tx_set_tree.root_hash())

        debiting_active_state = ActiveState(
            wallet=sender_wallet,
            updated_spendings=sender_highest_spent + amount,
            updated_gains=sender_highest_gained,
            eon_number=eon_number + i,
            tx_set_hash=tx_set_hash)
        debiting_active_state_authorization = sign_message(
            debiting_active_state.checksum(), account.get('pk'))

        debiting_active_state_signature = Signature(
            wallet=sender_wallet,
            checksum=hex_value(debiting_active_state.checksum()),
            value=encode_signature(debiting_active_state_authorization))

        test_case.assertTrue(debiting_active_state_signature.is_valid())

        debit_concise_balance_marker = MinimumAvailableBalanceMarker(
            wallet=sender_wallet, eon_number=eon_number + i, amount=0)
        debit_concise_balance_marker_authorization = sign_message(
            debit_concise_balance_marker.checksum(), account.get('pk'))

        debit_concise_balance_marker_signature = Signature(
            wallet=sender_wallet,
            checksum=hex_value(debit_concise_balance_marker.checksum()),
            value=encode_signature(debit_concise_balance_marker_authorization))

        test_case.assertTrue(debit_concise_balance_marker_signature.is_valid())

        recipient_highest_spent, recipient_highest_gained = recipient_wallet_context.off_chain_actively_sent_received_amounts(
            eon_number=eon_number + i, only_appended=False)

        if i == 0:
            tx_set_tree = recipient_wallet_context.authorized_transfers_tree(
                only_appended=False, force_append=True)
        else:
            tx_set_tree = WalletTransferContext.authorized_transfers_tree_from_list(
                [
                    swap.shorthand(recipient_wallet_context,
                                   is_last_transfer=True,
                                   starting_balance=recipient_balance)
                ])

        tx_set_hash = hex_value(tx_set_tree.root_hash())

        crediting_active_state = ActiveState(
            wallet=recipient_wallet,
            updated_spendings=recipient_highest_spent,
            updated_gains=recipient_highest_gained,
            eon_number=eon_number + i,
            tx_set_hash=tx_set_hash)
        crediting_active_state_authorization = sign_message(
            crediting_active_state.checksum(), account.get('pk'))

        crediting_active_state_signature = Signature(
            wallet=recipient_wallet,
            checksum=hex_value(crediting_active_state.checksum()),
            value=encode_signature(crediting_active_state_authorization))

        test_case.assertTrue(crediting_active_state_signature.is_valid())

        credit_concise_balance_marker = MinimumAvailableBalanceMarker(
            wallet=recipient_wallet, eon_number=eon_number + i, amount=0)
        credit_concise_balance_marker_authorization = sign_message(
            credit_concise_balance_marker.checksum(), account.get('pk'))

        credit_concise_balance_marker_signature = Signature(
            wallet=recipient_wallet,
            checksum=hex_value(credit_concise_balance_marker.checksum()),
            value=encode_signature(
                credit_concise_balance_marker_authorization))

        test_case.assertTrue(
            credit_concise_balance_marker_signature.is_valid())

        swap.processed, swap.complete = True, True

        if i == 0:
            tx_set_tree = recipient_wallet_context.authorized_transfers_tree(
                only_appended=False, force_append=True)
        else:
            tx_set_tree = WalletTransferContext.authorized_transfers_tree_from_list(
                [
                    swap.shorthand(recipient_wallet_context,
                                   is_last_transfer=True,
                                   starting_balance=0)
                ])

        tx_set_hash = hex_value(tx_set_tree.root_hash())

        recipient_fulfillment_active_state = ActiveState(
            wallet=recipient_wallet,
            updated_spendings=recipient_highest_spent,
            updated_gains=recipient_highest_gained + amount_swapped,
            eon_number=eon_number + i,
            tx_set_hash=tx_set_hash)
        recipient_fulfillment_active_state_authorization = sign_message(
            recipient_fulfillment_active_state.checksum(), account.get('pk'))
        swap.processed, swap.complete = False, False

        recipient_fulfillment_active_state_signature = Signature(
            wallet=recipient_wallet,
            checksum=hex_value(recipient_fulfillment_active_state.checksum()),
            value=encode_signature(
                recipient_fulfillment_active_state_authorization))

        test_case.assertTrue(
            recipient_fulfillment_active_state_signature.is_valid())

        debit_balance_signatures.append({
            'value':
            encode_signature(debit_concise_balance_marker_authorization)
        })
        debit_signatures.append(
            {'value': encode_signature(debiting_active_state_authorization)})
        credit_balance_signatures.append({
            'value':
            encode_signature(credit_concise_balance_marker_authorization)
        })
        credit_signatures.append(
            {'value': encode_signature(crediting_active_state_authorization)})
        fulfillment_signatures.append({
            'value':
            encode_signature(recipient_fulfillment_active_state_authorization)
        })

    # Make API Request
    url = reverse('swap-endpoint')
    data = {
        'debit_signature': debit_signatures,
        'debit_balance_signature': debit_balance_signatures,
        'credit_signature': credit_signatures,
        'credit_balance_signature': credit_balance_signatures,
        'credit_fulfillment_signature': fulfillment_signatures,
        'eon_number': eon_number,
        'amount': amount,
        'amount_swapped': amount_swapped,
        'nonce': nonce,
        'wallet': {
            'address': sender_wallet.address,
            'token': sender_wallet.token.address,
        },
        'recipient': {
            'address': recipient_wallet.address,
            'token': recipient_wallet.token.address,
        },
        'sell_order': sell_order
    }

    # Send tx to server
    x = datetime.now()
    response = test_case.client.post(url, data, format='json')
    y = datetime.now()
    delta = y - x

    # Ensure the transaction was recorded
    test_case.assertEqual(response.status_code, expected_status,
                          response.content)

    print('SWAP Time: {}s for {}/{}'.format(delta, amount, amount_swapped))

    # assert that swap created for current eon is confirmed
    tx = json.loads(response.content)
    swap = Transfer.objects.get(id=tx['id'])
    test_case.assertEqual(swap.eon_number, eon_number)
    test_case.assertTrue(swap.is_signed_by_operator())

    # Log time delta
    return delta
Exemplo n.º 8
0
    def create(self, validated_data):
        wallet = validated_data.pop('wallet')
        recipient = validated_data.pop('recipient')

        if not TokenPair.objects.filter(token_from=wallet.token,
                                        token_to=recipient.token).exists():
            raise serializers.ValidationError(
                detail='', code=ErrorCode.TOKEN_PAIR_BLOCKED)

        # swap data
        valid_eons = validated_data.pop('valid_eons')
        swap_amount = validated_data.pop('amount')
        swap_nonce = validated_data.pop('nonce')
        sell_order = validated_data.pop('sell_order', True)
        swap_amount_swapped = validated_data.pop('amount_swapped')
        debit_signatures = validated_data.pop('debit_signature')
        debit_balance_signatures = validated_data.pop(
            'debit_balance_signature')
        credit_balance_signatures = validated_data.pop(
            'credit_balance_signature')
        credit_signatures = validated_data.pop('credit_signature')
        recipient_fulfillment_signatures = validated_data.pop(
            'credit_fulfillment_signature')

        # common transaction id
        tx_id = uuid.uuid4()
        tx_time = timezone.now()

        # cached items to be used later
        sender_available_balance = 0
        recipient_available_balance = 0

        swap_set = []

        debit_tx_set_index = []
        credit_tx_set_index = []
        # recipient_fulfillment_tx_set_index = []

        debit_tx_set_cache = []
        credit_tx_set_cache = []
        # recipient_fulfillment_tx_set_cache = []

        debit_balance_signature_records = []
        credit_balance_signature_records = []
        debit_signature_records = []
        credit_signature_records = []
        recipient_fulfillment_signature_records = []

        debit_balance_records = []
        credit_balance_records = []
        debit_active_state_records = []
        credit_active_state_records = []
        recipient_fulfillment_active_state_records = []

        initial_swap_confirmed = False

        # get current eon
        current_eon = LocalViewInterface.latest().eon_number()

        # initial swap 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)

        wallets = sorted([wallet, recipient], key=lambda w: w.token.id)
        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='Checkpoint was already created for this eon.',
                    code=ErrorCode.EON_NUMBER_OUT_OF_SYNC)

            for eon_number in range(current_eon, current_eon + valid_eons):
                swap = Transfer(tx_id=tx_id,
                                wallet=wallet,
                                amount=swap_amount,
                                eon_number=eon_number,
                                recipient=recipient,
                                nonce=swap_nonce,
                                amount_swapped=swap_amount_swapped,
                                swap=True,
                                sell_order=sell_order,
                                time=tx_time)

                wallet_view_context = WalletTransferContext(wallet=wallet,
                                                            transfer=swap)
                recipient_view_context = WalletTransferContext(
                    wallet=recipient, transfer=swap)

                if eon_number == current_eon:
                    # 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 swap consistency
                    can_spend, sender_available_balance = 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)

                    # Ensure that sender balance is exactly equal to total outgoing amount
                    if sender_available_balance != swap.amount:
                        raise serializers.ValidationError(
                            detail=
                            'Sender balance should be exactly equal to outgoing swap amount, {} != {}.'
                            .format(sender_available_balance, swap.amount),
                            code=ErrorCode.DEBIT_WALLET_BALANCE_AMOUNT_MISMATCH
                        )

                    # Ensure that recipient balance is zero
                    recipient_available_balance = recipient_view_context.available_funds_at_eon(
                        eon_number=eon_number, only_appended=False)
                    if recipient_available_balance != 0:
                        raise serializers.ValidationError(
                            detail='Recipient balance should be exactly zero.',
                            code=ErrorCode.CREDIT_WALLET_BALANCE_NOT_ZERO)

                    current_eon_swap = swap
                    sender_highest_spendings, sender_highest_gains = wallet_view_context.off_chain_actively_sent_received_amounts(
                        eon_number=swap.eon_number, only_appended=False)
                    recipient_highest_spendings, recipient_highest_gains = recipient_view_context.off_chain_actively_sent_received_amounts(
                        eon_number=swap.eon_number, only_appended=False)
                else:
                    sender_highest_spendings, sender_highest_gains = 0, 0
                    recipient_highest_spendings, recipient_highest_gains = 0, 0

                # Minimum Debit Balance Marker
                debit_balance_marker_signature_data = debit_balance_signatures[
                    eon_number - current_eon]
                debit_balance_marker = MinimumAvailableBalanceMarker(
                    wallet=wallet, eon_number=eon_number, amount=0)

                debit_balance_marker_checksum = crypto.hex_value(
                    debit_balance_marker.checksum())
                debit_balance_marker_signature = Signature(
                    wallet=wallet,
                    checksum=debit_balance_marker_checksum,
                    value=debit_balance_marker_signature_data.get('value'))
                if not debit_balance_marker_signature.is_valid():
                    raise serializers.ValidationError(
                        detail='',
                        code=ErrorCode.INVALID_DEBIT_BALANCE_SIGNATURE)

                # Minimum Credit Balance Marker
                credit_balance_marker_signature_data = credit_balance_signatures[
                    eon_number - current_eon]
                credit_balance_marker = MinimumAvailableBalanceMarker(
                    wallet=recipient, eon_number=eon_number, amount=0)

                credit_balance_marker_checksum = crypto.hex_value(
                    credit_balance_marker.checksum())
                credit_balance_marker_signature = Signature(
                    wallet=recipient,
                    checksum=credit_balance_marker_checksum,
                    value=credit_balance_marker_signature_data.get('value'))
                if not credit_balance_marker_signature.is_valid():
                    raise serializers.ValidationError(
                        detail='',
                        code=ErrorCode.INVALID_CREDIT_BALANCE_SIGNATURE)
                assert (sender_available_balance == swap.amount)
                # Debit Authorization
                debit_active_state_signature_data = debit_signatures[
                    eon_number - current_eon]
                debit_active_state, \
                    debit_active_state_signature, \
                    debit_transfer_index, \
                    debit_transfer_cache = check_active_state_signature(
                        swap,
                        wallet,
                        debit_active_state_signature_data,
                        eon_number > current_eon,
                        sender_available_balance,
                        sender_highest_spendings,
                        sender_highest_gains,
                        signature_type=SignatureType.DEBIT)

                # Credit Authorization
                credit_active_state_signature_data = credit_signatures[
                    eon_number - current_eon]
                credit_active_state, \
                    credit_active_state_signature, \
                    credit_transfer_index, \
                    credit_transfer_cache = check_active_state_signature(
                        swap,
                        recipient,
                        credit_active_state_signature_data,
                        eon_number > current_eon,
                        recipient_available_balance,
                        recipient_highest_spendings,
                        recipient_highest_gains,
                        signature_type=SignatureType.CREDIT)

                # Finality Authorization
                recipient_fulfillment_active_state_signature_data = recipient_fulfillment_signatures[
                    eon_number - current_eon]
                recipient_fulfillment_active_state, \
                    recipient_fulfillment_active_state_signature, \
                    recipient_fulfillment_transfer_index, \
                    recipient_fulfillment_transfer_cache = check_active_state_signature(
                        swap,
                        recipient,
                        recipient_fulfillment_active_state_signature_data,
                        eon_number > current_eon,
                        recipient_available_balance,
                        recipient_highest_spendings,
                        recipient_highest_gains,
                        signature_type=SignatureType.FULFILLMENT)

                # accumulate records to be saved
                debit_balance_signature_records.append(
                    debit_balance_marker_signature)
                credit_balance_signature_records.append(
                    credit_balance_marker_signature)
                debit_signature_records.append(debit_active_state_signature)
                credit_signature_records.append(credit_active_state_signature)
                recipient_fulfillment_signature_records.append(
                    recipient_fulfillment_active_state_signature)

                debit_balance_records.append(debit_balance_marker)
                credit_balance_records.append(credit_balance_marker)
                debit_active_state_records.append(debit_active_state)
                credit_active_state_records.append(credit_active_state)
                recipient_fulfillment_active_state_records.append(
                    recipient_fulfillment_active_state)

                debit_tx_set_index.append(debit_transfer_index)
                credit_tx_set_index.append(credit_transfer_index)
                # recipient_fulfillment_tx_set_index.append(recipient_fulfillment_transfer_index)

                debit_tx_set_cache.append(debit_transfer_cache)
                credit_tx_set_cache.append(credit_transfer_cache)
                # recipient_fulfillment_tx_set_cache.append(recipient_fulfillment_transfer_cache)
                swap_set.append(swap)

            assert (swap_set[0] is not None
                    and swap_set[0].eon_number == current_eon)
            assert (len(swap_set) == valid_eons)

            # locking context covers saving the state as well to make sure checkpoint creation is consistent
            with transaction.atomic():
                Signature.objects.bulk_create(
                    debit_balance_signature_records +
                    credit_balance_signature_records +
                    debit_signature_records + credit_signature_records +
                    recipient_fulfillment_signature_records)

                for index in range(valid_eons):
                    debit_balance_records[
                        index].signature = debit_balance_signature_records[
                            index]
                    credit_balance_records[
                        index].signature = credit_balance_signature_records[
                            index]
                    debit_active_state_records[
                        index].wallet_signature = debit_signature_records[
                            index]
                    credit_active_state_records[
                        index].wallet_signature = credit_signature_records[
                            index]
                    recipient_fulfillment_active_state_records[
                        index].wallet_signature = recipient_fulfillment_signature_records[
                            index]

                ActiveState.objects.bulk_create(
                    debit_active_state_records + credit_active_state_records +
                    recipient_fulfillment_active_state_records)

                MinimumAvailableBalanceMarker.objects.bulk_create(
                    debit_balance_records + credit_balance_records)

                for index in range(valid_eons):
                    swap_set[
                        index].sender_active_state = debit_active_state_records[
                            index]
                    swap_set[
                        index].recipient_active_state = credit_active_state_records[
                            index]
                    swap_set[
                        index].recipient_fulfillment_active_state = recipient_fulfillment_active_state_records[
                            index]
                    swap_set[
                        index].sender_balance_marker = debit_balance_records[
                            index]

                    swap_set[
                        index].sender_starting_balance = sender_available_balance
                    swap_set[
                        index].recipient_starting_balance = recipient_available_balance

                    # cache swap index in sender active set
                    swap_set[index].sender_merkle_index = debit_tx_set_index[
                        index]
                    # cache swap index in recipient active set
                    swap_set[
                        index].recipient_merkle_index = credit_tx_set_index[
                            index]
                    # cache active set merkle mountains height array and hash array for sender active set
                    swap_set[index].sender_merkle_hash_cache, swap_set[
                        index].sender_merkle_height_cache = debit_tx_set_cache[
                            index]
                    # cache active set merkle mountains height array and hash array for recipient active set
                    swap_set[index].recipient_merkle_hash_cache, swap_set[
                        index].recipient_merkle_height_cache = debit_tx_set_cache[
                            index]

                Transfer.objects.bulk_create(swap_set)

                swap_set[0].sign_swap(settings.HUB_OWNER_ACCOUNT_ADDRESS,
                                      settings.HUB_OWNER_ACCOUNT_KEY)
                initial_swap_confirmed = True

        if initial_swap_confirmed:
            operator_celery.send_task('auditor.tasks.on_swap_confirmation',
                                      args=[swap_set[0].id])

        return swap_set[0]
Exemplo n.º 9
0
    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
Exemplo n.º 10
0
    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
Exemplo n.º 11
0
def send_transaction(test_case,
                     eon_number,
                     sender,
                     recipient,
                     amount,
                     nonce,
                     token,
                     expected_status=status.HTTP_201_CREATED):
    # Sender account
    sender_wallet = Wallet.objects.get(address=remove_0x_prefix(
        sender.get('address')),
                                       token=token)
    # Recipient account
    recipient_wallet = Wallet.objects.get(address=remove_0x_prefix(
        recipient.get('address')),
                                          token=token)

    transfer = Transfer(wallet=sender_wallet,
                        amount=amount,
                        eon_number=eon_number,
                        recipient=recipient_wallet,
                        nonce=nonce,
                        passive=True)

    sender_view_context = WalletTransferContext(wallet=sender_wallet,
                                                transfer=transfer)
    sender_highest_spent, sender_highest_gained = sender_view_context.off_chain_actively_sent_received_amounts(
        eon_number=eon_number, only_appended=False)
    updated_spendings = sender_highest_spent + amount

    # Authorize transaction
    transfer_set_root = hex_value(
        sender_view_context.authorized_transfers_tree_root(only_appended=False,
                                                           force_append=True))
    active_state = ActiveState(wallet=sender_wallet,
                               updated_spendings=updated_spendings,
                               updated_gains=sender_highest_gained,
                               eon_number=eon_number,
                               tx_set_hash=transfer_set_root)
    sender_active_state_authorization = sign_message(active_state.checksum(),
                                                     sender.get('pk'))

    sender_active_state_signature = Signature(
        wallet=sender_wallet,
        checksum=hex_value(active_state.checksum()),
        value=encode_signature(sender_active_state_authorization))

    test_case.assertTrue(sender_active_state_signature.is_valid())

    _, available_balance = sender_view_context.can_send_transfer(
        current_eon_number=eon_number, using_only_appended_funds=False)

    new_balance = available_balance - transfer.amount

    concise_balance_marker = MinimumAvailableBalanceMarker(
        wallet=sender_wallet, eon_number=eon_number, amount=new_balance)
    sender_concise_balance_marker_authorization = sign_message(
        concise_balance_marker.checksum(), sender.get('pk'))

    concise_balance_marker_signature = Signature(
        wallet=sender_wallet,
        checksum=hex_value(concise_balance_marker.checksum()),
        value=encode_signature(sender_concise_balance_marker_authorization))

    test_case.assertTrue(concise_balance_marker_signature.is_valid())

    print("Sender view:")
    print("available_balance:", available_balance)
    print("transfer_set_root:", transfer_set_root)
    print("active_state.updated_spendings:", active_state.updated_spendings)
    print("active_state.updated_gains:", active_state.updated_gains)

    # Make API Request
    url = reverse('transfer-endpoint')
    data = {
        'debit_signature': {
            'value': encode_signature(sender_active_state_authorization),
        },
        'debit_balance_signature': {
            'value':
            encode_signature(sender_concise_balance_marker_authorization),
        },
        'debit_balance': new_balance,
        'eon_number': eon_number,
        'amount': amount,
        'nonce': nonce,
        'wallet': {
            'address': sender_wallet.address,
            'token': sender_wallet.token.address,
        },
        'recipient': {
            'address': recipient_wallet.address,
            'token': recipient_wallet.token.address,
        },
    }

    # Send tx to server
    x = datetime.datetime.now()
    response = test_case.client.post(url, data, format='json')
    y = datetime.datetime.now()
    delta = y - x

    # Ensure the transaction was recorded
    test_case.assertEqual(response.status_code, expected_status,
                          '\n'.join([url,
                                     str(data),
                                     str(response.content)]))

    print('TX Time: {}s for {}'.format(delta, amount))

    # fo rpassive transfer assert that transaction is confirmed
    tx = json.loads(response.content)
    transfer = Transfer.objects.get(id=tx['id'])
    test_case.assertTrue(transfer.complete)
    test_case.assertTrue(transfer.appended)
    test_case.assertNotEqual(transfer.sender_active_state.operator_signature,
                             None)

    # Log time delta
    return delta