Exemplo n.º 1
0
def register_owner_account(token: Token):
    if Wallet.objects.filter(token=token, address__iexact=remove_0x_prefix(settings.HUB_OWNER_ACCOUNT_ADDRESS)).exists():
        logger.error('Owner account already registered.')
        return

    if not LocalViewInterface.get_contract_parameters():
        logger.error('Contract parameters not yet populated.')
        return

    logger.warning('Registering owner account: {}'.format(
        settings.HUB_OWNER_ACCOUNT_ADDRESS))

    latest_eon_number = LocalViewInterface.latest().eon_number()

    authorization_digest = Wallet(
        token=token, address=settings.HUB_OWNER_ACCOUNT_ADDRESS).get_admission_hash(latest_eon_number)
    authorization = sign_message(
        authorization_digest, settings.HUB_OWNER_ACCOUNT_KEY)
    latest_tos_config = TOSConfig.objects.all().order_by('time').last()
    tos_signature = sign_message(
        hex_value(latest_tos_config.digest()), settings.HUB_OWNER_ACCOUNT_KEY)
    registration = AdmissionRequestSerializer(data={
        'token': token.address,
        'address': remove_0x_prefix(settings.HUB_OWNER_ACCOUNT_ADDRESS),
        'authorization': {
            'value': encode_signature(authorization)
        },
        'tos_signature': {
            'value': encode_signature(tos_signature)
        }
    })
    registration.is_valid(raise_exception=True)
    registration.save()
    process_admissions()
Exemplo n.º 2
0
def register_owner_account(token: Token):
    if Wallet.objects.filter(token=token,
                             address=remove_0x_prefix(
                                 settings.HUB_OWNER_ACCOUNT_ADDRESS)).exists():
        return

    if not LocalViewInterface.get_contract_parameters():
        return

    latest_eon_number = LocalViewInterface.latest().eon_number()

    authorization_digest = Wallet(
        token=token,
        address=settings.HUB_OWNER_ACCOUNT_ADDRESS).get_admission_hash(
            latest_eon_number)
    authorization = sign_message(authorization_digest,
                                 settings.HUB_OWNER_ACCOUNT_KEY)
    latest_tos_config = TOSConfig.objects.all().order_by('time').last()
    tos_signature = sign_message(decode_hex(latest_tos_config.digest()),
                                 settings.HUB_OWNER_ACCOUNT_KEY)

    registration = AdmissionRequestSerializer(
        data={
            'token': token.address,
            'address': remove_0x_prefix(settings.HUB_OWNER_ACCOUNT_ADDRESS),
            'authorization': {
                'value': encode_signature(authorization)
            },
            'tos_signature': {
                'value': encode_signature(tos_signature)
            }
        })
    registration.is_valid(raise_exception=True)
    registration.save()
Exemplo n.º 3
0
def get_wallet_data(channel_name,
                    operation,
                    wallet_address,
                    token_address,
                    eon_number=None):

    # default eon_number is the latest eon
    if eon_number is None:
        eon_number = LocalViewInterface.latest().eon_number()

    # wrap negative eon_number
    if eon_number < 0:
        eon_number += LocalViewInterface.latest().eon_number()

    try:
        wallet = Wallet.objects.get(address__iexact=wallet_address,
                                    token__address__iexact=token_address)

        request_model = MockModel(eon_number=eon_number,
                                  wallet=wallet,
                                  transfer_id=0)

        data = WalletStateSerializer(request_model).data

        # send response to websocket channel
        async_to_sync(send_response)(
            channel_name=channel_name,
            resource="wallet",
            data=data,
        )
    except Wallet.DoesNotExist:
        # send error to websocket channel
        async_to_sync(send_error)(channel_name=channel_name,
                                  error='Wallet does not exist.',
                                  cause=operation)
Exemplo n.º 4
0
def cancel_finalize_swaps():

    if not LocalViewInterface.get_contract_parameters():
        logger.error('Contract parameters not yet populated.')
        return

    latest_eon_number = LocalViewInterface.latest().eon_number()

    # This lock is required because the ledger will be mutated as the swaps are processed
    with RootCommitment.global_lock():
        cancel_finalize_swaps_for_eon(latest_eon_number)
Exemplo n.º 5
0
def process_passive_transfers():
    logger.info('Processing passive transfers')

    if not LocalViewInterface.get_contract_parameters():
        logger.error('Contract parameters not yet populated.')
        return

    latest_eon_number = LocalViewInterface.latest().eon_number()

    # This lock is required because the ledger will be mutated as the transfers are processed
    with RootCommitment.global_lock():
        logger.info('Start')
        process_passive_transfers_for_eon(latest_eon_number)
Exemplo n.º 6
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
Exemplo n.º 7
0
def register_token(token_address, name, short_name, register_on_chain):
    try:
        Token.objects.get(address=remove_0x_prefix(token_address))
        raise ValueError(
            'Token {} already registered in local db.'.format(token_address))
    except Token.DoesNotExist:
        pass

    if register_on_chain:
        NOCUSTContractInterface().register_ERC20(token_address)
        print('Registration transaction queued.')

    token = Token.objects.create(address=remove_0x_prefix(token_address),
                                 name=name,
                                 short_name=short_name,
                                 trail=Token.objects.count(),
                                 block=LocalViewInterface.latest_block())
    print('Token locally registered.')

    register_owner_account(token)

    if same_hex_value(token_address, settings.SLA_TOKEN_ADDRESS):
        register_sla_recipient_account()

    return token
Exemplo n.º 8
0
def create_root_commitment_for_eon(token_commitments: [TokenCommitment], eon_number, latest_block_number):
    token_commitment_leaves = [commitment.shorthand()
                               for commitment in token_commitments]
    token_merkle_tree = TokenMerkleTree(token_commitment_leaves)
    token_merkle_tree_root = hex_value(token_merkle_tree.root_hash())

    previous_eon_basis = ZERO_CHECKSUM
    if eon_number > 1:
        local_block = LocalViewInterface.confirmed(eon_number - 1)
        print(local_block.__dict__)
        print(local_block.eon_number())
        previous_eon_basis = local_block.basis

    root_commitment = RootCommitment.objects.create(
        eon_number=eon_number,
        basis=previous_eon_basis,
        merkle_root=token_merkle_tree_root,
        block=latest_block_number)

    for token_commitment in token_commitments:
        token_commitment.root_commitment = root_commitment
        token_commitment.membership_hashes = token_merkle_tree.proof(
            token_commitment.token.trail)
        token_commitment.save()

    return root_commitment
Exemplo n.º 9
0
 def get_last_checkpoint(self, block_identifier='latest'):
     eon_number = self.get_last_checkpoint_submission_eon()
     eons_kept = LocalViewInterface.get_contract_parameters().eons_kept
     return self.contract\
         .functions\
         .getCheckpointAtSlot(eon_number % eons_kept)\
         .call(block_identifier=block_identifier)
Exemplo n.º 10
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)
Exemplo n.º 11
0
def process_admissions():

    if not LocalViewInterface.get_contract_parameters():
        logger.error('Contract parameters not yet populated.')
        return

    # This lock is needed because new wallets can be introduced, which would affect the checkpoint.
    with RootCommitment.global_lock():
        process_admissions_for_latest_eon()
Exemplo n.º 12
0
    def get_deposits(self,
                     token_address,
                     eon_number,
                     block_identifier='latest'):
        eons_kept = LocalViewInterface.get_contract_parameters().eons_kept
        aggregate_eon, aggregate_value = self.contract\
            .functions\
            .getDepositsAtSlot(add_0x_prefix(token_address), eon_number % eons_kept)\
            .call(block_identifier=block_identifier)

        return aggregate_value if aggregate_eon == eon_number else 0
Exemplo n.º 13
0
def get_operator_data(channel_name, operation):
    latest = LocalViewInterface.latest()
    confirmed = LocalViewInterface.confirmed()

    data = {
        'latest': {
            'block': latest.block,
            'eon_number': latest.eon_number(),
        },
        'confirmed': {
            'block': confirmed.block,
            'eon_number': confirmed.eon_number(),
        }
    }

    # send response to websocket channel
    async_to_sync(send_response)(
        channel_name=channel_name,
        resource="operator",
        data=data,
    )
Exemplo n.º 14
0
def create_checkpoint():

    if not LocalViewInterface.get_contract_parameters():
        logger.error('Contract parameters not yet populated.')
        return

    latest = LocalViewInterface.latest()
    latest_eon_number, latest_sub_block = latest.eon_number_and_sub_block()
    blocks_for_creation = LocalViewInterface.blocks_for_creation()

    confirmed_eon_number, confirmed_sub_block = LocalViewInterface.confirmed(
    ).eon_number_and_sub_block()
    if confirmed_eon_number < latest_eon_number:
        return
    if latest_sub_block < blocks_for_creation:
        return

    with RootCommitment.global_lock():
        new_checkpoint = create_checkpoint_for_eon(latest_eon_number, latest.block)
        if new_checkpoint:
            operator_celery.send_task('auditor.tasks.broadcast_wallet_data')
Exemplo n.º 15
0
def register_sla_recipient_account():
    if same_hex_value(settings.SLA_RECIPIENT_ADDRESS,
                      settings.HUB_OWNER_ACCOUNT_ADDRESS):
        logger.warning('Skipping registration: Hub Owner is SLA recipient.')
        return

    token = Token.objects.filter(
        address__iexact=remove_0x_prefix(settings.SLA_TOKEN_ADDRESS))

    if not token.exists():
        logger.error('SLA Payment Token not yet registered.')
        return

    if Wallet.objects.filter(token=token,
                             address__iexact=remove_0x_prefix(
                                 settings.SLA_RECIPIENT_ADDRESS)).exists():
        logger.error('Recipient account already registered.')
        return

    if not LocalViewInterface.get_contract_parameters():
        logger.error('Contract parameters not yet populated.')
        return

    latest_eon = LocalViewInterface.latest().eon_number()

    authorization_digest = Wallet(
        token=token,
        address=settings.SLA_RECIPIENT_ADDRESS).get_admission_hash(latest_eon)
    authorization = sign_message(authorization_digest,
                                 settings.SLA_RECIPIENT_KEY)
    registration = AdmissionRequestSerializer(
        data={
            'token': token.address,
            'address': remove_0x_prefix(settings.SLA_RECIPIENT_ADDRESS),
            'authorization': encode_signature(authorization)
        })
    registration.is_valid(raise_exception=True)
    registration.save()
    process_admissions()
Exemplo n.º 16
0
def cache_wallet_data(eon_number, token_address, wallet_address, data):
    if eon_number < LocalViewInterface.latest().eon_number():
        path = f"/audit_data_cache/{eon_number}/{token_address}"

        if not os.path.exists(path):
            os.makedirs(path)

        with open(f"{path}/{wallet_address}.json", "w+") as f:
            f.write(json.dumps(data))

        logger.info(f"Cached {eon_number}/{token_address}/{wallet_address} .")
    else:
        logger.info(
            f"Skipping cache for {eon_number}/{token_address}/{wallet_address}, eon {eon_number} is not over yet."
        )
Exemplo n.º 17
0
def register_eth_token():
    if Token.objects.filter(address__iexact=remove_0x_prefix(
            settings.HUB_LQD_CONTRACT_ADDRESS)).exists():
        logger.error('ETH token already registered.')
        return

    if not LocalViewInterface.get_contract_parameters():
        logger.error('Contract parameters not yet populated.')
        return

    logger.warning('Registering ETH Token')

    eth_token = register_token(token_address=settings.HUB_LQD_CONTRACT_ADDRESS,
                               name='Ethereum',
                               short_name='ETH',
                               register_on_chain=False)

    ContractLedgerState.objects.create(
        contract_state=LocalViewInterface.genesis(),
        token=eth_token,
        pending_withdrawals=0,
        confirmed_withdrawals=0,
        deposits=0,
        total_balance=0)
Exemplo n.º 18
0
def broadcast_wallet_data():
    eon_number = LocalViewInterface.latest().eon_number()
    for wallet in Wallet.objects.all():

        request_model = MockModel(eon_number=eon_number,
                                  wallet=wallet,
                                  transfer_id=0)

        data = WalletStateSerializer(request_model).data

        send_notification(stream_prefix='wallet',
                          stream_id="{}/{}".format(wallet.token.address,
                                                   wallet.address),
                          event_name=CHECKPOINT_CREATED,
                          data=data)
Exemplo n.º 19
0
def synchronize_contract_state(verbose=False):
    logger.info('Retrieving confirmed events')

    if not LocalViewInterface.get_contract_parameters():
        logger.error('Contract parameters not yet populated.')
        return

    with ContractState.global_lock():
        logger.info('Start..')
        contract_interface = NOCUSTContractInterface()
        logger.info('Interface acquired')
        contract_event_decoder = NOCUSTContractEventDecoder()
        logger.info('Decoder acquired')
        contract_interface.get_blocks_per_eon()

        return concurrently_retrieve_state(contract_interface,
                                           contract_event_decoder, verbose)
Exemplo n.º 20
0
    def validate(self, attrs):
        authorization = attrs.pop('authorization').get('value')
        address = attrs.get('address')
        token = attrs.get('token')
        tos_signature = attrs.pop('tos_signature').get('value')

        if BlacklistEntry.objects.filter(
                address__iexact=remove_0x_prefix(address)).exists():
            raise serializers.ValidationError(
                detail='', code=ErrorCode.WALLET_BLACKLISTED)

        if Wallet.objects.filter(address__iexact=remove_0x_prefix(address),
                                 token=token).exists():
            raise serializers.ValidationError(
                detail='', code=ErrorCode.WALLET_ALREADY_ADMITTED)

        dummy_wallet = Wallet(token=token, address=remove_0x_prefix(address))

        attrs['registration_eon_number'] = LocalViewInterface.latest(
        ).eon_number()
        admission_hash = hex_value(
            dummy_wallet.get_admission_hash(attrs['registration_eon_number']))

        attrs['signature'] = Signature(wallet=dummy_wallet,
                                       checksum=admission_hash,
                                       value=authorization)

        if not attrs['signature'].is_valid():
            raise serializers.ValidationError(
                detail='', code=ErrorCode.INVALID_ADMISSION_SIGNATURE)

        latest_tos_config = TOSConfig.objects.all().order_by('time').last()

        attrs['tos_signature'] = Signature(wallet=dummy_wallet,
                                           checksum=latest_tos_config.digest(),
                                           value=tos_signature)

        if not attrs['tos_signature'].is_valid():
            raise serializers.ValidationError(
                detail='Invalid TOS (digest: {}) signature'.format(
                    latest_tos_config.digest()),
                code=ErrorCode.INVALID_TOS_SIGNATURE)

        attrs['tos_config'] = latest_tos_config

        return attrs
Exemplo n.º 21
0
    def update(self, swap, validated_data):
        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 current_swap.complete:
                raise serializers.ValidationError(
                    detail='', code=ErrorCode.SWAP_ALREADY_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)

            freezing_signature_data = validated_data.pop('freezing_signature')
            freezing_checksum = crypto.hex_value(
                current_swap.swap_cancellation_message_checksum())
            freezing_signature = Signature(
                wallet=current_swap.wallet,
                checksum=freezing_checksum,
                value=freezing_signature_data.get('value'))

            if not freezing_signature.is_valid():
                raise serializers.ValidationError(
                    detail='', code=ErrorCode.INVALID_FREEZING_SIGNATURE)
            freezing_signature.save()

            # only current swap should be locked, future swaps are not matched
            with current_swap.lock(
                    auto_renewal=False), current_swap.wallet.lock(
                        auto_renewal=False), current_swap.recipient.lock(
                            auto_renewal=False):
                swap_set.update(cancelled=True,
                                swap_freezing_signature=freezing_signature)
        return current_swap
Exemplo n.º 22
0
    def to_representation(self, swap_data_request: SwapDataRequest):
        latest = LocalViewInterface.latest().eon_number()
        eon_number = swap_data_request.eon_number if 0 <= swap_data_request.eon_number <= latest else latest
        all_sell_orders = Transfer.objects.filter(
            wallet__token=swap_data_request.left_token,
            recipient__token=swap_data_request.right_token,
            processed=False,
            complete=False,
            voided=False,
            cancelled=False,
            swap=True,
            eon_number=eon_number)

        all_sell_orders = sorted(all_sell_orders,
                                 key=cmp_to_key(
                                     price_comparison_function(inverse=True)))

        all_buy_orders = Transfer.objects.filter(
            wallet__token=swap_data_request.right_token,
            recipient__token=swap_data_request.left_token,
            processed=False,
            complete=False,
            voided=False,
            cancelled=False,
            swap=True,
            eon_number=eon_number)

        all_buy_orders = sorted(all_buy_orders,
                                key=cmp_to_key(
                                    price_comparison_function(inverse=False)))

        return {
            'sell_orders':
            OrderSerializer(combine_order_volumes(all_sell_orders),
                            many=True,
                            read_only=True).data,
            'buy_orders':
            OrderSerializer(combine_order_volumes(all_buy_orders),
                            many=True,
                            read_only=True).data,
        }
Exemplo n.º 23
0
    def fetch_contract_state_at_block(self, block_number):
        try:
            local_params = LocalViewInterface.get_contract_parameters()
            current_eon = 1 + \
                (block_number - local_params.genesis_block) // local_params.blocks_per_eon

            contract_state_variables = self.contract\
                .functions\
                .getServerContractStateVariables()\
                .call(block_identifier=block_number)

            basis = contract_state_variables[0]
            last_checkpoint_submission_eon = contract_state_variables[1]
            last_checkpoint = contract_state_variables[2]
            is_checkpoint_submitted_for_current_eon = contract_state_variables[
                3]
            has_missed_checkpoint_submission = contract_state_variables[4]
            live_challenge_count = contract_state_variables[5]

        except Exception as exception:
            traceback.print_exc()
            logger.error('Could not query contract state: {}'.format(
                str(exception)))
            return None

        contract_state = ContractState(
            block=block_number,
            confirmed=False,
            basis=crypto.hex_value(basis),
            last_checkpoint_submission_eon=last_checkpoint_submission_eon,
            last_checkpoint=crypto.hex_value(last_checkpoint),
            is_checkpoint_submitted_for_current_eon=
            is_checkpoint_submitted_for_current_eon,
            has_missed_checkpoint_submission=has_missed_checkpoint_submission,
            live_challenge_count=live_challenge_count)

        contract_ledger_states = []
        for token in Token.objects.all():
            if token.block >= block_number:
                continue

            try:
                contract_state_ledger_variables = self.contract\
                    .functions\
                    .getServerContractLedgerStateVariables(current_eon, add_0x_prefix(token.address))\
                    .call(block_identifier=block_number)

                pending_withdrawals = contract_state_ledger_variables[0]
                confirmed_withdrawals = contract_state_ledger_variables[1]
                deposits = contract_state_ledger_variables[2]
                total_balance = contract_state_ledger_variables[3]

                contract_ledger_states.append(
                    ContractLedgerState(
                        token=token,
                        pending_withdrawals=pending_withdrawals,
                        confirmed_withdrawals=confirmed_withdrawals,
                        deposits=deposits,
                        total_balance=total_balance))
            except Exception as exception:
                traceback.print_exc()
                logger.error(
                    'Could not query contract ledger state for {}: {}'.format(
                        token.address, str(exception)))
                contract_ledger_states.append(
                    ContractLedgerState(token=token,
                                        pending_withdrawals=0,
                                        confirmed_withdrawals=0,
                                        deposits=0,
                                        total_balance=0))

        return contract_state, contract_ledger_states
Exemplo n.º 24
0
 def get_basis(self, eon_number, block_identifier='latest'):
     eons_kept = LocalViewInterface.get_contract_parameters().eons_kept
     return self.contract\
         .functions\
         .getParentChainAccumulatorAtSlot(eon_number % eons_kept)\
         .call(block_identifier=block_identifier)
Exemplo n.º 25
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.º 26
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.º 27
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
Exemplo n.º 28
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.º 29
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
Exemplo n.º 30
0
    def validate(self, attrs):
        admission_requests = attrs.pop('admissions')

        if len(admission_requests) > settings.BULK_ADMISSION_LIMIT:
            raise serializers.ValidationError(
                detail='Expected <= {} but got {} admisson requests.'.format(
                    settings.BULK_ADMISSION_LIMIT, len(admission_requests)),
                code=ErrorCode.TOO_MANY_ADMISSION_REQUESTS)

        registration_eon_number = LocalViewInterface.latest().eon_number()
        latest_tos_config = TOSConfig.objects.all().order_by('time').last()

        attrs['signatures'] = []
        attrs['tos_signatures'] = []
        attrs['wallets'] = []

        all_tokens = {}
        for token in Token.objects.all():
            all_tokens[token.address.lower()] = token

        for admission_request in admission_requests:
            address = remove_0x_prefix(admission_request['address'])
            token = all_tokens.get(
                remove_0x_prefix(admission_request['token']).lower())

            if token is None:
                raise serializers.ValidationError(
                    detail='This token {} is not registered.'.format(
                        admission_request['token']),
                    code=ErrorCode.TOKEN_NOT_REGISTERED)

            if BlacklistEntry.objects.filter(address__iexact=address).exists():
                continue

            if Wallet.objects.filter(address__iexact=address,
                                     token=token).exists():
                continue

            wallet = Wallet(token=token,
                            address=address,
                            registration_eon_number=registration_eon_number)

            admission_hash = hex_value(
                wallet.get_admission_hash(registration_eon_number))

            signature = Signature(
                wallet=wallet,
                checksum=admission_hash,
                value=admission_request.get('authorization').get('value'))

            if not signature.is_valid():
                raise serializers.ValidationError(
                    detail='Invalid authorization for address {} and token {}.'
                    .format(admission_request['address'],
                            admission_request['token']),
                    code=ErrorCode.INVALID_ADMISSION_SIGNATURE)

            tos_signature = Signature(
                wallet=wallet,
                checksum=latest_tos_config.digest(),
                value=admission_request.get('tos_signature').get('value'))

            if not tos_signature.is_valid():
                raise serializers.ValidationError(
                    detail=
                    'Invalid TOS (digest: {}) signature for address {} and token {}.'
                    .format(latest_tos_config.digest(),
                            admission_request['address'],
                            admission_request['token']),
                    code=ErrorCode.INVALID_TOS_SIGNATURE)

            attrs['signatures'].append(signature)
            attrs['tos_signatures'].append(tos_signature)
            attrs['wallets'].append(wallet)

        attrs['tos_config'] = latest_tos_config
        return attrs