Skip to content

Commit 2528923

Browse files
authored
Merge pull request #200 from thenewboston-developers/BC-272-add-block-to-blockchain-once-it-gets-enough-confirmations
Unittest is_valid_consensus()
2 parents 418af43 + 289014a commit 2528923

File tree

5 files changed

+55
-12
lines changed

5 files changed

+55
-12
lines changed

node/blockchain/inner_models/block_confirmation.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,13 @@ def validate_signer(self, blockchain_facade):
4545
if not blockchain_facade.is_confirmation_validator(self.signer):
4646
raise ValidationError('Invalid block signer')
4747

48+
def validate_number(self, blockchain_facade):
49+
if blockchain_facade.get_next_block_number() != self.get_number():
50+
raise ValidationError('Invalid block number')
51+
4852
def validate_business_logic(self):
4953
pass
5054

5155
def validate_blockchain_state_dependent(self, blockchain_facade):
5256
self.validate_signer(blockchain_facade)
57+
self.validate_number(blockchain_facade)

node/blockchain/models/block_confirmation.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,31 @@
33
from djongo import models
44

55
from node.blockchain.inner_models import BlockConfirmation as PydanticBlockConfirmation
6+
from node.core.managers import CustomManager
67
from node.core.models import CustomModel
78

89

10+
class BlockConfirmationManager(CustomManager):
11+
12+
def create_from_block_confirmation(self, block_confirmation: PydanticBlockConfirmation):
13+
return self.create(
14+
number=block_confirmation.get_number(),
15+
signer=block_confirmation.signer,
16+
hash=block_confirmation.get_hash(),
17+
body=block_confirmation.json(),
18+
)
19+
20+
def update_or_create_from_block_confirmation(self, block_confirmation: PydanticBlockConfirmation):
21+
return self.update_or_create(
22+
number=block_confirmation.get_number(),
23+
signer=block_confirmation.signer,
24+
defaults={
25+
'hash': block_confirmation.get_hash(),
26+
'body': block_confirmation.json(),
27+
},
28+
)
29+
30+
931
class BlockConfirmation(CustomModel):
1032

1133
_id = models.UUIDField(primary_key=True, default=uuid.uuid4) # noqa: A003
@@ -14,6 +36,8 @@ class BlockConfirmation(CustomModel):
1436
signer = models.CharField(max_length=64) # noqa: A003
1537
body = models.BinaryField()
1638

39+
objects = BlockConfirmationManager()
40+
1741
class Meta:
1842
unique_together = ('number', 'signer')
1943
ordering = unique_together

node/blockchain/tasks/process_block_confirmations.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@
1818

1919

2020
def get_next_block_confirmations(next_block_number) -> list[BlockConfirmation]:
21-
facade = BlockchainFacade.get_instance()
22-
cv_identifiers = facade.get_confirmation_validator_identifiers()
21+
cv_identifiers = BlockchainFacade.get_instance().get_confirmation_validator_identifiers()
2322
return list(BlockConfirmation.objects.filter(number=next_block_number, signer__in=cv_identifiers))
2423

2524

@@ -51,6 +50,8 @@ def get_consensus_block_hash_with_confirmations(
5150
def is_valid_consensus(confirmations: list[BlockConfirmation], minimum_consensus: int):
5251
# Validate confirmations, since they may have not been validated on API call because some of them were added
5352
# much earlier then the next block number become equal to confirmation block number
53+
assert len(set(confirmation.number for confirmation in confirmations)) <= 1
54+
assert len(set(confirmation.hash for confirmation in confirmations)) <= 1
5455
assert len(set(confirmation.signer for confirmation in confirmations)) == len(confirmations)
5556
facade = BlockchainFacade.get_instance()
5657

node/blockchain/tests/test_tasks/test_process_block_confirmations.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
import pytest
44
from model_bakery import baker
55

6-
from node.blockchain.models import Node
6+
from node.blockchain.facade import BlockchainFacade
7+
from node.blockchain.inner_models import BlockConfirmation as PydanticBlockConfirmation
8+
from node.blockchain.models import BlockConfirmation, Node
79
from node.blockchain.tasks.process_block_confirmations import (
8-
get_consensus_block_hash_with_confirmations, get_next_block_confirmations
10+
get_consensus_block_hash_with_confirmations, get_next_block_confirmations, is_valid_consensus
911
)
1012
from node.blockchain.types import NodeRole
1113

@@ -43,3 +45,21 @@ def test_get_consensus_block_hash_with_confirmations():
4345

4446
result = get_consensus_block_hash_with_confirmations(confirmations, 3)
4547
assert result is None
48+
49+
50+
@pytest.mark.parametrize('delta, is_valid', ((0, True), (1, False)))
51+
@pytest.mark.django_db
52+
def test_is_valid_consensus(delta, is_valid):
53+
block_number = BlockchainFacade.get_instance().get_next_block_number()
54+
bc0 = BlockConfirmation.objects.create_from_block_confirmation(
55+
PydanticBlockConfirmation.create(number=block_number + delta, hash_='a' * 64, signing_key='0' * 64)
56+
)
57+
bc1 = BlockConfirmation.objects.create_from_block_confirmation(
58+
PydanticBlockConfirmation.create(number=block_number + delta, hash_='a' * 64, signing_key='1' * 64)
59+
)
60+
bc2 = BlockConfirmation.objects.create_from_block_confirmation(
61+
PydanticBlockConfirmation.create(number=block_number + delta, hash_='a' * 64, signing_key='2' * 64)
62+
)
63+
64+
confirmations = [bc0, bc1, bc2]
65+
assert is_valid_consensus(confirmations, 2) == is_valid

node/blockchain/views/block_confirmation.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,7 @@ def create(self, request, *args, **kwargs):
2525
else:
2626
block_confirmation.validate_business_logic()
2727

28-
ORMBlockConfirmation.objects.update_or_create(
29-
number=block_confirmation.get_number(),
30-
signer=block_confirmation.signer,
31-
defaults={
32-
'hash': block_confirmation.get_hash(),
33-
'body': block_confirmation.json(), # TODO(dmu) LOW: Pick request body AS IS instead
34-
},
35-
)
28+
ORMBlockConfirmation.objects.update_or_create_from_block_confirmation(block_confirmation)
3629

3730
if is_next_block_number and (
3831
ORMBlockConfirmation.objects.filter(number=next_block_number).count() >= facade.get_minimum_consensus()

0 commit comments

Comments
 (0)