Beispiel #1
0
def create_token_commitment_for_eon(token: Token, eon_number):
    logger.info('Creating Token Commitment for {} at {}'.format(
        token.address, eon_number))
    last_eon_number = eon_number - 1

    with transaction.atomic():
        wallets = Wallet.objects\
            .filter(
                token=token,
                registration_operator_authorization__isnull=False,
                trail_identifier__isnull=False)\
            .order_by('trail_identifier')

        new_balances = []
        left, right = 0, 0

        for wallet in wallets:
            with wallet.lock(auto_renewal=True):
                wallet_transfer_context = WalletTransferContext(
                    wallet=wallet, transfer=None)

                last_transfer, last_transfer_is_outgoing = wallet_transfer_context.last_appended_active_transfer(
                    eon_number=last_eon_number)

                last_transfer_active_state = None
                if last_transfer is not None and last_transfer.is_open_swap():
                    last_transfer.retire_swap()

                if last_transfer is not None:
                    last_transfer_active_state = WalletTransferContext.appropriate_transfer_active_state(
                        transfer=last_transfer,
                        is_outgoing=last_transfer_is_outgoing)

                available_funds = wallet_transfer_context.available_funds_at_eon(
                    eon_number=last_eon_number,
                    only_appended=True)

                right = left + available_funds
                assert right >= left, 'Wallet {} Token {} Balance {}'.format(
                    wallet.address, token.address, available_funds)

                passive_checksum, passive_amount, passive_marker = wallet_transfer_context.get_passive_values(
                    eon_number=last_eon_number)

                new_balances.append({
                    'contract': settings.HUB_LQD_CONTRACT_ADDRESS,
                    'token': token.address,
                    'wallet': wallet.address,
                    'left': left,
                    'right': right,
                    'active_state_checksum': last_transfer_active_state.checksum() if last_transfer_active_state is not None else b'\0'*32,
                    'active_state': last_transfer_active_state,
                    'passive_checksum': passive_checksum,
                    'passive_amount': passive_amount,
                    'passive_marker': passive_marker,
                })
                left = right

                last_incoming_passive_transfer = wallet_transfer_context.last_appended_incoming_passive_transfer(
                    eon_number=last_eon_number)
                if last_incoming_passive_transfer:
                    wallet_transfer_context = WalletTransferContext(
                        wallet=wallet, transfer=last_incoming_passive_transfer)

                    passive_eon_transfers_list = wallet_transfer_context.incoming_passive_transfers_list(
                        only_appended=True,
                        force_append=False)
                    passive_transfers_merkle_tree = wallet_transfer_context.incoming_passive_transfers_tree(
                        only_appended=True,
                        force_append=False)

                    for index, incoming_passive_transfer in enumerate(passive_eon_transfers_list):

                        final_transfer_index = index
                        final_transfer_membership_proof = passive_transfers_merkle_tree.proof(
                            final_transfer_index)
                        final_transfer_membership_proof_chain = final_transfer_membership_proof.get(
                            "chain")
                        final_transfer_membership_proof_values = final_transfer_membership_proof.get(
                            "values")

                        assert incoming_passive_transfer.final_receipt_hashes is None
                        assert incoming_passive_transfer.final_receipt_index is None
                        assert incoming_passive_transfer.final_receipt_values is None

                        incoming_passive_transfer.final_receipt_hashes = final_transfer_membership_proof_chain
                        incoming_passive_transfer.final_receipt_index = final_transfer_index
                        incoming_passive_transfer.final_receipt_values = final_transfer_membership_proof_values

                        incoming_passive_transfer.save()

                if last_transfer_active_state is None:
                    continue

                wallet_transfer_context = WalletTransferContext(
                    wallet=wallet, transfer=last_transfer)
                starting_balance = int(
                    wallet_transfer_context.starting_balance_in_eon(last_eon_number))

                # if last active transfer is a multi eon swap
                # starting balance included in every tx checksum should be set to the cached starting balance
                # this way checkpoint state will match signed active state
                if last_transfer.is_swap() and not last_transfer.cancelled:
                    if Transfer.objects.filter(eon_number=last_transfer.eon_number-1, tx_id=last_transfer.tx_id).exists():
                        matched_out, matched_in = last_transfer.matched_amounts(
                            all_eons=True)
                        current_matched_out, current_matched_in = last_transfer.matched_amounts(
                            all_eons=False)
                        if last_transfer_is_outgoing:
                            sender_starting_balance = last_transfer.sender_starting_balance

                            # current eon's starting balance should be equal to
                            # cached starting balance - committed matched out amount in past rounds
                            assert(starting_balance == sender_starting_balance -
                                   matched_out + current_matched_out)
                            starting_balance = sender_starting_balance
                        else:
                            recipient_starting_balance = last_transfer.recipient_starting_balance

                            # current eon's starting balance should be equal to
                            # cached starting balance + committed matched in amount in past rounds
                            assert(
                                starting_balance == recipient_starting_balance + matched_in - current_matched_in)
                            starting_balance = recipient_starting_balance

                confirmed_eon_transfers_list = wallet_transfer_context.authorized_transfers_list(
                    only_appended=True,
                    force_append=False)
                confirmed_eon_transfers_list_shorthand = wallet_transfer_context.authorized_transfers_list_shorthand(
                    only_appended=True,
                    force_append=False,
                    last_transfer_is_finalized=False,
                    starting_balance=starting_balance)
                transaction_merkle_tree = TransactionMerkleTree(
                    confirmed_eon_transfers_list_shorthand)
                transaction_merkle_tree_root = hex_value(
                    transaction_merkle_tree.root_hash())

                assert transaction_merkle_tree_root == last_transfer_active_state.tx_set_hash,\
                    '{}/{}'.format(transaction_merkle_tree_root,
                                   last_transfer_active_state.tx_set_hash)

                for confirmed_incoming_transfer in confirmed_eon_transfers_list:
                    if confirmed_incoming_transfer.recipient != wallet:
                        continue

                    final_transfer_index = transaction_merkle_tree.merkle_tree_nonce_map.get(
                        confirmed_incoming_transfer.nonce)
                    final_transfer_membership_proof_chain = transaction_merkle_tree.proof(
                        final_transfer_index)

                    assert confirmed_incoming_transfer.final_receipt_hashes is None
                    assert confirmed_incoming_transfer.final_receipt_index is None

                    confirmed_incoming_transfer.final_receipt_hashes = final_transfer_membership_proof_chain
                    confirmed_incoming_transfer.final_receipt_index = final_transfer_index

                    confirmed_incoming_transfer.save()

        managed_funds = 0
        if eon_number > 1:
            last_eon = LocalViewInterface.confirmed(eon_number=last_eon_number)
            pending_withdrawals_until_last_eon = \
                WithdrawalRequest.objects\
                .filter(wallet__token=token, eon_number__lte=last_eon_number, slashed=False)\
                .filter(Q(withdrawal__isnull=True) | Q(withdrawal__block__gt=last_eon.block))

            if not pending_withdrawals_until_last_eon.exists():
                last_eon_pending_withdrawals = 0
            else:
                last_eon_pending_withdrawals = pending_withdrawals_until_last_eon\
                    .aggregate(Sum('amount')) \
                    .get('amount__sum')

            total_token_balance = last_eon.contractledgerstate_set.get(
                token=token).total_balance
            managed_funds = total_token_balance - last_eon_pending_withdrawals

        if right < managed_funds:
            logger.warning('UNCLAIMED FUNDS: {} in {}'.format(
                managed_funds - right, token.address))
            send_admin_email(
                subject='Soft TokenCommitment Warning: Extra funds',
                content='There are some additional funds in the balance pool that belong to no one: {} of {}'
                .format(managed_funds - right, token.address))
            altered_balances = new_balances + [{
                'contract': settings.HUB_LQD_CONTRACT_ADDRESS,
                'token': token.address,
                'wallet': settings.HUB_OWNER_ACCOUNT_ADDRESS,
                'left': left,
                'right': managed_funds,
                'active_state_checksum': b'\0'*32,
                'active_state': None,
                'passive_checksum': b'\0'*32,
                'passive_amount': 0,
                'passive_marker': 0,
            }]
            new_merkle_tree = MerkleTree(altered_balances, managed_funds)
            right = managed_funds
        else:
            if right > managed_funds:
                logger.error('OVERCLAIMING FUNDS!! {} > {} in {}'.format(
                    right, managed_funds, token.address))
                send_admin_email(
                    subject='HARD Checkpoint Error: OVERCLAIMING!',
                    content='OVERCLAIMING FUNDS!! {} > {} in {}'.format(right, managed_funds, token.address))
            new_merkle_tree = MerkleTree(new_balances, right)

        bulk_manager = BulkCreateManager(chunk_size=500)
        for index, balance in enumerate(new_balances):
            if not balance.get('wallet') or balance.get('wallet') == '0x0000000000000000000000000000000000000000':
                continue

            merkle_proof = new_merkle_tree.proof(index)

            wallet = Wallet.objects.get(
                token=token,
                address=remove_0x_prefix(balance.get('wallet')))

            # TODO verify validity through RPC prior to insertion

            assert(wallet.trail_identifier == index)

            # create records in batches
            bulk_manager.add(
                ExclusiveBalanceAllotment(
                    wallet=wallet,
                    eon_number=eon_number,
                    left=balance.get('left'),
                    right=balance.get('right'),
                    merkle_proof_hashes=merkle_proof.get('chain'),
                    merkle_proof_values=merkle_proof.get('values'),
                    merkle_proof_trail=index,
                    active_state=balance.get('active_state')
                )
            )
        # make sure remaining batch is added
        bulk_manager.done()

        token_commitment = TokenCommitment.objects.create(
            token=token,
            merkle_root=hex_value(new_merkle_tree.root_hash()),
            upper_bound=right)

        return token_commitment
Beispiel #2
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
Beispiel #3
0
def respond_to_challenges():
    contract_interface = NOCUSTContractInterface()

    contract_interface.get_current_eon_number()

    latest_root_commitment = RootCommitment.objects\
        .all()\
        .order_by('eon_number')\
        .last()

    if latest_root_commitment is None:
        return

    with Challenge.global_lock():
        challenges = Challenge.objects\
            .filter(rebuted=False, eon_number__lte=latest_root_commitment.eon_number)\
            .order_by('block')
        for challenge in challenges:
            token = challenge.wallet.token
            challenge_entry_token = token.address

            if challenge.wallet.token != challenge.recipient.token:
                try:
                    tp = TokenPair.objects.get(
                        token_from=challenge.wallet.token,
                        token_to=challenge.recipient.token)
                except TokenPair.DoesNotExist:
                    logger.warning(
                        "Skipping challenge for {}. token pair not found!".
                        format(challenge.wallet.address))
                    continue

                token = challenge.recipient.token
                challenge_entry_token = to_checksum_address(tp.conduit)

            challenge_entry = contract_interface.get_challenge_record(
                token_address=challenge_entry_token,
                recipient=challenge.recipient.address,
                sender=challenge.wallet.address)

            if not challenge_entry.challengeStage:
                logger.warning(
                    "Skipping answered challenge for {}. Where is the answer tx_id?"
                    .format(challenge.wallet.address))
                continue

            try:
                recipient_balance = ExclusiveBalanceAllotment.objects.get(
                    wallet=challenge.recipient,
                    eon_number=challenge.eon_number)
            except ExclusiveBalanceAllotment.DoesNotExist:
                logger.error("Could not find balance for {} at eon {}.".format(
                    challenge.wallet.address, challenge.eon_number))
                send_admin_email(subject='DISPUTE! NO BALANCE!',
                                 content='{}'.format(challenge.wallet.address))
                return

            token_commitment = TokenCommitment.objects.get(
                token=token, root_commitment__eon_number=challenge.eon_number)

            # state update challenge
            if challenge.wallet.token == challenge.recipient.token and challenge.wallet.address == challenge.recipient.address:

                v_0, r_0, s_0 = recipient_balance.wallet_v_r_s()
                v_1, r_1, s_1 = recipient_balance.operator_v_r_s()

                recipient_transfer_context = WalletTransferContext(
                    wallet=challenge.recipient, transfer=None)

                passive_checksum, passive_amount, passive_marker = recipient_transfer_context.get_passive_values(
                    eon_number=challenge_entry.initialStateEon)

                logger.info(
                    "Answering challenge for {} with balance {}.".format(
                        challenge.wallet.address, recipient_balance.amount()))
                logger.info("{}{}{}, {}{}{}".format(v_0, r_0, s_0, v_1, r_1,
                                                    s_1))
                send_admin_email(subject='DISPUTE! Sate Update.',
                                 content='{}'.format(challenge.wallet.address))

                # TODO signal critical failure if this does not succeed!
                transaction = contract_interface.queue_answer_state_update_challenge(
                    challenge=challenge,
                    allotment_chain=[
                        crypto.zfill(crypto.decode_hex(checksum))
                        for checksum in long_string_to_list(
                            recipient_balance.merkle_proof_hashes, 64)
                    ],
                    membership_chain=[
                        crypto.zfill(crypto.decode_hex(checksum))
                        for checksum in long_string_to_list(
                            token_commitment.membership_hashes, 64)
                    ],
                    values=csf_to_list(recipient_balance.merkle_proof_values,
                                       int),
                    l_r=[
                        int(recipient_balance.left),
                        int(recipient_balance.right)
                    ],
                    tx_set_root=crypto.zfill(
                        crypto.decode_hex(
                            recipient_balance.transaction_set_root())),
                    deltas=[d for d in recipient_balance.deltas()],
                    r=[crypto.uint256(r_0),
                       crypto.uint256(r_1)],
                    s=[crypto.uint256(s_0),
                       crypto.uint256(s_1)],
                    v=[v_0, v_1],
                    passive_checksum=passive_checksum,
                    passive_amount=passive_amount,
                    passive_marker=passive_marker)

            # transfer challenge
            elif challenge.wallet.token == challenge.recipient.token and challenge.wallet.address != challenge.recipient.address:
                try:
                    transfer = Transfer.objects.get(
                        recipient=challenge.recipient,
                        eon_number=challenge_entry.initialStateEon,
                        nonce=challenge_entry.deliveredTxNonce)
                except Transfer.DoesNotExist:
                    logger.error(
                        "Could not find transfer for {} at eon {} with nonce {}."
                        .format(challenge.recipient.address,
                                challenge.eon_number,
                                challenge_entry.deliveredTxNonce))
                    send_admin_email(
                        subject='DISPUTE! NO TRANSFER!',
                        content=
                        "Could not find transfer for {} at eon {} with nonce {}."
                        .format(challenge.recipient.address,
                                challenge.eon_number,
                                challenge_entry.deliveredTxNonce))
                    return

                recipient_transfer_context = WalletTransferContext(
                    wallet=challenge.recipient, transfer=None)

                transfers_list_nonce_index_map = {}
                transfers_list = recipient_transfer_context.authorized_transfers_list_shorthand(
                    only_appended=True,
                    force_append=False,
                    eon_number=challenge_entry.initialStateEon,
                    last_transfer_is_finalized=False,
                    index_map=transfers_list_nonce_index_map)

                transfer_tree = TransactionMerkleTree(transfers_list)
                transfer_index = transfers_list_nonce_index_map.get(
                    transfer.nonce)
                transfer_node = transfer_tree.merkle_tree_leaf_map.get(
                    transfer_index)
                transfer_proof = [
                    node.get('hash') for node in calculate_merkle_proof(
                        transfer_index, transfer_node)
                ]

                passive_checksum, passive_amount, passive_marker = recipient_transfer_context.get_passive_values(
                    eon_number=challenge_entry.initialStateEon)

                send_admin_email(subject='DISPUTE! Transfer Delivery.',
                                 content='{} {} {}'.format(
                                     challenge.wallet.address,
                                     challenge.recipient.address,
                                     transfer_index))

                # TODO signal critical failure if this does not succeed!
                transaction = contract_interface.queue_answer_delivery_challenge(
                    challenge=challenge,
                    tx_trail=transfer_index,
                    allotment_chain=[
                        crypto.zfill(crypto.decode_hex(v))
                        for v in long_string_to_list(
                            recipient_balance.merkle_proof_hashes, 64)
                    ],
                    membership_chain=[
                        crypto.zfill(crypto.decode_hex(checksum))
                        for checksum in long_string_to_list(
                            token_commitment.membership_hashes, 64)
                    ],
                    values=csf_to_list(recipient_balance.merkle_proof_values,
                                       int),
                    l_r=[
                        int(recipient_balance.left),
                        int(recipient_balance.right)
                    ],
                    deltas=[d for d in recipient_balance.deltas()],
                    tx_set_root=crypto.decode_hex(
                        recipient_balance.transaction_set_root()),
                    tx_chain=[crypto.zfill(x) for x in transfer_proof],
                    passive_checksum=passive_checksum,
                    passive_amount=passive_amount,
                    passive_marker=passive_marker)

            # swap challenge
            elif challenge.wallet.token != challenge.recipient.token and challenge.wallet.address == challenge.recipient.address:
                try:
                    transfer = Transfer.objects.get(
                        recipient=challenge.recipient,
                        eon_number=challenge_entry.initialStateEon,
                        nonce=challenge_entry.deliveredTxNonce)
                except Transfer.DoesNotExist:
                    logger.error(
                        "Could not find transfer for {} at eon {} with nonce {}."
                        .format(challenge.recipient.address,
                                challenge.eon_number,
                                challenge_entry.deliveredTxNonce))
                    send_admin_email(
                        subject='DISPUTE! NO SWAP!',
                        content=
                        "Could not find transfer for {} at eon {} with nonce {}."
                        .format(challenge.recipient.address,
                                challenge.eon_number,
                                challenge_entry.deliveredTxNonce))
                    return

                recipient_transfer_context = WalletTransferContext(
                    wallet=challenge.recipient, transfer=None)

                # if not initial transfer in a multi eon swap
                # override starting balance to cached starting balance
                if Transfer.objects.filter(eon_number=transfer.eon_number - 1,
                                           tx_id=transfer.tx_id).exists():
                    starting_balance = int(transfer.recipient_starting_balance)
                else:
                    starting_balance = int(
                        recipient_transfer_context.starting_balance_in_eon(
                            challenge_entry.initialStateEon))

                transfers_list_nonce_index_map = {}
                transfers_list = recipient_transfer_context.authorized_transfers_list_shorthand(
                    only_appended=True,
                    force_append=False,
                    eon_number=challenge_entry.initialStateEon,
                    last_transfer_is_finalized=True,
                    index_map=transfers_list_nonce_index_map,
                    starting_balance=starting_balance)

                transfer_tree = TransactionMerkleTree(transfers_list)
                transfer_index = transfers_list_nonce_index_map.get(
                    transfer.nonce)
                transfer_node = transfer_tree.merkle_tree_leaf_map.get(
                    transfer_index)
                transfer_proof = [
                    node.get('hash') for node in calculate_merkle_proof(
                        transfer_index, transfer_node)
                ]

                passive_checksum, passive_amount, passive_marker = recipient_transfer_context.get_passive_values(
                    eon_number=challenge_entry.initialStateEon)

                send_admin_email(subject='DISPUTE! Swap Delivery.',
                                 content='{} {} {}'.format(
                                     challenge.wallet.address,
                                     challenge.recipient.address,
                                     transfer_index))

                is_cancelled = transfer.cancelled and transfer.recipient_cancellation_active_state is not None
                if transfer.complete or is_cancelled:
                    starting_balance = 2**256 - 1

                # TODO signal critical failure if this does not succeed!
                transaction = contract_interface.queue_answer_swap_challenge(
                    challenge=challenge,
                    token_pair=[
                        challenge.wallet.token.address,
                        challenge.recipient.token.address
                    ],
                    balance_at_start_of_eon=starting_balance,
                    tx_trail=int(transfer_index),
                    allotment_chain=[
                        crypto.zfill(crypto.decode_hex(v))
                        for v in long_string_to_list(
                            recipient_balance.merkle_proof_hashes, 64)
                    ],
                    membership_chain=[
                        crypto.zfill(crypto.decode_hex(checksum))
                        for checksum in long_string_to_list(
                            token_commitment.membership_hashes, 64)
                    ],
                    values=csf_to_list(recipient_balance.merkle_proof_values,
                                       int),
                    l_r=[
                        int(recipient_balance.left),
                        int(recipient_balance.right)
                    ],
                    deltas=[d for d in recipient_balance.deltas()],
                    tx_set_root=crypto.zfill(
                        crypto.decode_hex(
                            recipient_balance.transaction_set_root())),
                    tx_chain=[crypto.zfill(x) for x in transfer_proof],
                    passive_checksum=passive_checksum,
                    passive_amount=passive_amount,
                    passive_marker=passive_marker)

            challenge.rebuted = True
            challenge.save()
            logger.warning(transaction)
Beispiel #4
0
def init_swap_challenge(test_case: RPCTestCase, swap: Transfer, eon_number):
    sender_transfer_context = WalletTransferContext(wallet=swap.wallet,
                                                    transfer=None)

    if Transfer.objects.filter(eon_number=swap.eon_number - 1,
                               tx_id=swap.tx_id).exists():
        starting_balance = int(swap.sender_starting_balance)
    else:
        starting_balance = int(
            sender_transfer_context.starting_balance_in_eon(eon_number))

    transfers_list_nonce_index_map = {}
    transfers_list = sender_transfer_context.authorized_transfers_list_shorthand(
        only_appended=True,
        force_append=False,
        eon_number=eon_number,
        last_transfer_is_finalized=True,
        index_map=transfers_list_nonce_index_map,
        starting_balance=starting_balance)

    sender_active_state = sender_transfer_context.last_appended_active_state(
        eon_number=eon_number)

    transfer_tree = TransactionMerkleTree(transfers_list)
    transfer_index = transfers_list_nonce_index_map.get(int(swap.nonce))
    transfer_node = transfer_tree.merkle_tree_leaf_map.get(transfer_index)
    transfer_proof = [
        node.get('hash')
        for node in calculate_merkle_proof(transfer_index, transfer_node)
    ]

    test_case.assertEqual(sender_active_state.tx_set_hash,
                          crypto.hex_value(transfer_tree.root_hash()))

    tx_set_root = crypto.zfill(
        crypto.decode_hex(sender_active_state.tx_set_hash))
    deltas = [
        int(sender_active_state.updated_spendings),
        int(sender_active_state.updated_gains)
    ]

    test_case.assertTrue(
        test_case.contract_interface.check_merkle_membership_proof(
            trail=int(transfer_index),
            chain=[crypto.zfill(x) for x in transfer_proof],
            node=transfer_node.get('hash'),
            merkle_root=tx_set_root))

    token_commitment = TokenCommitment.objects.get(
        token=swap.wallet.token, root_commitment__eon_number=eon_number + 1)

    v, r, s = sender_active_state.operator_signature.vrs()

    # swap_sender_balance = sender_transfer_context.balance_as_of_eon(
    #     eon_number)
    sender_balance = sender_transfer_context.balance_as_of_eon(eon_number + 1)

    passive_checksum, passive_amount, passive_marker = sender_transfer_context.get_passive_values(
        eon_number=eon_number + 1)

    swap_order = [
        int(swap.amount),  # sell
        int(swap.amount_swapped),  # buy
        # int(swap_sender_balance.right - swap_sender_balance.left),
        starting_balance,  # balance
        int(swap.nonce)
    ]  # nonce

    chain_transition_checksum = test_case.contract_interface.check_proof_of_transition_agreement(
        token_address=swap.wallet.token.address,
        holder=swap.wallet.address,
        trail_identifier=swap.wallet.trail_identifier,
        eon_number=eon_number,
        tx_set_root=tx_set_root,
        deltas=deltas,
        attester=settings.HUB_OWNER_ACCOUNT_ADDRESS,
        r=crypto.uint256(r),
        s=crypto.uint256(s),
        v=v)
    test_case.assertEqual(crypto.hex_value(sender_active_state.checksum()),
                          crypto.hex_value(chain_transition_checksum))

    node_hash = merkle_tree.leaf_hash(
        merkle_tree.wallet_leaf_inner_hash, {
            'contract': settings.HUB_LQD_CONTRACT_ADDRESS,
            'token': swap.wallet.token.address,
            'wallet': swap.wallet.address,
            'left': sender_balance.left,
            'right': sender_balance.right,
            'active_state_checksum': sender_active_state.checksum(),
            'passive_checksum': passive_checksum,
            'passive_amount': passive_amount,
            'passive_marker': passive_marker,
        })
    checkpoint = RootCommitment.objects.get(eon_number=eon_number + 1)
    test_case.contract_interface.check_exclusive_allotment_proof(
        allotment_trail=int(sender_balance.merkle_proof_trail),
        membership_trail=swap.wallet.token.trail,
        node=node_hash,
        merkle_root=crypto.decode_hex(checkpoint.merkle_root),
        allotment_chain=[
            crypto.zfill(crypto.decode_hex(v)) for v in long_string_to_list(
                sender_balance.merkle_proof_hashes, 64)
        ],
        membership_chain=[
            crypto.zfill(crypto.decode_hex(checksum))
            for checksum in long_string_to_list(
                token_commitment.membership_hashes, 64)
        ],
        value=csf_to_list(sender_balance.merkle_proof_values, int),
        left=int(sender_balance.left),
        right=int(sender_balance.right))

    test_case.contract_interface.issue_swap_challenge(
        token_pair=[swap.wallet.token.address, swap.recipient.token.address],
        wallet=swap.wallet.address,
        swap_order=swap_order,
        sender_tx_recipient_trails=[
            swap.wallet.trail_identifier,
            int(transfer_index), swap.recipient.trail_identifier
        ],
        allotment_chain=[
            crypto.zfill(crypto.decode_hex(v)) for v in long_string_to_list(
                sender_balance.merkle_proof_hashes, 64)
        ],
        membership_chain=[
            crypto.zfill(crypto.decode_hex(checksum))
            for checksum in long_string_to_list(
                token_commitment.membership_hashes, 64)
        ],
        tx_chain=[crypto.zfill(x) for x in transfer_proof],
        values=csf_to_list(sender_balance.merkle_proof_values, int),
        l_r=[int(sender_balance.left),
             int(sender_balance.right)],
        tx_set_root=tx_set_root,
        deltas=deltas,
        passive_checksum=passive_checksum,
        passive_amount=passive_amount,
        passive_marker=passive_marker)