@@ -3,26 +3,25 @@ use anchor_lang::prelude::*;
33use crate :: { messages:: ExecutionReportSingleChain , CcipOfframpError , ExecutionReportBuffer } ;
44pub trait Buffering {
55 fn is_initialized ( & self ) -> bool ;
6- fn filled_chunks ( & self ) -> u32 ;
6+ fn filled_chunks ( & self ) -> u8 ;
77 fn is_complete ( & self ) -> bool ;
88 fn bytes ( & self ) -> Result < & [ u8 ] > ;
9- fn initialize ( & mut self , report_length : u32 , chunk_length : u32 ) -> Result < ( ) > ;
10- fn add_chunk ( & mut self , report_length : u32 , chunk : & [ u8 ] , chunk_index : u8 ) -> Result < ( ) > ;
11- fn recover_wrong_size (
9+ fn add_chunk (
1210 & mut self ,
1311 report_length : u32 ,
14- new_chunk : & [ u8 ] ,
12+ chunk : & [ u8 ] ,
1513 chunk_index : u8 ,
14+ num_chunks : u8 ,
1615 ) -> Result < ( ) > ;
1716}
1817
1918impl Buffering for ExecutionReportBuffer {
2019 fn is_initialized ( & self ) -> bool {
21- self . total_chunks > 0
20+ ! self . data . is_empty ( )
2221 }
2322
24- fn filled_chunks ( & self ) -> u32 {
25- self . chunk_bitmap . count_ones ( )
23+ fn filled_chunks ( & self ) -> u8 {
24+ self . chunk_bitmap . count_ones ( ) as u8
2625 }
2726
2827 fn is_complete ( & self ) -> bool {
@@ -38,35 +37,29 @@ impl Buffering for ExecutionReportBuffer {
3837 Ok ( & self . data )
3938 }
4039
41- fn initialize ( & mut self , report_length : u32 , chunk_length : u32 ) -> Result < ( ) > {
40+ fn add_chunk (
41+ & mut self ,
42+ report_length : u32 ,
43+ chunk : & [ u8 ] ,
44+ chunk_index : u8 ,
45+ num_chunks : u8 ,
46+ ) -> Result < ( ) > {
4247 require ! (
43- ! self . is_initialized ( ) ,
44- CcipOfframpError :: ExecutionReportBufferAlreadyInitialized
48+ num_chunks > 0 && chunk_index < num_chunks ,
49+ CcipOfframpError :: ExecutionReportBufferInvalidChunkIndex
4550 ) ;
4651 require ! (
47- chunk_length > 0 && report_length >= chunk_length,
48- CcipOfframpError :: ExecutionReportBufferInvalidLength
49- ) ;
50- self . data . resize ( report_length as usize , 0 ) ;
51- self . total_chunks = ( report_length + chunk_length - 1 ) / chunk_length;
52- require_gt ! (
53- 64 ,
54- self . total_chunks,
55- CcipOfframpError :: ExecutionReportBufferChunkSizeTooSmall
52+ !chunk. is_empty( ) && chunk. len( ) <= report_length as usize ,
53+ CcipOfframpError :: ExecutionReportBufferInvalidChunkSize
5654 ) ;
57- self . chunk_length = chunk_length;
58- self . report_length = report_length;
59- Ok ( ( ) )
60- }
6155
62- fn add_chunk ( & mut self , report_length : u32 , chunk : & [ u8 ] , chunk_index : u8 ) -> Result < ( ) > {
6356 if !self . is_initialized ( ) {
64- self . initialize ( report_length, chunk. len ( ) as u32 ) ?;
57+ self . initialize ( report_length, chunk. len ( ) as u32 , num_chunks , chunk_index ) ?;
6558 }
6659
6760 require_eq ! (
68- self . report_length ,
69- report_length,
61+ self . data . len ( ) ,
62+ report_length as usize ,
7063 CcipOfframpError :: ExecutionReportBufferInvalidLength
7164 ) ;
7265
@@ -76,13 +69,6 @@ impl Buffering for ExecutionReportBuffer {
7669 CcipOfframpError :: ExecutionReportBufferAlreadyContainsChunk
7770 ) ;
7871
79- if chunk. len ( ) as u32 > self . chunk_length {
80- // We hit the special case where the first received chunk was the last one
81- // in the buffer (terminator), which may be smaller than all others. It's okay,
82- // we can recover in place.
83- return self . recover_wrong_size ( report_length, chunk, chunk_index) ;
84- }
85-
8672 require_gte ! (
8773 self . chunk_length,
8874 chunk. len( ) as u32 ,
@@ -92,15 +78,15 @@ impl Buffering for ExecutionReportBuffer {
9278 if chunk. len ( ) < self . chunk_length as usize {
9379 // Only the terminator (last chunk) can be smaller than the others.
9480 require_eq ! (
95- chunk_index as u32 ,
81+ chunk_index,
9682 self . total_chunks - 1 ,
9783 CcipOfframpError :: ExecutionReportBufferInvalidChunkSize
9884 ) ;
9985 }
10086
10187 require_gt ! (
10288 self . total_chunks,
103- chunk_index as u32 ,
89+ chunk_index,
10490 CcipOfframpError :: ExecutionReportBufferInvalidChunkIndex
10591 ) ;
10692
@@ -111,37 +97,48 @@ impl Buffering for ExecutionReportBuffer {
11197
11298 Ok ( ( ) )
11399 }
100+ }
114101
115- fn recover_wrong_size (
102+ impl ExecutionReportBuffer {
103+ fn initialize (
116104 & mut self ,
117105 report_length : u32 ,
118- new_chunk : & [ u8 ] ,
106+ chunk_length : u32 ,
107+ total_chunks : u8 ,
119108 chunk_index : u8 ,
120109 ) -> Result < ( ) > {
121- // Only makes sense to recover if we got the first chunk wrong (because it was the buffer
122- // terminator). Any size mismatch beyond that means the user is sending the chunks incorrectly.
123- require_eq ! (
124- self . filled_chunks( ) ,
125- 1 ,
110+ require ! (
111+ !self . is_initialized( ) ,
112+ CcipOfframpError :: ExecutionReportBufferAlreadyInitialized
113+ ) ;
114+ require ! (
115+ report_length > 0 && chunk_length <= report_length && chunk_length > 0 ,
116+ CcipOfframpError :: ExecutionReportBufferInvalidLength
117+ ) ;
118+ require ! (
119+ total_chunks < 64 && total_chunks > 0 ,
126120 CcipOfframpError :: ExecutionReportBufferInvalidChunkSize
127121 ) ;
128122
129- // We extract what we now know is the terminator
130- let terminator_index = self . chunk_bitmap . trailing_zeros ( ) as u8 ;
131- let mut terminator = vec ! [ 0u8 ; self . chunk_length as usize ] ;
132- let start = terminator_index as usize * self . chunk_length as usize ;
133- let end = start + terminator. len ( ) ;
134- terminator. copy_from_slice ( & self . data [ start..end] ) ;
135-
136- // We reset the buffer metadata. It's okay to leave the old data in, as it will be clobbered.
137- self . chunk_bitmap = 0 ;
138- self . total_chunks = 0 ;
139- self . chunk_length = 0 ;
140-
141- // We reinsert the new chunk and terminator, which will be accepted as it's smaller. From now
142- // on, we won't accept bigger chunks than this again.
143- self . add_chunk ( report_length, new_chunk, chunk_index) ?;
144- self . add_chunk ( report_length, & terminator, terminator_index) ?;
123+ // If we're initializing with the last chunk, it could be smaller
124+ // than the rest, so we calculate the chunk size based on the expected
125+ // size of all the others.
126+ let is_last = chunk_index == total_chunks - 1 ;
127+ let global_chunk_length = if is_last && total_chunks > 1 {
128+ ( report_length - chunk_length) / ( total_chunks as u32 - 1 )
129+ } else {
130+ chunk_length
131+ } ;
132+
133+ require_eq ! (
134+ total_chunks as u32 ,
135+ div_ceil( report_length, global_chunk_length) ,
136+ CcipOfframpError :: ExecutionReportBufferInvalidLength ,
137+ ) ;
138+ self . chunk_length = global_chunk_length;
139+ self . total_chunks = total_chunks;
140+ self . data . resize ( report_length as usize , 0 ) ;
141+
145142 Ok ( ( ) )
146143 }
147144}
@@ -166,6 +163,12 @@ pub fn deserialize_from_buffer_account(
166163 ) )
167164}
168165
166+ fn div_ceil < T : Into < u32 > > ( a : T , b : T ) -> u32 {
167+ let ( a, b) = ( a. into ( ) , b. into ( ) ) ;
168+ assert ! ( a + b - 1 > 0 ) ;
169+ ( a + b - 1 ) / b
170+ }
171+
169172#[ cfg( test) ]
170173mod tests {
171174 use super :: * ;
@@ -176,7 +179,6 @@ mod tests {
176179 chunk_bitmap : 0 ,
177180 total_chunks : 0 ,
178181 chunk_length : 0 ,
179- report_length : 0 ,
180182 version : 0 ,
181183 }
182184 }
@@ -186,19 +188,27 @@ mod tests {
186188 let mut buffer = empty_buffer ( ) ;
187189
188190 assert_eq ! (
189- buffer. initialize( 10 , 100 ) . unwrap_err( ) ,
191+ buffer. initialize( 10 , 100 , 1 , 0 ) . unwrap_err( ) ,
190192 CcipOfframpError :: ExecutionReportBufferInvalidLength . into( )
191193 ) ;
192194
195+ let mut buffer = empty_buffer ( ) ;
193196 assert_eq ! (
194- buffer. initialize( 0 , 1 ) . unwrap_err( ) ,
197+ buffer. initialize( 0 , 1 , 1 , 0 ) . unwrap_err( ) ,
195198 CcipOfframpError :: ExecutionReportBufferInvalidLength . into( )
196199 ) ;
197200
201+ let mut buffer = empty_buffer ( ) ;
198202 // Too small buffer sizes aren't OK as they'd break the bitmap
199203 assert_eq ! (
200- buffer. initialize( 100000 , 1 ) . unwrap_err( ) ,
201- CcipOfframpError :: ExecutionReportBufferChunkSizeTooSmall . into( )
204+ buffer. initialize( 100000 , 1 , 1 , 0 ) . unwrap_err( ) ,
205+ CcipOfframpError :: ExecutionReportBufferInvalidLength . into( )
206+ ) ;
207+
208+ let mut buffer = empty_buffer ( ) ;
209+ assert_eq ! (
210+ buffer. initialize( 100 , 10 , 255 , 0 ) . unwrap_err( ) ,
211+ CcipOfframpError :: ExecutionReportBufferInvalidChunkSize . into( )
202212 ) ;
203213 }
204214
@@ -218,11 +228,13 @@ mod tests {
218228 message. len ( ) as u32 ,
219229 & message[ i * chunk_size..( i + 1 ) * chunk_size] ,
220230 i as u8 ,
231+ 3 ,
221232 )
222233 . unwrap ( ) ;
223234 assert_eq ! ( buffer. filled_chunks( ) as usize , i + 1 ) ;
224235 }
225236
237+ dbg ! ( & buffer) ;
226238 assert ! ( buffer. is_complete( ) ) ;
227239 assert_eq ! ( buffer. bytes( ) . unwrap( ) , message) ;
228240 }
@@ -243,6 +255,7 @@ mod tests {
243255 message. len ( ) as u32 ,
244256 & message[ i * chunk_size..message. len ( ) . min ( ( i + 1 ) * chunk_size) ] ,
245257 i as u8 ,
258+ 4 ,
246259 )
247260 . unwrap ( ) ;
248261 assert_eq ! ( buffer. filled_chunks( ) as usize , i + 1 ) ;
@@ -270,6 +283,7 @@ mod tests {
270283 message. len ( ) as u32 ,
271284 & message[ i * chunk_size..message. len ( ) . min ( ( i + 1 ) * chunk_size) ] ,
272285 i as u8 ,
286+ 4 ,
273287 )
274288 . unwrap ( ) ;
275289 assert_eq ! ( buffer. filled_chunks( ) as usize , 4 - i) ;
@@ -284,31 +298,31 @@ mod tests {
284298 #[ test]
285299 fn rejects_invalid_chunks ( ) {
286300 let mut buffer = empty_buffer ( ) ;
287- buffer. add_chunk ( 22 , b"AAAAA" , 0 ) . unwrap ( ) ;
301+ buffer. add_chunk ( 22 , b"AAAAA" , 0 , 5 ) . unwrap ( ) ;
288302 // This is OK as it is the terminator.
289- buffer. add_chunk ( 22 , b"BB" , 4 ) . unwrap ( ) ;
303+ buffer. add_chunk ( 22 , b"BB" , 4 , 5 ) . unwrap ( ) ;
290304 // This is not OK.
291- buffer. add_chunk ( 22 , b"CCCCCCCCCCC" , 2 ) . unwrap_err ( ) ;
305+ buffer. add_chunk ( 22 , b"CCCCCCCCCCC" , 2 , 5 ) . unwrap_err ( ) ;
292306
293307 let mut buffer = empty_buffer ( ) ;
294- buffer. add_chunk ( 100 , b"small" , 0 ) . unwrap ( ) ;
308+ buffer. add_chunk ( 100 , b"small" , 0 , 20 ) . unwrap ( ) ;
295309 // Invalid due to inconsistent total report size.
296- buffer. add_chunk ( 50 , b"small" , 1 ) . unwrap_err ( ) ;
310+ buffer. add_chunk ( 50 , b"small" , 1 , 20 ) . unwrap_err ( ) ;
297311
298312 let mut buffer = empty_buffer ( ) ;
299- buffer. add_chunk ( 100 , b"small" , 0 ) . unwrap ( ) ;
313+ buffer. add_chunk ( 100 , b"small" , 0 , 20 ) . unwrap ( ) ;
300314 // Invalid due to repeated index.
301- buffer. add_chunk ( 100 , b"small" , 0 ) . unwrap_err ( ) ;
315+ buffer. add_chunk ( 100 , b"small" , 0 , 20 ) . unwrap_err ( ) ;
302316
303317 let mut buffer = empty_buffer ( ) ;
304318 buffer
305- . add_chunk ( 10 , b"Much bigger than ten characters" , 0 )
319+ . add_chunk ( 10 , b"Much bigger than ten characters" , 0 , 1 )
306320 . unwrap_err ( ) ;
307321
308322 let mut buffer = empty_buffer ( ) ;
309- buffer. add_chunk ( 29 , b"Medium sized" , 1 ) . unwrap ( ) ;
323+ buffer. add_chunk ( 29 , b"Medium sized" , 1 , 3 ) . unwrap ( ) ;
310324 // Cannot be smaller: only the terminator can (must come at the end.)
311- buffer. add_chunk ( 29 , b"small" , 0 ) . unwrap_err ( ) ;
312- buffer. add_chunk ( 29 , b"small" , 2 ) . unwrap ( ) ;
325+ buffer. add_chunk ( 29 , b"small" , 0 , 3 ) . unwrap_err ( ) ;
326+ buffer. add_chunk ( 29 , b"small" , 2 , 3 ) . unwrap ( ) ;
313327 }
314328}
0 commit comments