4
4
from crypto .bls .bls_bft import BlsBft
5
5
from crypto .bls .bls_bft_replica import BlsBftReplica
6
6
from crypto .bls .bls_multi_signature import MultiSignature , MultiSignatureValue
7
- from plenum .common .constants import DOMAIN_LEDGER_ID , BLS_PREFIX , POOL_LEDGER_ID
7
+ from plenum .common .constants import DOMAIN_LEDGER_ID , BLS_PREFIX , POOL_LEDGER_ID , AUDIT_LEDGER_ID , TXN_PAYLOAD , \
8
+ TXN_PAYLOAD_DATA , AUDIT_TXN_LEDGER_ROOT , AUDIT_TXN_STATE_ROOT , AUDIT_TXN_PP_SEQ_NO
8
9
from plenum .common .messages .node_messages import PrePrepare , Prepare , Commit
9
10
from plenum .common .metrics_collector import MetricsCollector , NullMetricsCollector , measure_time , MetricsName
10
11
from plenum .common .types import f
11
12
from plenum .common .util import compare_3PC_keys
13
+ from plenum .server .database_manager import DatabaseManager
12
14
from stp_core .common .log import getlogger
13
15
14
16
logger = getlogger ()
@@ -19,10 +21,14 @@ def __init__(self,
19
21
node_id ,
20
22
bls_bft : BlsBft ,
21
23
is_master ,
24
+ database_manager : DatabaseManager ,
22
25
metrics : MetricsCollector = NullMetricsCollector ()):
23
26
super ().__init__ (bls_bft , is_master )
27
+ self ._all_bls_latest_multi_sigs = None
24
28
self .node_id = node_id
29
+ self ._database_manager = database_manager
25
30
self ._signatures = {}
31
+ self ._all_signatures = {}
26
32
self ._bls_latest_multi_sig = None # MultiSignature
27
33
self .state_root_serializer = state_roots_serializer
28
34
self .metrics = metrics
@@ -35,6 +41,13 @@ def _can_process_ledger(self, ledger_id):
35
41
36
42
@measure_time (MetricsName .BLS_VALIDATE_PREPREPARE_TIME )
37
43
def validate_pre_prepare (self , pre_prepare : PrePrepare , sender ):
44
+ if f .BLS_MULTI_SIGS .nm in pre_prepare and pre_prepare .blsMultiSigs :
45
+ multi_sigs = pre_prepare .blsMultiSigs
46
+ for sig in multi_sigs :
47
+ multi_sig = MultiSignature .from_list (* sig )
48
+ if not self ._validate_multi_sig (multi_sig ):
49
+ return BlsBftReplica .PPR_BLS_MULTISIG_WRONG
50
+
38
51
if f .BLS_MULTI_SIG .nm not in pre_prepare or \
39
52
pre_prepare .blsMultiSig is None :
40
53
return
@@ -48,6 +61,22 @@ def validate_prepare(self, prepare: Prepare, sender):
48
61
49
62
@measure_time (MetricsName .BLS_VALIDATE_COMMIT_TIME )
50
63
def validate_commit (self , commit : Commit , sender , pre_prepare : PrePrepare ):
64
+ if f .BLS_SIGS .nm in commit :
65
+ audit_txn = self ._get_correct_audit_transaction (pre_prepare )
66
+ if audit_txn :
67
+ audit_payload = audit_txn [TXN_PAYLOAD ][TXN_PAYLOAD_DATA ]
68
+ for lid , sig in commit .blsSigs .items ():
69
+ lid = int (lid )
70
+ if lid not in audit_payload [AUDIT_TXN_STATE_ROOT ] or lid not in audit_payload [AUDIT_TXN_LEDGER_ROOT ]:
71
+ return BlsBftReplicaPlenum .CM_BLS_SIG_WRONG
72
+ if not self ._validate_signature (sender , sig ,
73
+ BlsBftReplicaPlenum ._create_fake_pre_prepare_for_multi_sig (
74
+ lid ,
75
+ audit_payload [AUDIT_TXN_STATE_ROOT ][lid ],
76
+ audit_payload [AUDIT_TXN_LEDGER_ROOT ][lid ],
77
+ pre_prepare
78
+ )):
79
+ return BlsBftReplicaPlenum .CM_BLS_SIG_WRONG
51
80
if f .BLS_SIG .nm not in commit :
52
81
# TODO: It's optional for now
53
82
return
@@ -62,12 +91,14 @@ def update_pre_prepare(self, pre_prepare_params, ledger_id):
62
91
if not self ._can_process_ledger (ledger_id ):
63
92
return pre_prepare_params
64
93
65
- if not self ._bls_latest_multi_sig :
66
- return pre_prepare_params
94
+ if self ._bls_latest_multi_sig is not None :
95
+ pre_prepare_params .append (self ._bls_latest_multi_sig .as_list ())
96
+ self ._bls_latest_multi_sig = None
67
97
68
- pre_prepare_params .append (self ._bls_latest_multi_sig .as_list ())
69
- self ._bls_latest_multi_sig = None
70
98
# Send signature in COMMITs only
99
+ if self ._all_bls_latest_multi_sigs is not None :
100
+ pre_prepare_params .append ([val .as_list () for val in self ._all_bls_latest_multi_sigs ])
101
+ self ._all_bls_latest_multi_sigs = None
71
102
72
103
return pre_prepare_params
73
104
@@ -92,6 +123,22 @@ def update_commit(self, commit_params, pre_prepare: PrePrepare):
92
123
logger .debug ("{}{} signed COMMIT {} for state {} with sig {}"
93
124
.format (BLS_PREFIX , self , commit_params , state_root_hash , bls_signature ))
94
125
commit_params .append (bls_signature )
126
+
127
+ last_audit_txn = self ._get_correct_audit_transaction (pre_prepare )
128
+ if last_audit_txn :
129
+ res = {}
130
+ payload_data = last_audit_txn [TXN_PAYLOAD ][TXN_PAYLOAD_DATA ]
131
+ for ledger_id in payload_data [AUDIT_TXN_STATE_ROOT ].keys ():
132
+ fake_pp = BlsBftReplicaPlenum ._create_fake_pre_prepare_for_multi_sig (
133
+ ledger_id ,
134
+ payload_data [AUDIT_TXN_STATE_ROOT ].get (ledger_id ),
135
+ payload_data [AUDIT_TXN_LEDGER_ROOT ].get (ledger_id ),
136
+ pre_prepare
137
+ )
138
+ bls_signature = self ._sign_state (fake_pp )
139
+ res [str (ledger_id )] = bls_signature
140
+ commit_params .append (res )
141
+
95
142
return commit_params
96
143
97
144
# ----PROCESS----
@@ -105,15 +152,19 @@ def process_prepare(self, prepare: Prepare, sender):
105
152
pass
106
153
107
154
def process_commit (self , commit : Commit , sender ):
108
- if f .BLS_SIG .nm not in commit :
109
- return
110
- if commit .blsSig is None :
111
- return
112
-
113
155
key_3PC = (commit .viewNo , commit .ppSeqNo )
114
- if key_3PC not in self ._signatures :
115
- self ._signatures [key_3PC ] = {}
116
- self ._signatures [key_3PC ][self .get_node_name (sender )] = commit .blsSig
156
+ if f .BLS_SIG .nm in commit and commit .blsSig is not None :
157
+ if key_3PC not in self ._signatures :
158
+ self ._signatures [key_3PC ] = {}
159
+ self ._signatures [key_3PC ][self .get_node_name (sender )] = commit .blsSig
160
+
161
+ if f .BLS_SIGS .nm in commit and commit .blsSigs is not None :
162
+ if key_3PC not in self ._all_signatures :
163
+ self ._all_signatures [key_3PC ] = {}
164
+ for ledger_id in commit .blsSigs .keys ():
165
+ if ledger_id not in self ._all_signatures [key_3PC ]:
166
+ self ._all_signatures [key_3PC ][ledger_id ] = {}
167
+ self ._all_signatures [key_3PC ][ledger_id ][self .get_node_name (sender )] = commit .blsSigs [ledger_id ]
117
168
118
169
def process_order (self , key , quorums , pre_prepare ):
119
170
if not self ._can_process_ledger (pre_prepare .ledgerId ):
@@ -126,12 +177,19 @@ def process_order(self, key, quorums, pre_prepare):
126
177
# but save on master only
127
178
bls_multi_sig = self ._calculate_multi_sig (key , pre_prepare )
128
179
180
+ all_bls_multi_sigs = self ._calculate_all_multi_sigs (key , pre_prepare )
181
+
129
182
if not self ._is_master :
130
183
return
131
184
132
- self ._save_multi_sig_local (bls_multi_sig )
185
+ if all_bls_multi_sigs :
186
+ for bls_multi_sig in all_bls_multi_sigs :
187
+ self ._save_multi_sig_local (bls_multi_sig )
188
+ elif bls_multi_sig :
189
+ self ._save_multi_sig_local (bls_multi_sig )
133
190
134
191
self ._bls_latest_multi_sig = bls_multi_sig
192
+ self ._all_bls_latest_multi_sigs = all_bls_multi_sigs
135
193
136
194
# ----GC----
137
195
@@ -142,6 +200,7 @@ def gc(self, key_3PC):
142
200
keys_to_remove .append (key )
143
201
for key in keys_to_remove :
144
202
self ._signatures .pop (key , None )
203
+ self ._all_signatures .pop (key , None )
145
204
146
205
# ----MULT_SIG----
147
206
@@ -154,7 +213,7 @@ def _create_multi_sig_value_for_pre_prepare(self, pre_prepare: PrePrepare, pool_
154
213
return multi_sig_value
155
214
156
215
def validate_key_proof_of_possession (self , key_proof , pk ):
157
- return self ._bls_bft .bls_crypto_verifier \
216
+ return self ._bls_bft .bls_crypto_verifier \
158
217
.verify_key_proof_of_possession (key_proof , pk )
159
218
160
219
def _validate_signature (self , sender , bls_sig , pre_prepare : PrePrepare ):
@@ -198,6 +257,28 @@ def _sign_state(self, pre_prepare: PrePrepare):
198
257
def _can_calculate_multi_sig (self ,
199
258
key_3PC ,
200
259
quorums ) -> bool :
260
+ if key_3PC in self ._all_signatures :
261
+ sigs_for_request = self ._all_signatures [key_3PC ]
262
+ sigs_invalid = list (
263
+ filter (
264
+ lambda item : not quorums .bls_signatures .is_reached (len (list (item [1 ].values ()))),
265
+ sigs_for_request .items ()
266
+ )
267
+ )
268
+ if sigs_invalid :
269
+ for lid , sigs in sigs_invalid :
270
+ logger .debug (
271
+ '{}Can not create bls signatures for batch {}: '
272
+ 'There are only {} signatures for ledger {}, '
273
+ 'while {} required for multi_signature' .format (BLS_PREFIX ,
274
+ key_3PC ,
275
+ len (list (sigs .values ())),
276
+ quorums .bls_signatures .value ,
277
+ lid )
278
+ )
279
+ else :
280
+ return True
281
+
201
282
if key_3PC not in self ._signatures :
202
283
return False
203
284
@@ -216,6 +297,26 @@ def _can_calculate_multi_sig(self,
216
297
217
298
def _calculate_multi_sig (self , key_3PC , pre_prepare ) -> Optional [MultiSignature ]:
218
299
sigs_for_request = self ._signatures [key_3PC ]
300
+ return self ._calculate_single_multi_sig (sigs_for_request , pre_prepare )
301
+
302
+ def _calculate_all_multi_sigs (self , key_3PC , pre_prepare ) -> Optional [list ]:
303
+ sigs_for_request = self ._all_signatures .get (key_3PC )
304
+ res = []
305
+ if sigs_for_request :
306
+ for lid in sigs_for_request :
307
+ sig = sigs_for_request [lid ]
308
+ audit_txn = self ._get_correct_audit_transaction (pre_prepare )
309
+ if audit_txn :
310
+ audit_payload = audit_txn [TXN_PAYLOAD ][TXN_PAYLOAD_DATA ]
311
+ fake_pp = BlsBftReplicaPlenum . \
312
+ _create_fake_pre_prepare_for_multi_sig (int (lid ),
313
+ audit_payload [AUDIT_TXN_STATE_ROOT ][int (lid )],
314
+ audit_payload [AUDIT_TXN_LEDGER_ROOT ][int (lid )],
315
+ pre_prepare )
316
+ res .append (self ._calculate_single_multi_sig (sig , fake_pp ))
317
+ return res
318
+
319
+ def _calculate_single_multi_sig (self , sigs_for_request , pre_prepare ) -> Optional [MultiSignature ]:
219
320
bls_signatures = list (sigs_for_request .values ())
220
321
participants = list (sigs_for_request .keys ())
221
322
@@ -245,7 +346,16 @@ def _save_multi_sig_local(self,
245
346
246
347
def _save_multi_sig_shared (self , pre_prepare : PrePrepare ):
247
348
248
- if f .BLS_MULTI_SIG .nm not in pre_prepare :
349
+ if f .BLS_MULTI_SIGS .nm in pre_prepare and pre_prepare .blsMultiSigs is not None :
350
+ multi_sigs = pre_prepare .blsMultiSigs
351
+ for sig in multi_sigs :
352
+ multi_sig = MultiSignature .from_list (* sig )
353
+ self ._bls_bft .bls_store .put (multi_sig )
354
+ logger .debug ("{}{} saved multi signature {} for root {} (calculated by Primary)"
355
+ .format (BLS_PREFIX , self , multi_sig ,
356
+ multi_sig .value .state_root_hash ))
357
+ return
358
+ elif f .BLS_MULTI_SIG .nm not in pre_prepare :
249
359
return
250
360
if pre_prepare .blsMultiSig is None :
251
361
return
@@ -257,6 +367,41 @@ def _save_multi_sig_shared(self, pre_prepare: PrePrepare):
257
367
multi_sig .value .state_root_hash ))
258
368
# TODO: support multiple multi-sigs for multiple previous batches
259
369
370
+ def _get_correct_audit_transaction (self , pp : PrePrepare ):
371
+ ledger = self ._database_manager .get_ledger (AUDIT_LEDGER_ID )
372
+ if ledger is None :
373
+ return None
374
+ seqNo = ledger .uncommitted_size
375
+ for curSeqNo in reversed (range (1 , seqNo + 1 )):
376
+ txn = ledger .get_by_seq_no_uncommitted (curSeqNo )
377
+ if txn :
378
+ payload = txn [TXN_PAYLOAD ][TXN_PAYLOAD_DATA ]
379
+ if pp .ppSeqNo == payload [AUDIT_TXN_PP_SEQ_NO ]:
380
+ return txn
381
+ return None
382
+
383
+ @staticmethod
384
+ def _create_fake_pre_prepare_for_multi_sig (lid , state_root_hash , txn_root_hash , pre_prepare ):
385
+ params = [
386
+ pre_prepare .instId ,
387
+ pre_prepare .viewNo ,
388
+ pre_prepare .ppSeqNo ,
389
+ pre_prepare .ppTime ,
390
+ pre_prepare .reqIdr ,
391
+ pre_prepare .discarded ,
392
+ pre_prepare .digest ,
393
+ 1 , # doing it to work around the ledgers that are not in plenum -- it will fail the validation of pre-prepare
394
+ state_root_hash ,
395
+ txn_root_hash ,
396
+ pre_prepare .sub_seq_no ,
397
+ pre_prepare .final ,
398
+ pre_prepare .poolStateRootHash ,
399
+ pre_prepare .auditTxnRootHash
400
+ ]
401
+ pp = PrePrepare (* params )
402
+ pp .ledgerId = lid
403
+ return pp
404
+
260
405
@staticmethod
261
406
def get_node_name (replica_name : str ):
262
407
# TODO: there is the same method in Replica
0 commit comments