1+ use std:: collections:: HashMap ;
2+
13use current_aleph_bft:: NodeCount ;
24use futures:: {
35 channel:: { mpsc, oneshot} ,
46 StreamExt ,
57} ;
68use log:: { debug, error, warn} ;
9+ use parity_scale_codec:: Encode ;
10+ use sp_runtime:: traits:: Hash as _;
711
812use crate :: {
913 abft:: {
1014 current:: performance:: { scorer:: Scorer , Batch } ,
1115 LOG_TARGET ,
1216 } ,
17+ aleph_primitives:: {
18+ crypto:: SignatureSet , AuthoritySignature , Hash , Hashing , RawScore , Score , ScoreNonce ,
19+ } ,
1320 data_io:: AlephData ,
1421 metrics:: ScoreMetrics ,
1522 party:: manager:: Runnable ,
16- Hasher , UnverifiedHeader ,
23+ runtime_api:: RuntimeApi ,
24+ Hasher , SessionId , UnverifiedHeader ,
1725} ;
1826
27+ const SCORE_SUBMISSION_PERIOD : usize = 300 ;
28+
1929struct FinalizationWrapper < UH , FH >
2030where
2131 UH : UnverifiedHeader ,
@@ -59,26 +69,41 @@ where
5969}
6070
6171/// A service computing the performance score of ABFT nodes based on batches of ordered units.
62- pub struct Service < UH >
72+ pub struct Service < UH , RA >
6373where
6474 UH : UnverifiedHeader ,
75+ RA : RuntimeApi ,
6576{
6677 my_index : usize ,
78+ session_id : SessionId ,
6779 batches_from_abft : mpsc:: UnboundedReceiver < Batch < UH > > ,
80+ hashes_for_aggregator : mpsc:: UnboundedSender < Hash > ,
81+ signatures_from_aggregator : mpsc:: UnboundedReceiver < ( Hash , SignatureSet < AuthoritySignature > ) > ,
82+ runtime_api : RA ,
83+ pending_scores : HashMap < Hash , Score > ,
84+ nonce : ScoreNonce ,
6885 scorer : Scorer ,
6986 metrics : ScoreMetrics ,
7087}
7188
72- impl < UH > Service < UH >
89+ impl < UH , RA > Service < UH , RA >
7390where
7491 UH : UnverifiedHeader ,
92+ RA : RuntimeApi ,
7593{
7694 /// Create a new service, together with a unit finalization handler that should be passed to
7795 /// ABFT. It will wrap the provided finalization handler and call it in the background.
7896 pub fn new < FH > (
7997 my_index : usize ,
8098 n_members : usize ,
99+ session_id : SessionId ,
81100 finalization_handler : FH ,
101+ hashes_for_aggregator : mpsc:: UnboundedSender < Hash > ,
102+ signatures_from_aggregator : mpsc:: UnboundedReceiver < (
103+ Hash ,
104+ SignatureSet < AuthoritySignature > ,
105+ ) > ,
106+ runtime_api : RA ,
82107 metrics : ScoreMetrics ,
83108 ) -> (
84109 Self ,
@@ -91,34 +116,81 @@ where
91116 (
92117 Service {
93118 my_index,
119+ session_id,
94120 batches_from_abft,
121+ hashes_for_aggregator,
122+ signatures_from_aggregator,
123+ runtime_api,
124+ pending_scores : HashMap :: new ( ) ,
125+ nonce : 0 ,
95126 scorer : Scorer :: new ( NodeCount ( n_members) ) ,
96127 metrics,
97128 } ,
98129 FinalizationWrapper :: new ( finalization_handler, batches_for_us) ,
99130 )
100131 }
132+
133+ fn make_score ( & mut self , points : RawScore ) -> Score {
134+ let result = Score {
135+ session_id : self . session_id . 0 ,
136+ nonce : self . nonce ,
137+ points,
138+ } ;
139+ self . nonce += 1 ;
140+ result
141+ }
101142}
102143
103144#[ async_trait:: async_trait]
104- impl < UH > Runnable for Service < UH >
145+ impl < UH , RA > Runnable for Service < UH , RA >
105146where
106147 UH : UnverifiedHeader ,
148+ RA : RuntimeApi ,
107149{
108150 async fn run ( mut self , mut exit : oneshot:: Receiver < ( ) > ) {
151+ let mut batch_counter = 1 ;
109152 loop {
110153 tokio:: select! {
111154 maybe_batch = self . batches_from_abft. next( ) => {
112- let score = match maybe_batch {
155+ let points = match maybe_batch {
113156 Some ( batch) => self . scorer. process_batch( batch) ,
114157 None => {
115158 error!( target: LOG_TARGET , "Batches' channel closed, ABFT performance scoring terminating." ) ;
116159 break ;
117160 } ,
118161 } ;
119- debug!( target: LOG_TARGET , "Received ABFT score: {:?}." , score) ;
120- self . metrics. report_score( score[ self . my_index] ) ;
121- // TODO(A0-4339): sometimes submit these scores to the chain.
162+ self . metrics. report_score( points[ self . my_index] ) ;
163+ if batch_counter % SCORE_SUBMISSION_PERIOD == 0 {
164+ let score = self . make_score( points) ;
165+ let score_hash = Hashing :: hash_of( & score. encode( ) ) ;
166+ debug!( target: LOG_TARGET , "Gathering signature under ABFT score: {:?}." , score) ;
167+ self . pending_scores. insert( score_hash, score) ;
168+ if let Err ( e) = self . hashes_for_aggregator. unbounded_send( score_hash) {
169+ error!( target: LOG_TARGET , "Failed to send score hash to signature aggregation: {}." , e) ;
170+ break ;
171+ }
172+ }
173+ batch_counter += 1 ;
174+ }
175+ maybe_signed = self . signatures_from_aggregator. next( ) => {
176+ match maybe_signed {
177+ Some ( ( hash, signature) ) => {
178+ match self . pending_scores. remove( & hash) {
179+ Some ( score) => {
180+ if let Err ( e) = self . runtime_api. submit_abft_score( score, signature) {
181+ warn!( target: LOG_TARGET , "Failed to submit performance score to chain: {}." , e) ;
182+ }
183+ } ,
184+ None => {
185+ warn!( target: LOG_TARGET , "Received multisigned hash for unknown performance score, this shouldn't ever happen." ) ;
186+ } ,
187+ }
188+ } ,
189+ None => {
190+ error!( target: LOG_TARGET , "Signatures' channel closed, ABFT performance scoring terminating." ) ;
191+ break ;
192+ } ,
193+ }
122194 }
123195 _ = & mut exit => {
124196 debug!( target: LOG_TARGET , "ABFT performance scoring task received exit signal. Terminating." ) ;
0 commit comments