@@ -41,17 +41,20 @@ use vm::database::NULL_HEADER_DB;
4141
4242use vm:: costs:: ExecutionCost ;
4343
44+ pub type UnconfirmedTxMap = HashMap < Txid , ( StacksTransaction , BlockHeaderHash , u16 ) > ;
45+
4446pub struct UnconfirmedState {
4547 pub confirmed_chain_tip : StacksBlockId ,
4648 pub unconfirmed_chain_tip : StacksBlockId ,
4749 pub clarity_inst : ClarityInstance ,
48- pub mined_txs : HashMap < Txid , ( StacksTransaction , BlockHeaderHash , u16 ) > ,
50+ pub mined_txs : UnconfirmedTxMap ,
4951 pub cost_so_far : ExecutionCost ,
5052 pub bytes_so_far : u64 ,
5153
5254 pub last_mblock : Option < StacksMicroblockHeader > ,
5355 pub last_mblock_seq : u16 ,
5456
57+ readonly : bool ,
5558 dirty : bool ,
5659}
5760
@@ -68,13 +71,40 @@ impl UnconfirmedState {
6871 confirmed_chain_tip : tip,
6972 unconfirmed_chain_tip : unconfirmed_tip,
7073 clarity_inst : clarity_instance,
71- mined_txs : HashMap :: new ( ) ,
74+ mined_txs : UnconfirmedTxMap :: new ( ) ,
75+ cost_so_far : ExecutionCost :: zero ( ) ,
76+ bytes_so_far : 0 ,
77+
78+ last_mblock : None ,
79+ last_mblock_seq : 0 ,
80+
81+ readonly : false ,
82+ dirty : false ,
83+ } )
84+ }
85+
86+ /// Make a new unconfirmed state, but don't do anything with it yet, and deny refreshes.
87+ fn new_readonly (
88+ chainstate : & StacksChainState ,
89+ tip : StacksBlockId ,
90+ ) -> Result < UnconfirmedState , Error > {
91+ let marf = MarfedKV :: open_unconfirmed ( & chainstate. clarity_state_index_root , None ) ?;
92+
93+ let clarity_instance = ClarityInstance :: new ( marf, chainstate. block_limit . clone ( ) ) ;
94+ let unconfirmed_tip = MARF :: make_unconfirmed_chain_tip ( & tip) ;
95+
96+ Ok ( UnconfirmedState {
97+ confirmed_chain_tip : tip,
98+ unconfirmed_chain_tip : unconfirmed_tip,
99+ clarity_inst : clarity_instance,
100+ mined_txs : UnconfirmedTxMap :: new ( ) ,
72101 cost_so_far : ExecutionCost :: zero ( ) ,
73102 bytes_so_far : 0 ,
74103
75104 last_mblock : None ,
76105 last_mblock_seq : 0 ,
77106
107+ readonly : true ,
78108 dirty : false ,
79109 } )
80110 }
@@ -91,7 +121,7 @@ impl UnconfirmedState {
91121 mblocks : Vec < StacksMicroblock > ,
92122 ) -> Result < ( u128 , u128 , Vec < StacksTransactionReceipt > ) , Error > {
93123 if self . last_mblock_seq == u16:: max_value ( ) {
94- // drop them
124+ // drop them -- nothing to do
95125 return Ok ( ( 0 , 0 , vec ! [ ] ) ) ;
96126 }
97127
@@ -108,7 +138,7 @@ impl UnconfirmedState {
108138 let mut total_fees = 0 ;
109139 let mut total_burns = 0 ;
110140 let mut all_receipts = vec ! [ ] ;
111- let mut mined_txs = HashMap :: new ( ) ;
141+ let mut mined_txs = UnconfirmedTxMap :: new ( ) ;
112142 let new_cost;
113143 let mut new_bytes = 0 ;
114144
@@ -195,7 +225,7 @@ impl UnconfirmedState {
195225 Ok ( ( total_fees, total_burns, all_receipts) )
196226 }
197227
198- /// Load up Stacks microblock stream to process
228+ /// Load up the Stacks microblock stream to process, composed of only the new microblocks
199229 fn load_child_microblocks (
200230 & self ,
201231 chainstate : & StacksChainState ,
@@ -222,6 +252,11 @@ impl UnconfirmedState {
222252 chainstate : & StacksChainState ,
223253 burn_dbconn : & dyn BurnStateDB ,
224254 ) -> Result < ( u128 , u128 , Vec < StacksTransactionReceipt > ) , Error > {
255+ assert ! (
256+ !self . readonly,
257+ "BUG: code tried to write unconfirmed state to a read-only instance"
258+ ) ;
259+
225260 if self . last_mblock_seq == u16:: max_value ( ) {
226261 // no-op
227262 return Ok ( ( 0 , 0 , vec ! [ ] ) ) ;
@@ -235,7 +270,7 @@ impl UnconfirmedState {
235270
236271 /// Is there any state to read?
237272 pub fn is_readable ( & self ) -> bool {
238- self . has_data ( ) && !self . dirty
273+ ( self . has_data ( ) || self . readonly ) && !self . dirty
239274 }
240275
241276 /// Can we write to this unconfirmed state?
@@ -395,6 +430,25 @@ impl StacksChainState {
395430 res
396431 }
397432
433+ /// Instantiate a read-only view of unconfirmed state.
434+ /// Use from a dedicated chainstate handle that will only do read-only operations on it (such
435+ /// as the p2p network thread)
436+ pub fn refresh_unconfirmed_readonly (
437+ & mut self ,
438+ canonical_tip : StacksBlockId ,
439+ ) -> Result < ( ) , Error > {
440+ if let Some ( ref unconfirmed) = self . unconfirmed_state {
441+ assert ! (
442+ unconfirmed. readonly,
443+ "BUG: tried to replace a read/write unconfirmed state instance"
444+ ) ;
445+ }
446+
447+ let unconfirmed = UnconfirmedState :: new_readonly ( self , canonical_tip) ?;
448+ self . unconfirmed_state = Some ( unconfirmed) ;
449+ Ok ( ( ) )
450+ }
451+
398452 pub fn set_unconfirmed_dirty ( & mut self , dirty : bool ) {
399453 if let Some ( ref mut unconfirmed) = self . unconfirmed_state . as_mut ( ) {
400454 unconfirmed. dirty = dirty;
@@ -632,6 +686,7 @@ mod test {
632686 clarity_db. get_account_stx_balance ( & recv_addr. into ( ) )
633687 } )
634688 } )
689+ . unwrap ( )
635690 . unwrap ( ) ;
636691 peer. sortdb = Some ( sortdb) ;
637692
@@ -862,6 +917,7 @@ mod test {
862917 clarity_db. get_account_stx_balance ( & recv_addr. into ( ) )
863918 } )
864919 } )
920+ . unwrap ( )
865921 . unwrap ( ) ;
866922 peer. sortdb = Some ( sortdb) ;
867923
0 commit comments