def get(request, block_identifier): """ description: List confirmation block chain segment (starting at the block_identifier) """ if len(block_identifier) != HEAD_HASH_LENGTH: return Response( { ERROR: f'block_identifier must be {HEAD_HASH_LENGTH} characters long' }, status=status.HTTP_403_FORBIDDEN) confirmation_block_cache_key = get_confirmation_block_cache_key( block_identifier=block_identifier) confirmation_block = cache.get(confirmation_block_cache_key) if not confirmation_block: return Response(status=status.HTTP_404_NOT_FOUND) counter = 0 results = [] while confirmation_block and counter < settings.CONFIRMATION_BLOCK_CHAIN_SEGMENT_LENGTH: results.append(confirmation_block) message = confirmation_block['message'] message_hash = get_message_hash(message=message) confirmation_block_cache_key = get_confirmation_block_cache_key( block_identifier=message_hash) confirmation_block = cache.get(confirmation_block_cache_key) counter += 1 return Response(results)
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)
def get_updated_accounts(*, sender_account_balance, validated_block): """Return the updated balances of all accounts involved""" existing_accounts = [] new_accounts = [] message = validated_block['message'] txs = message['txs'] total_amount = sum([tx['amount'] for tx in txs]) existing_accounts.append({ 'account_number': validated_block['account_number'], 'balance': sender_account_balance - total_amount, 'balance_lock': get_message_hash(message=message) }) for tx in txs: amount = tx['amount'] recipient = tx['recipient'] recipient_account_balance = get_account_balance(account_number=recipient) if recipient_account_balance is None: new_accounts.append({ 'account_number': recipient, 'balance': amount }) else: existing_accounts.append({ 'account_number': recipient, 'balance': recipient_account_balance + amount }) return existing_accounts, new_accounts
def send_confirmation_block_history(*, block_identifier, ip_address, port, protocol): """ Send historical confirmation blocks (starting with the block_identifier) to the confirmation validator """ address = format_address(ip_address=ip_address, port=port, protocol=protocol) url = f'{address}/confirmation_blocks' valid_confirmation_block = get_valid_confirmation_block( block_identifier=block_identifier) while valid_confirmation_block: try: post(url=url, body=valid_confirmation_block) except Exception as e: capture_exception(e) logger.exception(e) block_identifier = get_message_hash( message=valid_confirmation_block['message']) valid_confirmation_block = get_valid_confirmation_block( block_identifier=block_identifier)
def get_initial_block_identifier(self, primary_validator_config): """ Return initial block identifier If seed_block_identifier, fetch related (seed) block and hash to get initial block identifier Otherwise, return root_account_file_hash """ seed_block_identifier = primary_validator_config.get( 'seed_block_identifier') if not seed_block_identifier: self_configuration = get_self_configuration( exception_class=RuntimeError) root_account_file_hash = self_configuration.root_account_file_hash pv_root_account_file_hash = primary_validator_config.get( 'root_account_file_hash') if root_account_file_hash != pv_root_account_file_hash: self._error( 'SelfConfiguration.root_account_file_hash does not match primary validator root_account_file_hash' ) self._error(f'SelfConfiguration: {root_account_file_hash}') self._error(f'Primary validator: {pv_root_account_file_hash}') raise RuntimeError() return root_account_file_hash address = self.get_primary_validator_address() confirmation_block = get_confirmation_block( address=address, block_identifier=seed_block_identifier) return get_message_hash(message=confirmation_block['message'])
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)
def get_initial_block_identifier(): """ Return initial block identifier If seed_block_identifier, fetch related (seed) block and hash to get initial block identifier Otherwise, return root_account_file_hash """ self_configuration = get_self_configuration(exception_class=RuntimeError) seed_block_identifier = self_configuration.seed_block_identifier if seed_block_identifier: confirmation_block_cache_key = get_confirmation_block_cache_key( block_identifier=seed_block_identifier) confirmation_block = cache.get(confirmation_block_cache_key) return get_message_hash(message=confirmation_block['message']) return self_configuration.root_account_file_hash
def confirmation_block_data(block_data, block_identifier, primary_validator_signing_key, account_number): yield generate_signed_request( data={ 'block': block_data, 'block_identifier': block_identifier, 'updated_balances': [{ 'account_number': block_data['account_number'], 'balance': 1, 'balance_lock': get_message_hash(message=block_data['message']), }], }, nid_signing_key=primary_validator_signing_key, )
def send_block_to_bank(block): """ Send block to bank """ next_balance_lock = get_message_hash(message=block['message']) print(f'\nNext balance lock will be: {next_balance_lock}\n') bank_address = format_address(ip_address='167.99.173.247', port=None, protocol='http') url = f'{bank_address}/blocks' results = post(url=url, body=block) if isinstance(results, dict): for k, v in results.items(): print(f'{k}: {v}') write_json(os.path.join(BLOCKS_DIR, 'blocks-response.json'), results)
def block_chain_view(_): """ Return block chain """ block_chain = {} initial_block_identifier = get_initial_block_identifier() block_identifier = initial_block_identifier valid_confirmation_block = get_valid_confirmation_block(block_identifier=block_identifier) i = 0 while valid_confirmation_block: block_chain[i] = valid_confirmation_block block_identifier = get_message_hash(message=valid_confirmation_block['message']) valid_confirmation_block = get_valid_confirmation_block(block_identifier=block_identifier) i += 1 return Response({ 'initial_block_identifier': initial_block_identifier, 'block_chain': block_chain })
def block_chain_view(_): """ Return block chain """ block_chain = {} initial_block_identifier = get_initial_block_identifier() block_identifier = initial_block_identifier confirmation_block_cache_key = get_confirmation_block_cache_key(block_identifier=block_identifier) confirmation_block = cache.get(confirmation_block_cache_key) i = 0 while confirmation_block: block_chain[i] = confirmation_block block_identifier = get_message_hash(message=confirmation_block['message']) confirmation_block_cache_key = get_confirmation_block_cache_key(block_identifier=block_identifier) confirmation_block = cache.get(confirmation_block_cache_key) i += 1 return Response({ 'initial_block_identifier': initial_block_identifier, 'block_chain': block_chain })