|
| 1 | +from itertools import groupby |
| 2 | +from operator import attrgetter |
| 3 | + |
1 | 4 | from celery import shared_task
|
| 5 | +from django.db import transaction |
| 6 | + |
| 7 | +from node.blockchain.constants import BLOCK_LOCK |
| 8 | +from node.blockchain.facade import BlockchainFacade |
| 9 | +from node.blockchain.models import BlockConfirmation, PendingBlock |
| 10 | +from node.blockchain.utils.lock import lock |
| 11 | + |
| 12 | + |
| 13 | +@lock(BLOCK_LOCK) |
| 14 | +def process_next_block(): |
| 15 | + facade = BlockchainFacade.get_instance() |
| 16 | + next_block_number = facade.get_next_block_number() |
| 17 | + cv_identifiers = facade.get_confirmation_validator_identifiers() |
| 18 | + confirmations = BlockConfirmation.objects.filter(number=next_block_number, signer__in=cv_identifiers) |
| 19 | + grouped_confirmations = groupby(confirmations.order_by('hash'), key=attrgetter('hash')) |
| 20 | + minimum_consensus = len(cv_identifiers) * 2 / 3 |
| 21 | + |
| 22 | + finalizable_hashes = [ |
| 23 | + hash_ for hash_, _confirmations in grouped_confirmations if len(list(_confirmations)) >= minimum_consensus |
| 24 | + ] |
| 25 | + |
| 26 | + if not finalizable_hashes: |
| 27 | + return False |
| 28 | + |
| 29 | + if len(finalizable_hashes) >= 2: |
| 30 | + # We should never get here |
| 31 | + raise ValueError('More than one finalizable hash found') |
| 32 | + |
| 33 | + assert len(finalizable_hashes) == 1 |
| 34 | + hash_ = finalizable_hashes[0] |
| 35 | + |
| 36 | + pending_block = PendingBlock.objects.get_or_none(number=next_block_number, hash=hash_) |
| 37 | + if pending_block is None: |
| 38 | + # TODO(dmu) CRITICAL: https://thenewboston.atlassian.net/browse/BC-283 |
| 39 | + raise NotImplementedError('Edge case of processing confirmed missing pending block is not implemented') |
| 40 | + |
| 41 | + with transaction.atomic(): |
| 42 | + facade.add_block_from_json(pending_block.body, expect_locked=True) |
| 43 | + # There may be blocks with other hashes therefore we delete all of them |
| 44 | + PendingBlock.objects.filter(number__lte=next_block_number).delete() |
| 45 | + BlockConfirmation.objects.filter(number__lte=next_block_number).delete() |
2 | 46 |
|
3 |
| -# from node.blockchain.facade import BlockchainFacade |
| 47 | + return True |
4 | 48 |
|
5 | 49 |
|
6 | 50 | @shared_task
|
7 | 51 | def process_block_confirmations_task():
|
8 |
| - # TODO(dmu) CRITICAL: Implement https://thenewboston.atlassian.net/browse/BC-272 |
9 |
| - # facade = BlockchainFacade.get_instance() |
10 |
| - # next_block_number = facade.get_next_block_number() |
11 |
| - # get all confirmation for next block number |
12 |
| - # if there is no 2/3 |
13 |
| - raise NotImplementedError |
| 52 | + should_process_next_block = True |
| 53 | + while should_process_next_block: |
| 54 | + should_process_next_block = process_next_block() |
14 | 55 |
|
15 | 56 |
|
16 | 57 | def start_process_block_confirmations_task():
|
|
0 commit comments