Skip to content

Commit 9201de5

Browse files
committed
process_block_confirmations_task drafted
1 parent 18b2d35 commit 9201de5

File tree

4 files changed

+61
-10
lines changed

4 files changed

+61
-10
lines changed

node/blockchain/facade.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,3 +260,11 @@ def get_primary_validator(self) -> Optional[Node]:
260260
def yield_nodes(roles=None):
261261
for node in ORMNode.objects.filter_by_roles(roles):
262262
yield node.get_node()
263+
264+
@staticmethod
265+
def get_confirmation_validator_identifiers():
266+
return ORMNode.objects.filter_by_roles((NodeRole.CONFIRMATION_VALIDATOR,)).values_list('_id', flat=True)
267+
268+
@staticmethod
269+
def is_confirmation_validator(identifier):
270+
return ORMNode.objects.is_confirmation_validator(identifier)

node/blockchain/inner_models/block_confirmation.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from node.core.utils.cryptography import derive_public_key
77
from node.core.utils.types import non_negative_int
88

9-
from ..types import AccountNumber, Hash, NodeRole, Signature
9+
from ..types import AccountNumber, Hash, Signature
1010
from .base import BaseModel
1111

1212

@@ -42,8 +42,7 @@ def validate_signer(self, blockchain_facade):
4242
if not blockchain_facade.has_blocks():
4343
return
4444

45-
cv_identifiers = (node.identifier for node in blockchain_facade.yield_nodes({NodeRole.CONFIRMATION_VALIDATOR}))
46-
if self.signer not in cv_identifiers:
45+
if not blockchain_facade.is_confirmation_validator(self.signer):
4746
raise ValidationError('Invalid block signer')
4847

4948
def validate_business_logic(self):

node/blockchain/models/node.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ class NodeManager(DjongoManager.from_queryset(NodeQuerySet)): # type: ignore
5858
def get_queryset(self):
5959
return super().get_queryset().filter(node__isnull=False)
6060

61+
def is_confirmation_validator(self, identifier):
62+
return self.filter_by_roles((NodeRole.CONFIRMATION_VALIDATOR,)).filter(_id=identifier).exists()
63+
6164

6265
class Node(AccountState):
6366

node/blockchain/tasks/process_block_confirmations.py

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,57 @@
1+
from itertools import groupby
2+
from operator import attrgetter
3+
14
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()
246

3-
# from node.blockchain.facade import BlockchainFacade
47+
return True
448

549

650
@shared_task
751
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()
1455

1556

1657
def start_process_block_confirmations_task():

0 commit comments

Comments
 (0)