def send_invalid_block_to_banks(*, confirmation_block):
    """
    Send invalid block to banks
    This function is called by the confirmation validators only
    """

    block = confirmation_block['block']
    block_identifier = confirmation_block['block_identifier']

    self_configuration = get_self_configuration(exception_class=RuntimeError)
    primary_validator_node_identifier = self_configuration.primary_validator.node_identifier
    self_configuration.primary_validator = None
    self_configuration.save()

    invalid_block = generate_signed_request(data={
        'block':
        block,
        'block_identifier':
        block_identifier,
        'primary_validator_node_identifier':
        primary_validator_node_identifier
    },
                                            nid_signing_key=get_signing_key())

    for bank in get_banks_with_active_confirmation_services():
        address = format_address(ip_address=bank.ip_address,
                                 port=bank.port,
                                 protocol=bank.protocol)
        url = f'{address}/invalid_blocks'

        try:
            post(url=url, body=invalid_block)
        except Exception as e:
            logger.exception(e)
Esempio n. 2
0
def send_primary_validator_updated_notices():
    """
    Send a notice to all validators that the banks primary validator has been updated

    - 200 response > validator is syncing to new primary validator
    - 400 response > validator is not syncing to new primary validator (can be deleted)
    """
    self_configuration = get_self_configuration(exception_class=RuntimeError)
    primary_validator = self_configuration.primary_validator
    confirmation_validators = Validator.objects.all().exclude(node_identifier=primary_validator.node_identifier)

    data = {
        'ip_address': primary_validator.ip_address,
        'port': primary_validator.port,
        'protocol': primary_validator.protocol
    }

    for confirmation_validator in confirmation_validators:
        signed_request = generate_signed_request(
            data=data,
            nid_signing_key=get_signing_key()
        )
        node_address = format_address(
            ip_address=confirmation_validator.ip_address,
            port=confirmation_validator.port,
            protocol=confirmation_validator.protocol,
        )
        url = f'{node_address}/primary_validator_updated'

        try:
            post(url=url, body=signed_request)
        except Exception as e:
            confirmation_validator.delete()
            logger.exception(e)
Esempio n. 3
0
def sign_block_to_confirm_and_update_head_block_hash(*, block, existing_accounts, new_accounts):
    """
    Sign block to confirm validity
    Update HEAD_BLOCK_HASH
    """

    try:
        head_block_hash = cache.get(HEAD_BLOCK_HASH)

        message = {
            'block': block,
            'block_identifier': head_block_hash,
            'updated_balances': format_updated_balances(existing_accounts, new_accounts)
        }
        confirmation_block = generate_signed_request(
            data=message,
            nid_signing_key=get_signing_key()
        )

        message_hash = get_message_hash(message=message)
        cache.set(HEAD_BLOCK_HASH, message_hash, None)

        return confirmation_block, message_hash
    except Exception as e:
        capture_exception(e)
        logger.exception(e)
Esempio n. 4
0
def test_invalid_account_number(client, account, self_configuration):
    client.patch_json(
        reverse('account-detail', args=[account.account_number + 'abcdef']),
        generate_signed_request(data={'trust': 100},
                                nid_signing_key=get_signing_key()),
        expected=HTTP_404_NOT_FOUND,
    )
Esempio n. 5
0
def send_connection_request(*, node, self_configuration):
    """
    Send connection request to node
    """

    node_address = format_address(
        ip_address=node.ip_address,
        port=node.port,
        protocol=node.protocol,
    )

    signed_request = generate_signed_request(data={
        'ip_address':
        self_configuration.ip_address,
        'port':
        self_configuration.port,
        'protocol':
        self_configuration.protocol
    },
                                             nid_signing_key=get_signing_key())
    url = f'{node_address}/connection_requests'

    try:
        post(url=url, body=signed_request)
    except Exception as e:
        logger.exception(e)
        raise e
Esempio n. 6
0
def send_signed_block(*, block, ip_address, port, protocol, url_path):
    """
    Sign block and send to recipient
    """

    signing_key = get_signing_key()
    node_identifier = get_verify_key(signing_key=signing_key)
    node_identifier = encode_verify_key(verify_key=node_identifier)
    message = sort_and_encode(block)

    signed_block = {
        'block': block,
        'node_identifier': node_identifier,
        'signature': generate_signature(message=message,
                                        signing_key=signing_key)
    }
    node_address = format_address(ip_address=ip_address,
                                  port=port,
                                  protocol=protocol)
    url = f'{node_address}{url_path}'

    try:
        post(url=url, body=signed_block)
    except Exception as e:
        request_new_primary_validator()
        logger.exception(e)
def crawl_request(client, command, status):
    return client.post_json(
        reverse('crawl-list'),
        generate_signed_request(data={'crawl': command},
                                nid_signing_key=get_signing_key()),
        expected=status,
    )
Esempio n. 8
0
def send_confirmation_block_history_request():
    """
    Request missing blocks from the primary validator
    """

    self_configuration = get_self_configuration(exception_class=RuntimeError)
    primary_validator = self_configuration.primary_validator

    address = format_address(
        ip_address=primary_validator.ip_address,
        port=primary_validator.port,
        protocol=primary_validator.protocol
    )
    url = f'{address}/confirmation_block_history'

    signed_request = generate_signed_request(
        data={
            'block_identifier': cache.get(HEAD_BLOCK_HASH)
        },
        nid_signing_key=get_signing_key()
    )

    try:
        post(url=url, body=signed_request)
    except Exception as e:
        capture_exception(e)
        logger.exception(e)
Esempio n. 9
0
async def test_crawl_status_async(client, confirmation_validator_configuration, celery_worker):

    communicator = WebsocketCommunicator(
        CrawlStatusConsumer,
        'ws/crawl_status'
    )
    connected, subprotocol = await communicator.connect()
    assert connected

    await sync_to_async(
        client.post_json
    )(
        reverse('crawl-list'),
        generate_signed_request(
            data={
                'crawl': CRAWL_COMMAND_START
            },
            nid_signing_key=get_signing_key()
        ),
        expected=HTTP_200_OK
    )
    async_response = await communicator.receive_json_from(timeout=3)
    await communicator.disconnect()
    crawl_status = async_response['payload']

    assert async_response['notification_type'] == CRAWL_STATUS_NOTIFICATION
    assert crawl_status['crawl_last_completed']
    assert crawl_status['crawl_status'] == CRAWL_STATUS_NOT_CRAWLING
Esempio n. 10
0
def connect_to_primary_validator(*, primary_validator):
    """
    Connect to a validator

    - used in the syncing process
    """
    self_configuration = get_self_configuration(exception_class=RuntimeError)

    primary_validator_address = format_address(
        ip_address=primary_validator.ip_address,
        port=primary_validator.port,
        protocol=primary_validator.protocol,
    )

    if is_connected_to_primary_validator(
            primary_validator_address=primary_validator_address,
            self_configuration=self_configuration):
        return

    signed_request = generate_signed_request(data={
        'ip_address':
        self_configuration.ip_address,
        'port':
        self_configuration.port,
        'protocol':
        self_configuration.protocol
    },
                                             nid_signing_key=get_signing_key())
    url = f'{primary_validator_address}/connection_requests'

    try:
        post(url=url, body=signed_request)
    except Exception as e:
        logger.exception(e)
        raise e
Esempio n. 11
0
def test_bank_account_number_trust(client, account, self_configuration, trust,
                                   response_msg):
    response = client.patch_json(
        reverse('account-detail', args=[account.account_number]),
        generate_signed_request(data={'trust': trust},
                                nid_signing_key=get_signing_key()),
        expected=HTTP_400_BAD_REQUEST,
    )
    assert response['trust'] == [response_msg]
Esempio n. 12
0
def test_banks_post(client, bank_fake_data, self_configuration):
    response = client.post_json(reverse('bank-list'),
                                generate_signed_request(
                                    data=bank_fake_data,
                                    nid_signing_key=get_signing_key(),
                                ),
                                expected=status.HTTP_201_CREATED)
    bank_fake_data['trust'] = f'{bank_fake_data["trust"]:.2f}'
    assert response == bank_fake_data
Esempio n. 13
0
def test_accounts_patch(client, account, account_fake_data,
                        self_configuration):
    response = client.patch_json(
        reverse('account-detail', args=[account.account_number]),
        generate_signed_request(data=account_fake_data,
                                nid_signing_key=get_signing_key()),
        expected=HTTP_200_OK,
    )
    assert response['account_number'] != account_fake_data['account_number']
    assert float(response['trust']) == account_fake_data['trust']
Esempio n. 14
0
def test_banks_patch(client, bank, bank_fake_data, self_configuration):
    response = client.patch_json(
        reverse('bank-detail', args=[bank.node_identifier]),
        generate_signed_request(
            data=bank_fake_data,
            nid_signing_key=get_signing_key(),
        ),
        expected=status.HTTP_200_OK,
    )
    assert response['trust'] == f'{bank_fake_data["trust"]:.2f}'
def test_crawl_start_200(client, primary_validator_configuration):
    client.post_json(
        reverse('crawl-list'),
        generate_signed_request(
            data={'crawl': CRAWL_COMMAND_START},
            nid_signing_key=get_signing_key(),
        ),
        expected=HTTP_404_NOT_FOUND,
    )
    assert cache.get(CRAWL_STATUS) is None
Esempio n. 16
0
def test_validator_patch(client, primary_validator_configuration, validator,
                         validator_fake_data):
    response = client.patch_json(
        reverse('validator-detail', args=[validator.node_identifier]),
        generate_signed_request(
            data=validator_fake_data,
            nid_signing_key=get_signing_key(),
        ),
        expected=HTTP_200_OK,
    )
    assert float(response['trust']) == validator_fake_data['trust']
Esempio n. 17
0
def test_banks_post(client, primary_validator_configuration, bank, bank_fake_data):
    response = client.post_json(
        reverse('bank-list'),
        generate_signed_request(
            data=bank_fake_data,
            nid_signing_key=get_signing_key(),
        ),
        expected=HTTP_200_OK,
    )

    assert float(response['trust']) == bank_fake_data['trust']
Esempio n. 18
0
def test_update_bank_with_invalid_trust_value(client, bank, self_configuration,
                                              trust, response_msg):
    response = client.patch_json(
        reverse('bank-detail', args=[bank.node_identifier]),
        generate_signed_request(
            data={'trust': trust},
            nid_signing_key=get_signing_key(),
        ),
        expected=status.HTTP_400_BAD_REQUEST,
    )

    assert response['trust'] == [response_msg]
Esempio n. 19
0
def test_create_bank_with_invalid_trust_value(client, bank, bank_fake_data,
                                              self_configuration, trust,
                                              response_msg):
    bank_fake_data['trust'] = trust
    response = client.post_json(
        reverse('bank-list'),
        generate_signed_request(
            data=bank_fake_data,
            nid_signing_key=get_signing_key(),
        ),
        expected=status.HTTP_400_BAD_REQUEST,
    )

    assert response['trust'] == [response_msg]
Esempio n. 20
0
def send_signed_post_request(*, data, ip_address, port, protocol, url_path):
    """Sign data and send to recipient"""
    signed_request = generate_signed_request(data=data,
                                             nid_signing_key=get_signing_key())

    node_address = format_address(ip_address=ip_address,
                                  port=port,
                                  protocol=protocol)
    url = f'{node_address}{url_path}'

    try:
        post(url=url, body=signed_request)
    except Exception as e:
        capture_exception(e)
        logger.exception(e)
Esempio n. 21
0
def set_primary_validator():
    """
    Set the primary validator to the validator that is the:

    - most trusted
    - online
    - configured as a primary validator
    """
    self_configuration = get_self_configuration(exception_class=RuntimeError)
    primary_validator = self_configuration.primary_validator
    primary_validator_candidates = get_primary_validator_candidates(current_primary_validator=primary_validator)

    for validator in primary_validator_candidates:
        signed_request = generate_signed_request(
            data={
                'validator_node_identifier': validator.node_identifier
            },
            nid_signing_key=get_signing_key()
        )
        node_address = format_address(
            ip_address=validator.ip_address,
            port=validator.port,
            protocol=validator.protocol,
        )
        url = f'{node_address}/upgrade_request'

        try:
            validator_config = post(url=url, body=signed_request)

            if validator_config['node_type'] != PRIMARY_VALIDATOR:
                continue

            self_configuration.primary_validator = validator
            self_configuration.save()

            send_primary_validator_updated_notices.delay()
            send_primary_validator_updated_notification()
            return
        except Exception as e:
            logger.exception(e)
def send_upgrade_notices(*, requesting_banks_node_identifier):
    """
    Description:

    - notice from a previous confirmation validator that they are now a primary validator
    - triggered from an /upgrade_request from the validators most trusted bank
    - banks that trust self more than their existing primary validator will set self as new primary validator
    - banks that do not trust self more than their existing primary validator will remain on their existing network and
      can therefore be deleted

    Responses:
    - 200 response > bank set self as new primary validator
    - 400 response > bank is remaining on their existing network (can be deleted)

    Notes:
        The requesting (most trusted) bank may be excluded from notice recipients since it will already receive the
        updated information from the /upgrade_request response
    """
    banks = Bank.objects.all().exclude(node_identifier=requesting_banks_node_identifier)

    for bank in banks:
        signed_request = generate_signed_request(
            data={
                'bank_node_identifier': bank.node_identifier
            },
            nid_signing_key=get_signing_key()
        )
        node_address = format_address(
            ip_address=bank.ip_address,
            port=bank.port,
            protocol=bank.protocol,
        )
        url = f'{node_address}/upgrade_notice'

        try:
            post(url=url, body=signed_request)
        except Exception as e:
            bank.delete()
            capture_exception(e)
            logger.exception(e)
Esempio n. 23
0
    def post(self, request, format=None):
        serializer = FormSerializer(data=request.data,
                                    context={"request": request})
        if serializer.is_valid(raise_exception=True):
            try:
                amount = FaucetOption.objects.get(
                    pk=serializer.data['faucet_option_id'])
                url_str = serializer.data['url']

                platform = get_platform(url_str)
                if platform:
                    post = platform.process(url_str, amount)

                    if post:
                        receiver_account_number = post.get_account_number()
                        post_id = post.get_id()
                        platform = post.get_platform()
                        user_id = post.get_user()

                        bank_config = SelfConfiguration.objects.first()
                        pv_config = bank_config.primary_validator

                        signing_key = get_signing_key()
                        sender_account_number = encode_verify_key(
                            verify_key=signing_key.verify_key)

                        account = validate_post_exists(receiver_account_number,
                                                       post_id)
                        faucet_model = validate_expiry(account, user_id)

                        if account and not faucet_model:
                            response = requests.get((
                                f'{pv_config.protocol}://{pv_config.ip_address}'
                                f':{pv_config.port}'
                                f'/accounts/'
                                f'{sender_account_number}/balance_lock'))

                            if response.status_code == 200:
                                balance_lock = response.json().get(
                                    'balance_lock')
                                if not balance_lock:
                                    balance_lock = bank_config.node_identifier

                                faucet_model, created = (
                                    FaucetModel.objects.update_or_create(
                                        account=account,
                                        social_user_id=user_id,
                                        social_type=platform,
                                        defaults={
                                            'next_valid_access_time':
                                            (timezone.now() +
                                             timedelta(hours=amount.delay))
                                        }))

                                post_model, created = PostModel.objects.get_or_create(
                                    post_id=post_id,
                                    reward=amount,
                                    social_user=faucet_model)

                                transactions = [{
                                    'amount':
                                    amount.coins,
                                    'recipient':
                                    receiver_account_number,
                                    'memo':
                                    "Thank you for using TNBExplorer testnet"
                                }, {
                                    'amount':
                                    bank_config.default_transaction_fee,
                                    'recipient': bank_config.account_number,
                                    'fee': "BANK"
                                }, {
                                    'amount':
                                    pv_config.default_transaction_fee,
                                    'recipient': pv_config.account_number,
                                    'fee': "PRIMARY_VALIDATOR"
                                }]

                                block = generate_block(
                                    account_number=signing_key.verify_key,
                                    balance_lock=balance_lock,
                                    signing_key=signing_key,
                                    transactions=transactions)
                                serializer = BlockSerializerCreate(
                                    data=block,
                                    context={'request': request},
                                )
                                serializer.is_valid(raise_exception=True)
                                block = serializer.save()
                                return Response(
                                    success_response((
                                        f'SUCCESS! {amount.coins} faucet funds'
                                        f' transferred to {receiver_account_number}.'
                                    )))
                            else:
                                return Response(error_response(
                                    'Unable to obtain TNB account details!'),
                                                status=status.
                                                HTTP_500_INTERNAL_SERVER_ERROR)
                        else:
                            if faucet_model:
                                duration = (
                                    faucet_model.next_valid_access_time -
                                    timezone.now())
                                totsec = duration.total_seconds()
                                h = int(totsec // 3600)
                                m = int((totsec % 3600) // 60)
                                sec = round((totsec % 3600) % 60)
                                return Response(
                                    error_response(
                                        ('Slow down! Try again after ('
                                         f'{h} hours {m} mins and {sec} secs'
                                         ') till cooldown period expires.')),
                                    status=status.HTTP_429_TOO_MANY_REQUESTS)
                            else:
                                return Response(
                                    error_response(
                                        ('Same post cannot be used again! '
                                         ' Try again with a new one :P')),
                                    status=status.HTTP_400_BAD_REQUEST)
                    else:
                        return Response(error_response(
                            ('Failed to extract information!'
                             ' Make sure post is public,'
                             ' contains #TNBFaucet and your account number')),
                                        status=status.HTTP_400_BAD_REQUEST)
                else:
                    return Response(error_response(
                        'Only facebook and twitter URL allowed!'),
                                    status=status.HTTP_400_BAD_REQUEST)
            except FaucetOption.DoesNotExist:
                return Response(error_response('bad request format/data'),
                                status=status.HTTP_400_BAD_REQUEST)
        else:
            return Response(error_response('bad request format/data'),
                            status=status.HTTP_400_BAD_REQUEST)
Esempio n. 24
0
def faucet_view(request):
    form = FaucetForm()
    if request.method == 'POST':
        form = FaucetForm(request.POST)
        if form.is_valid():
            # form data
            url_str = form.cleaned_data['url']
            amount = form.cleaned_data['amount']

            platform = get_platform(url_str)
            if platform:
                post = platform.process(url_str, amount)

                if post:
                    receiver_account_number = post.get_account_number()
                    post_id = post.get_id()
                    platform = post.get_platform()
                    user_id = post.get_user()

                    bank_config = SelfConfiguration.objects.first()
                    pv_config = bank_config.primary_validator

                    signing_key = get_signing_key()
                    sender_account_number = encode_verify_key(
                        verify_key=signing_key.verify_key)

                    account = validate_post_exists(receiver_account_number,
                                                   post_id)
                    faucet_model = validate_expiry(account, user_id)

                    if account and not faucet_model:
                        response = requests.get(
                            (f'{pv_config.protocol}://{pv_config.ip_address}'
                             f':{pv_config.port}'
                             f'/accounts/'
                             f'{sender_account_number}/balance_lock'))

                        if response.status_code == 200:
                            balance_lock = response.json().get('balance_lock')
                            if not balance_lock:
                                balance_lock = bank_config.node_identifier

                            faucet_model, created = (
                                FaucetModel.objects.update_or_create(
                                    account=account,
                                    social_user_id=user_id,
                                    social_type=platform,
                                    defaults={
                                        'next_valid_access_time':
                                        (timezone.now() +
                                         timedelta(hours=amount.delay))
                                    }))

                            post_model, created = PostModel.objects.get_or_create(
                                post_id=post_id,
                                reward=amount,
                                social_user=faucet_model)

                            transactions = [{
                                'amount':
                                amount.coins,
                                'recipient':
                                receiver_account_number,
                                'memo':
                                "Thank you for using TNBExplorer testnet"
                            }, {
                                'amount': bank_config.default_transaction_fee,
                                'recipient': bank_config.account_number,
                                'fee': "BANK"
                            }, {
                                'amount': pv_config.default_transaction_fee,
                                'recipient': pv_config.account_number,
                                'fee': "PRIMARY_VALIDATOR"
                            }]

                            block = generate_block(
                                account_number=signing_key.verify_key,
                                balance_lock=balance_lock,
                                signing_key=signing_key,
                                transactions=transactions)
                            serializer = BlockSerializerCreate(
                                data=block,
                                context={'request': request},
                            )
                            serializer.is_valid(raise_exception=True)
                            block = serializer.save()
                            messages.success(request, (
                                f'SUCCESS! {amount.coins} faucet funds'
                                f' transferred to {receiver_account_number}.'))
                            form = FaucetForm()
                        else:
                            messages.error(
                                request,
                                'Unable to obtain TNB account details!')
                    else:
                        form = FaucetForm()
                        if faucet_model:
                            duration = (faucet_model.next_valid_access_time -
                                        timezone.now())
                            totsec = duration.total_seconds()
                            h = int(totsec // 3600)
                            m = int((totsec % 3600) // 60)
                            sec = round((totsec % 3600) % 60)
                            messages.error(
                                request, ('Slow down! Try again after ('
                                          f'{h} hours {m} mins and {sec} secs'
                                          ') till cooldown period expires.'))
                        else:
                            messages.error(request,
                                           ('Same post cannot be used again! '
                                            ' Try again with a new one :P'))
                else:
                    messages.error(
                        request,
                        ('Failed to extract information!'
                         ' Make sure post is public,'
                         ' contains #TNBFaucet and your account number'))
            else:
                messages.error(request,
                               'Only facebook and twitter URL allowed!')
        else:
            messages.error(request,
                           'Form invalid! Please provide correct details!')

    context = {'form': form}
    return render(request, 'index.html', context)