8
8
9
9
from node .blockchain .constants import BLOCK_LOCK
10
10
from node .blockchain .facade import BlockchainFacade
11
- from node .blockchain .inner_models import BlockConfirmation as PydanticBlockConfirmation
12
11
from node .blockchain .mixins .crypto import HashableStringWrapper
13
12
from node .blockchain .models import BlockConfirmation , PendingBlock
14
13
from node .blockchain .types import Hash
18
17
logger = logging .getLogger (__name__ )
19
18
20
19
21
- def get_consensus_block_hash_with_confirmations (
22
- facade , next_block_number , minimum_consensus
23
- ) -> Optional [tuple [Hash , list [PydanticBlockConfirmation ]]]:
20
+ def get_next_block_confirmations (next_block_number ) -> list [BlockConfirmation ]:
21
+ facade = BlockchainFacade .get_instance ()
24
22
cv_identifiers = facade .get_confirmation_validator_identifiers ()
23
+ return list (BlockConfirmation .objects .filter (number = next_block_number , signer__in = cv_identifiers ))
25
24
26
- # Query only confirmations for the next block number and received from confirmation validators
27
- all_confirmations = BlockConfirmation .objects .filter (number = next_block_number , signer__in = cv_identifiers )
28
25
29
- # Group confirmations by hash to see which hash wins the consensus
30
- grouped_confirmations = groupby (all_confirmations .order_by ('hash' ), key = attrgetter ('hash' ))
26
+ def get_consensus_block_hash_with_confirmations (confirmations ,
27
+ minimum_consensus ) -> Optional [tuple [Hash , list [BlockConfirmation ]]]:
28
+ key_func = attrgetter ('hash' )
29
+ grouped_confirmations = [(Hash (hash_ ), list (confirmations ))
30
+ for hash_ , confirmations in groupby (sorted (confirmations , key = key_func ), key = key_func )]
31
31
finalizable_hashes = [(hash_ , confirmations )
32
32
for hash_ , confirmations in grouped_confirmations
33
- if len (list ( confirmations ) ) >= minimum_consensus ]
33
+ if len (confirmations ) >= minimum_consensus ]
34
34
35
35
if not finalizable_hashes :
36
36
return None # No consensus, yet
@@ -40,36 +40,43 @@ def get_consensus_block_hash_with_confirmations(
40
40
41
41
assert len (finalizable_hashes ) == 1
42
42
block_hash , consensus_confirmations = finalizable_hashes [0 ]
43
- return block_hash , [confirmation .get_block_confirmation () for confirmation in consensus_confirmations ]
43
+ assert len (set (confirmation .signer for confirmation in consensus_confirmations )) == len (consensus_confirmations )
44
+ return block_hash , consensus_confirmations
44
45
45
46
46
- def is_valid_consensus (facade , confirmations , minimum_consensus ):
47
+ def is_valid_consensus (confirmations : list [ BlockConfirmation ] , minimum_consensus ):
47
48
# Validate confirmations, since they may have not been validated on API call because some of them were added
48
49
# much earlier then the next block number become equal to confirmation block number
49
- valid_confirmations = []
50
+ assert len (set (confirmation .signer for confirmation in confirmations )) == len (confirmations )
51
+ facade = BlockchainFacade .get_instance ()
52
+
53
+ confirmations_left = minimum_consensus
50
54
for confirmation in confirmations :
51
55
try :
52
- confirmation .validate_all (facade )
56
+ confirmation .get_block_confirmation (). validate_all (facade )
53
57
except ValidationError :
54
58
logger .warning ('Invalid confirmation detected: %s' , confirmation )
55
59
continue
56
60
57
- valid_confirmations .append (confirmation )
61
+ confirmations_left -= 1
62
+ if confirmations_left <= 0 :
63
+ return True
58
64
59
- return len ( valid_confirmations ) >= minimum_consensus
65
+ return False
60
66
61
67
62
68
@lock (BLOCK_LOCK )
63
69
def process_next_block ():
64
70
facade = BlockchainFacade .get_instance ()
65
71
next_block_number = facade .get_next_block_number ()
66
- minimum_consensus = facade . get_minimum_consensus ( )
72
+ confirmations = get_next_block_confirmations ( next_block_number )
67
73
68
- if not (result := get_consensus_block_hash_with_confirmations (facade , next_block_number , minimum_consensus )):
74
+ minimum_consensus = facade .get_minimum_consensus ()
75
+ if not (result := get_consensus_block_hash_with_confirmations (confirmations , minimum_consensus )):
69
76
return False
70
77
71
78
block_hash , confirmations = result
72
- if not is_valid_consensus (facade , confirmations , minimum_consensus ):
79
+ if not is_valid_consensus (confirmations , minimum_consensus ):
73
80
return False
74
81
75
82
pending_block = PendingBlock .objects .get_or_none (number = next_block_number , hash = block_hash )
0 commit comments