Beispiel #1
0
def process_passive_transfers_for_eon(operator_eon_number):
    checkpoint_created = RootCommitment.objects.filter(
        eon_number=operator_eon_number).exists()
    with transaction.atomic():
        transfers = Transfer.objects\
            .filter(processed=False, swap=False, passive=True)\
            .select_for_update()\
            .order_by('eon_number', 'id')

        for transfer in transfers:
            try:
                with transaction.atomic():
                    process_passive_transfer(transfer, operator_eon_number,
                                             checkpoint_created)
            except IntegrityError as e:
                send_admin_email(subject='Transfer Integrity Error',
                                 content='{}'.format(e))
                logger.error(e)
Beispiel #2
0
def create_checkpoint_for_eon(eon_number, latest_block_number):
    if RootCommitment.objects.filter(eon_number=eon_number).count() > 0:
        return False

    if eon_number > 1:
        last_eon_number = eon_number - 1
        last_eon = LocalViewInterface.confirmed(eon_number=last_eon_number)
        if not last_eon:
            logger.error(
                'Missing confirmed contract state for eon {}.'.format(last_eon_number))
            send_admin_email(
                subject='Soft Checkpoint Error: Missing Contract State',
                content='Missing confirmed contract state for previous eon {}. We may not be in sync with the blockchain!'.format(last_eon_number))
            return False

        last_confirmed_eon_number, last_confirmed_sub_block = last_eon.eon_number_and_sub_block()
        if last_confirmed_eon_number != last_eon_number:
            logger.error(
                'Attempted to use confirmed state for eon {}. Expected {}.'.format(last_confirmed_eon_number,
                                                                                   last_eon_number))
            send_admin_email(
                subject='Soft Checkpoint Error: Wrong last eon #',
                content='Need to sync chain! Attempted to use confirmed state for eon {}. Expected {}!'
                .format(last_confirmed_eon_number,
                        last_eon_number))
            return False
        last_sub_block_number = LocalViewInterface.get_contract_parameters().blocks_per_eon - 1
        if last_confirmed_sub_block != last_sub_block_number:
            logger.error(
                'Attempted to use confirmed state for sub block {}. Expected {}.'.format(last_confirmed_sub_block,
                                                                                         last_sub_block_number))
            send_admin_email(
                subject='Soft Checkpoint Error: Wrong last Sub block #',
                content='Need to sync chain! Attempted to use confirmed state for sub block {}. Expected {}.'
                .format(last_confirmed_sub_block,
                        last_sub_block_number))
            return False

    # commitment write read lock makes sure transaction confirmation will not mutate ledger while checkpoint is being created
    with transaction.atomic(), RootCommitment.read_write_lock(suffix=eon_number-1, is_write=True, auto_renewal=True):
        # TODO parallelism
        token_commitments = [create_token_commitment_for_eon(token, eon_number) for token in
                             Token.objects.all().order_by('trail')]

        root_commitment = create_root_commitment_for_eon(
            token_commitments, eon_number, latest_block_number)

        NOCUSTContractInterface().queue_submit_checkpoint(root_commitment)

    return True
Beispiel #3
0
def broadcast_checkpoint():
    """
    References to this function is absent in program code
    """
    with ContractState.global_lock():
        latest_block = LocalViewInterface.latest()
        submitted = latest_block.is_checkpoint_submitted_for_current_eon
        current_eon, current_sub_block = latest_block.eon_number_and_sub_block()
        blocks_for_submission = LocalViewInterface.blocks_for_submission()

        if submitted:
            logger.warning("TokenCommitment already submitted")
            return
        elif current_sub_block < blocks_for_submission:
            logger.warning('Too early to submit checkpoint: {} blocks left'.format(
                blocks_for_submission - current_sub_block))
            return
        elif current_sub_block > 150 and settings.DEBUG:
            logger.error("just let the damn tests pass..")  # TODO: todo
            return
        elif latest_block.has_missed_checkpoint_submission:
            logger.error(
                'The operator has missed a checkpoint submission. Cannot submit checkpoint.')
            send_admin_email(
                subject='The commit chain is halted.',
                content='Ouch.')
            return

        checkpoint = TokenCommitment.objects.get(eon_number=current_eon)

        if EthereumTransaction.objects.filter(tag=checkpoint.tag()).exists():
            logger.warning("TokenCommitment already enqueued.")
            send_admin_email(
                subject='Soft Submission Error: TokenCommitment already enqueued.',
                content='This should eventually be resolved.')
            return

        managed_funds = NOCUSTContractInterface().get_managed_funds(checkpoint.eon_number)
        if checkpoint.upper_bound > managed_funds:
            logger.error(
                "TokenCommitment upper bound greater than managed funds.")
            send_admin_email(
                subject='HARD Submission Error: TokenCommitment upper bound greater than managed funds.',
                content='Created checkpoint for {} while managed funds are {}. Some withdrawals are possibly pending cancellation.'.format(checkpoint.upper_bound, managed_funds))
            return

        NOCUSTContractInterface().queue_submit_checkpoint(checkpoint)
Beispiel #4
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 #5
0
def concurrently_retrieve_state(contract_interface, contract_event_decoder,
                                verbose):
    logger.info('Retrieve blocks.')
    latest_chain_block = contract_interface.current_block() - 1
    if not settings.DEBUG:
        latest_chain_block += 1

    logger.info('Latest chain block: {}'.format(latest_chain_block))

    running_from = LocalViewInterface.latest_block() + 1
    running_until = latest_chain_block

    if running_from > running_until:
        logger.info('No new blocks {}-{}.'.format(running_from, running_until))
        return 0

    confirm_from = LocalViewInterface.confirmed_block() + 1
    confirm_until = running_until - contract_interface.get_blocks_for_confirmation(
    )

    if confirm_from > confirm_until:
        logger.info('No new blocks to confirm.')

    update_from = min(running_from, confirm_from)
    update_until = min(update_from + 11, running_until + 1)
    skipped = running_until + 1 - update_until

    contract_state_tasks = []
    contract_state_tasks_block_numbers = []

    logger.info('Fetching [{},{})'.format(update_from, update_until))
    for block_number in range(update_from, update_until):
        if confirm_from <= block_number and block_number <= confirm_until:
            contract_state_tasks.append(
                fetch_confirmed_block.delay(block_number=block_number))
            contract_state_tasks_block_numbers.append(block_number)
        elif running_from <= block_number and block_number <= running_until:
            contract_state_tasks.append(
                fetch_running_block.delay(block_number=block_number))
            contract_state_tasks_block_numbers.append(block_number)

    for task_index, contract_state_task in enumerate(contract_state_tasks):
        block_number = contract_state_tasks_block_numbers[task_index]

        try:
            task_result = contract_state_task.get(
                timeout=settings.HUB_BLOCK_FETCH_TIMEOUT,
                disable_sync_subtasks=False)
        except exceptions.TimeoutError:
            logger.error('Timed-out fetching block {}'.format(block_number))
            for cleanup_index, task_to_clean_up in enumerate(
                    contract_state_tasks):
                if cleanup_index >= task_index:
                    try:
                        task_to_clean_up.forget()
                    except NotImplementedError as e:
                        logger.error('Could not forget task results.')
                        logger.error(e)
            break

        if confirm_from <= block_number and block_number <= confirm_until:
            confirmed_contract_state_dictionary, confirmed_contract_ledger_state_dictionaries, block_logs = task_result

            confirmed_contract_state = ContractState.from_dictionary_form(
                confirmed_contract_state_dictionary)
            confirmed_ledger_states = [
                ContractLedgerState.from_dictionary_form(
                    ledger_state, confirmed_contract_state) for ledger_state in
                confirmed_contract_ledger_state_dictionaries
            ]
            with transaction.atomic():
                if running_from <= block_number and block_number <= running_until:
                    confirmed_contract_state.save()
                    for ledger_state in confirmed_ledger_states:
                        ledger_state.contract_state = confirmed_contract_state
                        ledger_state.save()

                logger.info('Decoding logs for block {}.'.format(
                    confirmed_contract_state.block))
                decoded_logs = contract_event_decoder.decode_many(block_logs)
                eon_number = confirmed_contract_state.eon_number()
                logger.info(
                    "Processing decoded logs in block %d eon %s: %d logs" %
                    (confirmed_contract_state.block, eon_number,
                     len(decoded_logs)))
                for log in decoded_logs:

                    if log.get(u'name') in event_interpreter_map:
                        interpreter = event_interpreter_map.get(
                            log.get(u'name'))
                        interpreter.interpret(
                            decoded_event=log.get('data'),
                            txid=log.get('txid'),
                            block_number=confirmed_contract_state.block,
                            eon_number=eon_number,
                            verbose=verbose) if interpreter else None
                    else:
                        logger.error('UNKNOWN EVENT LOG {} '.format(log))
                        send_admin_email(
                            subject='Chain Sync Error: Unknown Log',
                            content='{}'.format(log))

                running_contract_state = LocalViewInterface.running(
                    block_number=confirmed_contract_state.block)

                if running_contract_state.confirm(confirmed_contract_state,
                                                  confirmed_ledger_states):
                    logger.info('Block {} confirmed.'.format(
                        confirmed_contract_state.block))
                else:
                    logger.error('Block {} failed to confirm.'.format(
                        confirmed_contract_state.block))
                    send_admin_email(
                        subject='Chain Sync Confirmation Failure {}'.format(
                            confirmed_contract_state.block),
                        content='{}'.format(confirmed_contract_state))
                    raise Exception()
        elif running_from <= block_number and block_number <= running_until:
            logger.info('Process running block {}'.format(block_number))
            confirmed_contract_state_dictionary, confirmed_contract_ledger_state_dictionaries = task_result

            contract_state = ContractState.from_dictionary_form(
                confirmed_contract_state_dictionary)
            contract_state.save()
            ledger_states = [
                ContractLedgerState.from_dictionary_form(
                    ledger_state, contract_state) for ledger_state in
                confirmed_contract_ledger_state_dictionaries
            ]
            for ledger_state in ledger_states:
                ledger_state.save()
            logger.info('Running block {} stored.'.format(
                contract_state.block))
        else:
            logger.info('Running from {} to {}.'.format(
                running_from, running_until))
            logger.info('Confirm from {} to {}.'.format(
                confirm_from, confirm_until))
            logger.info('Update from {} to {}.'.format(update_from,
                                                       update_until))
            logger.error('Unexpected block number {}'.format(block_number))
            send_admin_email(
                subject='Chain Sync Unexpected Block {}'.format(block_number),
                content='Out of order processing.')
            raise Exception()
    return skipped
Beispiel #6
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)
def send_queued_transactions():
    contract_interface = NOCUSTContractInterface()

    latest_block = LocalViewInterface.latest_block()

    with EthereumTransaction.global_lock(auto_renewal=True):
        pending_transactions = EthereumTransaction.objects.all().order_by('nonce')

        for transaction in pending_transactions:
            if transaction.ethereumtransactionattempt_set.filter(confirmed=True).exists():
                continue

            if transaction.ethereumtransactionattempt_set.exists():
                last_attempt = transaction.ethereumtransactionattempt_set.order_by(
                    'gas_price').last()
            else:
                initial_gas_price = contract_interface.web3.toWei(
                    '100', 'gwei')
                signed_tx = contract_interface.sign_for_delivery_as_owner(
                    transaction, initial_gas_price)
                try:
                    logger.info('Publishing Signed TX: {}'.format(transaction))
                    hash = contract_interface.send_raw_transaction(
                        signed_tx.rawTransaction)
                except ValueError as e:
                    send_admin_email(
                        subject='INITIAL TRANSACTION ATTEMPT ERROR',
                        content='{}: {}'.format(transaction.tag, e))
                    continue
                last_attempt = EthereumTransactionAttempt.objects.create(
                    transaction=transaction,
                    block=latest_block,
                    gas_price=initial_gas_price,
                    signed_attempt=signed_tx.rawTransaction.hex(),
                    hash=remove_0x_prefix(hash.hex()),
                    mined=False,
                    confirmed=False)

            if transaction.ethereumtransactionattempt_set.filter(mined=True).exists():
                try:
                    mined_transaction = transaction.ethereumtransactionattempt_set.get(
                        mined=True)
                except EthereumTransactionAttempt.DoesNotExist:
                    logger.error('Mined Transaction Attempt Inconsistency')
                    continue

                receipt = contract_interface.get_transaction_receipt_hex(
                    add_0x_prefix(mined_transaction.hash))

                if receipt is not None:
                    if receipt.get('blockNumber') - latest_block > settings.HUB_LQD_CONTRACT_CONFIRMATIONS:
                        logger.info('Transaction confirmed! {}'.format(
                            last_attempt.hash))
                        mined_transaction.confirmed = True
                        mined_transaction.save()
                    continue

                logger.warning(
                    'Transaction UNMINED: {}'.format(last_attempt.hash))
                mined_transaction.mined = False
                mined_transaction.save()

            receipt = contract_interface.get_transaction_receipt_hex(
                add_0x_prefix(last_attempt.hash))
            if receipt is not None:
                last_attempt.mined = True
                last_attempt.save()
                continue

            if latest_block - last_attempt.block > 10:
                new_gas_price = 2 * int(last_attempt.gas_price)
                signed_tx = contract_interface.sign_for_delivery_as_owner(
                    transaction, new_gas_price)
                try:
                    hash = contract_interface.send_raw_transaction(
                        signed_tx.rawTransaction)
                except ValueError as e:
                    send_admin_email(
                        subject='TRANSACTION RE-ATTEMPT ERROR',
                        content='{}: {}'.format(transaction.tag, e))
                    continue

                EthereumTransactionAttempt.objects.create(
                    transaction=transaction,
                    block=latest_block,
                    gas_price=new_gas_price,
                    signed_attempt=signed_tx.rawTransaction.hex(),
                    hash=remove_0x_prefix(hash.hex()),
                    mined=False,
                    confirmed=False)

                send_admin_email(
                    subject='Transaction Reattempt',
                    content='{}: {}'.format(transaction.tag, new_gas_price))
Beispiel #8
0
def send_address_not_found_log(who, address):
    logger.error("CHALLENGE ISSUED AGAINST UNKNOWN {} {}".format(who, address))
    send_admin_email(
        subject='DISPUTE! UNKNOWN {}!'.format(who),
        content='{}'.format(address))