Beispiel #1
0
def test_generate_block():
    signing_key, account_number = create_account()
    encoded_account_number = encode_verify_key(verify_key=account_number)

    transactions = [{
        'amount': 1,
        'recipient': random_encoded_account_number(),
    }, {
        'amount': 1,
        'recipient': random_encoded_account_number(),
    }, {
        'amount': 5,
        'recipient': random_encoded_account_number(),
    }]

    block = generate_block(account_number=account_number,
                           balance_lock=encoded_account_number,
                           signing_key=signing_key,
                           transactions=transactions)

    assert block['account_number'] == encoded_account_number
    assert block['message']['balance_key'] == encoded_account_number
    assert len(block['message']['txs']) == 3
    assert len(block['signature']) == SIGNATURE_LENGTH

    # Verify that signature is valid and the message has not been modified
    verify_signature(message=sort_and_encode(block['message']),
                     signature=block['signature'],
                     verify_key=block['account_number'])
Beispiel #2
0
def verify_request_signature(*, request, signed_data_key):
    """
    Verify the request signature

    signed_data - block or message
    """

    node_identifier = request.data.get('node_identifier')
    signature = request.data.get('signature')
    signed_data = request.data.get(signed_data_key)

    for field in ['node_identifier', 'signature', signed_data_key]:
        if not request.data.get(field):
            return request, {ERROR: f'{field} required'}

    error = None

    try:
        verify_signature(message=sort_and_encode(signed_data),
                         signature=signature,
                         verify_key=node_identifier)
    except BadSignatureError as e:
        logger.exception(e)
        error = {ERROR: BAD_SIGNATURE}
    except Exception as e:
        logger.exception(e)
        error = {ERROR: UNKNOWN}

    return request, error
Beispiel #3
0
    def validate(self, data):
        """
        Validate signature
        Validate Tx recipients are unique
        Validate account_number (the sender) is not included as a Tx recipient
        """

        account_number = data['account_number']
        message = data['message']
        txs = message['txs']
        signature = data['signature']

        verify_signature(message=sort_and_encode(message),
                         signature=signature,
                         verify_key=account_number)

        recipient_list = [tx['recipient'] for tx in txs]
        recipient_set = set(recipient_list)

        if len(recipient_list) != len(recipient_set):
            raise serializers.ValidationError('Tx recipients must be unique')

        if account_number in recipient_set:
            raise serializers.ValidationError(
                'Block account_number not allowed as Tx recipient')

        validate_keys(self, data)

        return data
Beispiel #4
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)
Beispiel #5
0
    def inner(request, *args, **kwargs):
        message = request.data.get('message')
        node_identifier = request.data.get('node_identifier')
        signature = request.data.get('signature')

        self_configuration = get_self_configuration(
            exception_class=RuntimeError)

        if node_identifier != self_configuration.node_identifier:
            return Response(status=status.HTTP_401_UNAUTHORIZED)

        try:
            verify_signature(message=sort_and_encode(message),
                             signature=signature,
                             verify_key=node_identifier)
        except BadSignatureError as e:
            logger.exception(e)
            return Response({ERROR: BAD_SIGNATURE},
                            status=status.HTTP_401_UNAUTHORIZED)
        except Exception as e:
            logger.exception(e)
            return Response({ERROR: UNKNOWN},
                            status=status.HTTP_401_UNAUTHORIZED)

        return func(request, *args, **kwargs)
Beispiel #6
0
def send_signed_block(*, block, ip_address, port, protocol, url_path):
    """
    Sign block and send to recipient
    """

    network_signing_key = get_environment_variable('NETWORK_SIGNING_KEY')
    signing_key = SigningKey(network_signing_key, encoder=HexEncoder)
    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:
        logger.exception(e)
Beispiel #7
0
def populate_confirmation_block_queue(*, address, error_handler,
                                      initial_block_identifier):
    """
    Fetch confirmation blocks from primary validator starting with initial_block_identifier
    Add all confirmation blocks to confirmation block queue
    """

    block_identifier = initial_block_identifier
    results = get_confirmation_block_chain_segment(
        address=address, block_identifier=block_identifier)

    error = False

    while results and not error:
        confirmation_block = get_confirmation_block_from_results(
            block_identifier=block_identifier, results=results)

        while confirmation_block:
            message = confirmation_block['message']

            try:
                verify_signature(
                    message=sort_and_encode(message),
                    signature=confirmation_block['signature'],
                    verify_key=confirmation_block['node_identifier'])
            except BadSignatureError as e:
                error_handler(e)
                error = True
                break
            except Exception as e:
                error_handler(e)
                error = True
                break

            serializer = ConfirmationBlockSerializerCreate(data=message)

            if serializer.is_valid():
                _bid = serializer.save()
                print(_bid)
            else:
                error_handler(serializer.errors)
                error = True
                break

            block_identifier = get_message_hash(message=message)
            confirmation_block = get_confirmation_block_from_results(
                block_identifier=block_identifier, results=results)

        if error:
            break

        results = get_confirmation_block_chain_segment(
            address=address, block_identifier=block_identifier)
Beispiel #8
0
def generate_signed_request(*, data, nid_signing_key):
    """Generate and return signed request"""
    node_identifier = get_verify_key(signing_key=nid_signing_key)
    signature = generate_signature(
        message=sort_and_encode(data),
        signing_key=nid_signing_key
    )
    return {
        'message': data,
        'node_identifier': encode_verify_key(verify_key=node_identifier),
        'signature': signature
    }
Beispiel #9
0
def signed_block(block_data, bank_signing_key):
    yield {
        'block':
        block_data,
        'node_identifier':
        encode_verify_key(verify_key=get_verify_key(
            signing_key=bank_signing_key, ), ),
        'signature':
        generate_signature(
            message=sort_and_encode(block_data),
            signing_key=bank_signing_key,
        ),
    }
def is_block_valid(*, block):
    """
    For given block verify:

    - signature
    - account balance exists
    - amount sent does not exceed account balance
    - balance key matches balance lock

    Return boolean indicating validity, senders account balance
    """
    account_number = block.get('account_number')
    message = block.get('message')
    signature = block.get('signature')

    try:
        verify_signature(message=sort_and_encode(message),
                         signature=signature,
                         verify_key=account_number)
    except BadSignatureError:
        return False, None
    except Exception as e:
        capture_exception(e)
        logger.exception(e)
        return False, None

    account_balance = get_account_balance(account_number=account_number)
    account_balance_lock = get_account_balance_lock(
        account_number=account_number)

    if account_balance is None:
        logger.error(f'Account balance for {account_number} not found')
        return False, None

    total_amount_valid, error = is_total_amount_valid(
        block=block, account_balance=account_balance)

    if not total_amount_valid:
        logger.error(error)
        return False, None

    balance_key = message.get('balance_key')

    if balance_key != account_balance_lock:
        logger.error(
            f'Balance key of {balance_key} does not match balance lock of {account_balance_lock}'
        )
        return False, None

    return True, account_balance
Beispiel #11
0
    def validate(self, data):
        """Validate signature, unique Tx recipients, unique Tx fees and account_number not included as a Tx recipient"""
        account_number = data['account_number']
        message = data['message']
        txs = message['txs']
        signature = data['signature']

        verify_signature(message=sort_and_encode(message),
                         signature=signature,
                         verify_key=account_number)

        recipient_list = [tx['recipient'] for tx in txs]
        recipient_set = set(recipient_list)

        if len(recipient_list) != len(recipient_set):
            raise serializers.ValidationError('Tx recipients must be unique')

        if account_number in recipient_set:
            raise serializers.ValidationError(
                'Block account_number not allowed as Tx recipient')

        bank_fee_exists = False
        primary_validator_fee_exists = False

        for tx in txs:
            fee = tx.get('fee', None)

            if fee is None:
                continue

            if fee == BANK:

                if bank_fee_exists:
                    raise serializers.ValidationError(
                        'Multiple bank fees not allowed')
                else:
                    bank_fee_exists = True

            if fee == PRIMARY_VALIDATOR:

                if primary_validator_fee_exists:
                    raise serializers.ValidationError(
                        'Multiple primary validator fees not allowed')
                else:
                    primary_validator_fee_exists = True

        validate_keys(self, data)

        return data
Beispiel #12
0
def generate_block(*, account_number, balance_lock, signing_key, transactions):
    """Generate block"""
    message = {
        'balance_key': balance_lock,
        'txs': sorted(transactions, key=itemgetter('recipient'))
    }

    signature = generate_signature(message=sort_and_encode(message),
                                   signing_key=signing_key)

    block = {
        'account_number': encode_verify_key(verify_key=account_number),
        'message': message,
        'signature': signature
    }

    return block
Beispiel #13
0
def run(send_to_pv=False):
    """
    Create block used for:
    - POST /bank_blocks
    - Bank > PV
    """

    treasury_signing_key = read_signing_key_file(
        os.path.join(SIGNING_KEY_DIR, 'treasury'))
    account_number = get_verify_key(signing_key=treasury_signing_key)

    balance_lock = get_account_balance_lock(
        account_number=TREASURY_ACCOUNT_NUMBER, live_pv=True)
    transactions = [{
        'amount': BANK_TX_FEE,
        'recipient': BANK_ACCOUNT_NUMBER,
    }, {
        'amount': PV_TX_FEE,
        'recipient': PV_ACCOUNT_NUMBER,
    }, {
        'amount': 1.0,
        'recipient': BUCKY_ACCOUNT_NUMBER,
    }]
    block = generate_block(account_number=account_number,
                           balance_lock=balance_lock,
                           signing_key=treasury_signing_key,
                           transactions=transactions)

    bank_nid_sk = read_signing_key_file(
        os.path.join(SIGNING_KEY_DIR, 'bank_nid'))
    bank_nid = get_verify_key(signing_key=bank_nid_sk)
    bank_nid = encode_verify_key(verify_key=bank_nid)
    message = sort_and_encode(block)

    signed_block = {
        'block': block,
        'node_identifier': bank_nid,
        'signature': generate_signature(message=message,
                                        signing_key=bank_nid_sk)
    }

    write_json(os.path.join(BLOCKS_DIR, 'bank-blocks-request.json'),
               signed_block)

    if send_to_pv:
        send_request_to_pv(signed_block)
Beispiel #14
0
    def validate(self, data):
        """
        Validate block signature

        Note: when building the block, message is pulled from 'initial_data' since 'data' has already been processed by
        the MessageSerializer converting all amounts to DecimalField (which are not JSON serializable)
        """

        block = {
            'account_number': data['account_number'],
            'message': self.initial_data['message'],
            'signature': data['signature']
        }
        verify_signature(
            message=sort_and_encode(block['message']),
            signature=block['signature'],
            verify_key=block['account_number']
        )
        return block
Beispiel #15
0
def get_message_hash(*, message):
    """Return has of given message"""
    return sha3(sort_and_encode(message)).digest().hex()