Skip to content

Commit bca2dc4

Browse files
authored
Merge pull request #189 from thenewboston-developers/BC-271-cv-to-cv-block-confirmation
Block confirmations
2 parents 1a982c9 + ed8dd61 commit bca2dc4

File tree

13 files changed

+158
-6
lines changed

13 files changed

+158
-6
lines changed

node/blockchain/facade.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ def get_next_block_identifier(self) -> Optional[BlockIdentifier]:
141141

142142
@staticmethod
143143
def get_block_by_number(number) -> Block:
144-
block = get_block_model().objects.get_block_by_number(_id=number)
144+
block = get_block_model().objects.get_block_by_number(number)
145145
return block.get_block() if block else None
146146

147147
@staticmethod

node/blockchain/inner_models/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from .account_state import AccountState # noqa: F401
22
from .block import Block, CoinTransferBlock, GenesisBlock, NodeDeclarationBlock # noqa: F401
3+
from .block_confirmation import BlockConfirmation, BlockConfirmationMessage # noqa: F401
34
from .block_message import ( # noqa: F401
45
BlockMessage, BlockMessageUpdate, CoinTransferBlockMessage, GenesisBlockMessage, NodeDeclarationBlockMessage,
56
PVScheduleUpdateBlockMessage

node/blockchain/inner_models/block.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ def validate_signature(cls, values):
4444
if cls == Block: # only child classes signature validation makes sense
4545
return values
4646

47-
validate_signature_helper(values)
48-
return values
47+
return validate_signature_helper(values)
4948

5049
def validate_business_logic(self):
5150
self.message.validate_business_logic()
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
from pydantic import root_validator
2+
3+
from node.blockchain.mixins.crypto import SignableMixin, validate_signature_helper
4+
from node.blockchain.mixins.validatable import ValidatableMixin
5+
from node.core.exceptions import ValidationError
6+
from node.core.utils.cryptography import derive_public_key
7+
from node.core.utils.types import non_negative_int
8+
9+
from ..types import AccountNumber, Hash, NodeRole, Signature
10+
from .base import BaseModel
11+
12+
13+
class BlockConfirmationMessage(BaseModel, SignableMixin):
14+
number: non_negative_int
15+
hash: Hash # noqa: A003
16+
17+
18+
class BlockConfirmation(ValidatableMixin, BaseModel):
19+
signer: AccountNumber
20+
signature: Signature
21+
message: BlockConfirmationMessage
22+
23+
@classmethod
24+
def create(cls, number, hash_, signing_key):
25+
message = BlockConfirmationMessage(number=number, hash=hash_)
26+
signer = derive_public_key(signing_key)
27+
signature = message.make_signature(signing_key)
28+
29+
return cls(signer=signer, signature=signature, message=message)
30+
31+
@root_validator
32+
def validate_signature(cls, values):
33+
return validate_signature_helper(values)
34+
35+
def get_number(self):
36+
return self.message.number
37+
38+
def get_hash(self):
39+
return self.message.hash
40+
41+
def validate_signer(self, blockchain_facade):
42+
if not blockchain_facade.has_blocks():
43+
return
44+
45+
cv_identifiers = (node.identifier for node in blockchain_facade.yield_nodes({NodeRole.CONFIRMATION_VALIDATOR}))
46+
if self.signer not in cv_identifiers:
47+
raise ValidationError('Invalid block signer')
48+
49+
def validate_business_logic(self):
50+
pass
51+
52+
def validate_blockchain_state_dependent(self, blockchain_facade):
53+
self.validate_signer(blockchain_facade)

node/blockchain/inner_models/signed_change_request/base.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,7 @@ def validate_signature(cls, values):
5555
if cls == SignedChangeRequest: # only child classes signature validation makes sense
5656
return values
5757

58-
validate_signature_helper(values)
59-
return values
58+
return validate_signature_helper(values)
6059

6160
def validate_business_logic(self):
6261
self.message.validate_business_logic()

node/blockchain/mixins/crypto.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ def validate_signature_helper(values: dict):
1919
# TODO(dmu) LOW: Pydantic does not recognize custom ValidationError. Fix?
2020
raise ValueError('Invalid signature')
2121

22+
return values
23+
2224

2325
class CryptoAuxiliaryMixin:
2426

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
class ValidatableMixin:
22

3-
def validate_business_logic(self): # validate() is used by pydantic
3+
def validate_business_logic(self): # validate() is used by pydantic there we use a longer name
44
raise NotImplementedError('Must be implemented in child class')
55

66
def validate_blockchain_state_dependent(self, blockchain_facade):
77
raise NotImplementedError('Must be implemented in child class')
8+
9+
def validate_all(self, blockchain_facade):
10+
self.validate_business_logic()
11+
self.validate_blockchain_state_dependent(blockchain_facade)

node/blockchain/models/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from .account_state import AccountState # noqa: F401
22
from .block import Block # noqa: F401
3+
from .block_confirmation import BlockConfirmation # noqa: F401
34
from .node import Node # noqa: F401
45
from .pending_block import PendingBlock # noqa: F401
56
from .schedule import Schedule # noqa: F401
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import uuid
2+
3+
from djongo import models
4+
5+
from node.core.models import CustomModel
6+
7+
8+
class BlockConfirmation(CustomModel):
9+
10+
_id = models.UUIDField(primary_key=True, default=uuid.uuid4) # noqa: A003
11+
number = models.PositiveBigIntegerField()
12+
hash = models.CharField(max_length=128) # noqa: A003
13+
signer = models.CharField(max_length=64) # noqa: A003
14+
body = models.BinaryField()
15+
16+
class Meta:
17+
unique_together = ('number', 'hash', 'signer')
18+
ordering = unique_together
19+
20+
def __str__(self):
21+
return f'block_number={self.number}, hash={self.hash}, signer={self.signer}'
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from rest_framework import serializers
2+
3+
from node.blockchain.inner_models import BlockConfirmation
4+
from node.core.fields import PydanticModelBackedJSONField
5+
from node.core.serializers import ValidateUnknownFieldsMixin
6+
7+
8+
class BlockConfirmationSerializer(serializers.Serializer, ValidateUnknownFieldsMixin):
9+
signer = serializers.CharField()
10+
signature = serializers.CharField()
11+
message = PydanticModelBackedJSONField()
12+
13+
def create(self, validated_data):
14+
return BlockConfirmation.parse_obj(validated_data)

0 commit comments

Comments
 (0)