66mod sequencer_consensus_context_test;
77
88use std:: cmp:: max;
9- use std:: collections:: { BTreeMap , HashMap , VecDeque } ;
9+ use std:: collections:: { BTreeMap , HashMap } ;
1010use std:: sync:: { Arc , Mutex } ;
1111use std:: time:: Duration ;
1212
@@ -235,9 +235,8 @@ pub struct SequencerConsensusContext {
235235 l2_gas_price : GasPrice ,
236236 l1_da_mode : L1DataAvailabilityMode ,
237237 previous_proposal_init : Option < PreviousProposalInitInfo > ,
238- /// SNIP-35: sliding window of recent fee_proposal values (size from
239- /// `FEE_PROPOSAL_WINDOW_SIZE`).
240- fee_proposals_window : VecDeque < GasPrice > ,
238+ /// SNIP-35 fee_proposals window. `None` = pre-V0_14_3.
239+ fee_proposals_window : BTreeMap < BlockNumber , Option < GasPrice > > ,
241240}
242241
243242#[ derive( Clone ) ]
@@ -288,16 +287,22 @@ impl SequencerConsensusContext {
288287 l2_gas_price : VersionedConstants :: latest_constants ( ) . min_gas_price ,
289288 l1_da_mode,
290289 previous_proposal_init : None ,
291- fee_proposals_window : VecDeque :: with_capacity ( FEE_PROPOSAL_WINDOW_SIZE ) ,
290+ fee_proposals_window : BTreeMap :: new ( ) ,
292291 }
293292 }
294293
295- /// FIFO append into the SNIP-35 sliding window.
296- fn push_fee_proposal ( & mut self , fee_proposal : GasPrice ) {
297- if self . fee_proposals_window . len ( ) >= FEE_PROPOSAL_WINDOW_SIZE {
298- self . fee_proposals_window . pop_front ( ) ;
299- }
300- self . fee_proposals_window . push_back ( fee_proposal) ;
294+ fn record_fee_proposal ( & mut self , height : BlockNumber , fee_proposal_fri : Option < GasPrice > ) {
295+ self . fee_proposals_window . insert ( height, fee_proposal_fri) ;
296+ }
297+
298+ fn prune_fee_proposals_window ( & mut self , current_height : BlockNumber ) {
299+ let window_size =
300+ u64:: try_from ( FEE_PROPOSAL_WINDOW_SIZE ) . expect ( "FEE_PROPOSAL_WINDOW_SIZE fits in u64" ) ;
301+ let cutoff = BlockNumber ( current_height. 0 . saturating_sub ( window_size) ) ;
302+ // Per `BTreeMap::split_off` docs: "Splits the collection into two at the given key.
303+ // Returns everything after the given key, including the key." Reassigning the returned
304+ // half back keeps `[cutoff, ..)` and drops everything below.
305+ self . fee_proposals_window = self . fee_proposals_window . split_off ( & cutoff) ;
301306 }
302307
303308 /// SNIP-35: bootstrap the fee_proposals window from state_sync on startup so `fee_actual` is
@@ -315,11 +320,13 @@ impl SequencerConsensusContext {
315320 u64:: try_from ( FEE_PROPOSAL_WINDOW_SIZE ) . expect ( "FEE_PROPOSAL_WINDOW_SIZE fits in u64" ) ;
316321 let start = height. 0 . saturating_sub ( window_size) ;
317322 for h in start..height. 0 {
318- match self . deps . state_sync_client . get_block ( BlockNumber ( h) ) . await {
323+ let block_number = BlockNumber ( h) ;
324+ match self . deps . state_sync_client . get_block ( block_number) . await {
319325 Ok ( block) => {
320- if let Some ( fee_proposal) = block. block_header_without_hash . fee_proposal_fri {
321- self . push_fee_proposal ( fee_proposal) ;
322- }
326+ self . record_fee_proposal (
327+ block_number,
328+ block. block_header_without_hash . fee_proposal_fri ,
329+ ) ;
323330 }
324331 Err ( e) => {
325332 warn ! (
@@ -436,9 +443,7 @@ impl SequencerConsensusContext {
436443 let DecisionReachedResponse { state_diff, central_objects } = decision_reached_response;
437444
438445 self . update_l2_gas_price ( height, l2_gas_used) ;
439- if let Some ( fee_proposal) = init. fee_proposal_fri {
440- self . push_fee_proposal ( fee_proposal) ;
441- }
446+ self . record_fee_proposal ( height, init. fee_proposal_fri ) ;
442447
443448 // A hash map of (possibly failed) transactions, where the key is the transaction hash
444449 // and the value is the transaction itself.
@@ -891,9 +896,6 @@ impl ConsensusContext for SequencerConsensusContext {
891896 sync_block. block_header_without_hash . next_l2_gas_price ,
892897 VersionedConstants :: latest_constants ( ) . min_gas_price ,
893898 ) ;
894- if let Some ( fee_proposal) = sync_block. block_header_without_hash . fee_proposal_fri {
895- self . push_fee_proposal ( fee_proposal) ;
896- }
897899
898900 // TODO(Asmaa): validate starknet_version and parent_hash when they are stored.
899901 let block_number = sync_block. block_header_without_hash . block_number ;
@@ -916,6 +918,7 @@ impl ConsensusContext for SequencerConsensusContext {
916918 ) ;
917919 return false ;
918920 }
921+ self . record_fee_proposal ( height, sync_block. block_header_without_hash . fee_proposal_fri ) ;
919922 self . previous_proposal_init =
920923 Some ( previous_proposal_init_from_block_header ( & sync_block. block_header_without_hash ) ) ;
921924 self . interrupt_active_proposal ( ) . await ;
@@ -958,6 +961,7 @@ impl ConsensusContext for SequencerConsensusContext {
958961 }
959962 self . current_height = Some ( height) ;
960963 self . current_round = round;
964+ self . prune_fee_proposals_window ( height) ;
961965 self . queued_proposals . clear ( ) ;
962966 // The Batcher must be told when we begin to work on a new height. The implicit model is
963967 // that consensus works on a given height until it is done (either a decision is reached
0 commit comments