Skip to content

Commit

Permalink
Unittest is_valid_consensus()
Browse files Browse the repository at this point in the history
  • Loading branch information
dmugtasimov committed Mar 20, 2022
1 parent 418af43 commit 289014a
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 12 deletions.
5 changes: 5 additions & 0 deletions node/blockchain/inner_models/block_confirmation.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,13 @@ def validate_signer(self, blockchain_facade):
if not blockchain_facade.is_confirmation_validator(self.signer):
raise ValidationError('Invalid block signer')

def validate_number(self, blockchain_facade):
if blockchain_facade.get_next_block_number() != self.get_number():
raise ValidationError('Invalid block number')

def validate_business_logic(self):
pass

def validate_blockchain_state_dependent(self, blockchain_facade):
self.validate_signer(blockchain_facade)
self.validate_number(blockchain_facade)
24 changes: 24 additions & 0 deletions node/blockchain/models/block_confirmation.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,31 @@
from djongo import models

from node.blockchain.inner_models import BlockConfirmation as PydanticBlockConfirmation
from node.core.managers import CustomManager
from node.core.models import CustomModel


class BlockConfirmationManager(CustomManager):

def create_from_block_confirmation(self, block_confirmation: PydanticBlockConfirmation):
return self.create(
number=block_confirmation.get_number(),
signer=block_confirmation.signer,
hash=block_confirmation.get_hash(),
body=block_confirmation.json(),
)

def update_or_create_from_block_confirmation(self, block_confirmation: PydanticBlockConfirmation):
return self.update_or_create(
number=block_confirmation.get_number(),
signer=block_confirmation.signer,
defaults={
'hash': block_confirmation.get_hash(),
'body': block_confirmation.json(),
},
)


class BlockConfirmation(CustomModel):

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

objects = BlockConfirmationManager()

class Meta:
unique_together = ('number', 'signer')
ordering = unique_together
Expand Down
5 changes: 3 additions & 2 deletions node/blockchain/tasks/process_block_confirmations.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@


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


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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
import pytest
from model_bakery import baker

from node.blockchain.models import Node
from node.blockchain.facade import BlockchainFacade
from node.blockchain.inner_models import BlockConfirmation as PydanticBlockConfirmation
from node.blockchain.models import BlockConfirmation, Node
from node.blockchain.tasks.process_block_confirmations import (
get_consensus_block_hash_with_confirmations, get_next_block_confirmations
get_consensus_block_hash_with_confirmations, get_next_block_confirmations, is_valid_consensus
)
from node.blockchain.types import NodeRole

Expand Down Expand Up @@ -43,3 +45,21 @@ def test_get_consensus_block_hash_with_confirmations():

result = get_consensus_block_hash_with_confirmations(confirmations, 3)
assert result is None


@pytest.mark.parametrize('delta, is_valid', ((0, True), (1, False)))
@pytest.mark.django_db
def test_is_valid_consensus(delta, is_valid):
block_number = BlockchainFacade.get_instance().get_next_block_number()
bc0 = BlockConfirmation.objects.create_from_block_confirmation(
PydanticBlockConfirmation.create(number=block_number + delta, hash_='a' * 64, signing_key='0' * 64)
)
bc1 = BlockConfirmation.objects.create_from_block_confirmation(
PydanticBlockConfirmation.create(number=block_number + delta, hash_='a' * 64, signing_key='1' * 64)
)
bc2 = BlockConfirmation.objects.create_from_block_confirmation(
PydanticBlockConfirmation.create(number=block_number + delta, hash_='a' * 64, signing_key='2' * 64)
)

confirmations = [bc0, bc1, bc2]
assert is_valid_consensus(confirmations, 2) == is_valid
9 changes: 1 addition & 8 deletions node/blockchain/views/block_confirmation.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,7 @@ def create(self, request, *args, **kwargs):
else:
block_confirmation.validate_business_logic()

ORMBlockConfirmation.objects.update_or_create(
number=block_confirmation.get_number(),
signer=block_confirmation.signer,
defaults={
'hash': block_confirmation.get_hash(),
'body': block_confirmation.json(), # TODO(dmu) LOW: Pick request body AS IS instead
},
)
ORMBlockConfirmation.objects.update_or_create_from_block_confirmation(block_confirmation)

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

0 comments on commit 289014a

Please sign in to comment.