@@ -1113,20 +1113,50 @@ impl fmt::Debug for TrieNodePatch {
11131113}
11141114
11151115impl StacksMessageCodec for TrieNodePatch {
1116+ /// Serializes this [`TrieNodePatch`] to the given writer, with the following format:
1117+ ///
1118+ /// 0 1 1+P 2+P 2+P+N
1119+ /// |----|--------|----------|----------------|
1120+ /// id ptr diff len ptr diffs
1121+ /// (1) (P) (1) (N)
1122+ ///
1123+ /// where:
1124+ /// - `id` is [`TrieNodeID::Patch`]
1125+ /// - `ptr` is a compressed [`TriePtr`]
1126+ /// - `diff len` is the number of diffs, serialized as `len - 1`
1127+ /// - `ptr diffs` are the patch diffs written in compressed format
1128+ ///
1129+ /// # Invariants
1130+ ///
1131+ /// The number of diffs must be in the range `1..=256`. A patch is valid only
1132+ /// if it contains at least one diff (see the factory methods
1133+ /// [`TrieNodePatch::try_from_nodetype`] and [`TrieNodePatch::try_from_patch`]).
1134+ ///
1135+ /// To fit in a `u8`, the diff count is normalized to `len - 1` before
1136+ /// serialization.
1137+ ///
1138+ /// # Errors
1139+ ///
1140+ /// Returns `Err(codec_error::SerializeError)` if:
1141+ /// * Writing to `fd` fails.
1142+ /// * `ptr` fails to serialize.
1143+ /// * Any pointer in `ptr diffs` fails to serialize.
1144+ /// * The diff count is `0` or greater than `256`.
11161145 fn consensus_serialize < W : Write > ( & self , fd : & mut W ) -> Result < ( ) , codec_error > {
11171146 write_next ( fd, & ( TrieNodeID :: Patch as u8 ) ) ?;
11181147 self . ptr
11191148 . write_bytes_compressed ( fd)
11201149 . map_err ( |e| codec_error:: SerializeError ( format ! ( "Failed to serialize .ptr: {e:?}" ) ) ) ?;
11211150
11221151 let num_ptrs = self . ptr_diff . len ( ) ;
1123- if num_ptrs >= 256 {
1124- return Err ( codec_error:: SerializeError (
1125- "Cannot serialize TrieNodePatch with more than 256 ptrs" . to_string ( ) ,
1126- ) ) ;
1152+ if num_ptrs == 0 || num_ptrs > 256 {
1153+ return Err ( codec_error:: SerializeError ( format ! (
1154+ "Cannot serialize TrieNodePatch with invalid ptrs len {num_ptrs} (expected 1..=256)"
1155+ ) ) ) ;
11271156 }
1128- // SAFETY: checked that num_ptrs < 256
1129- let num_ptrs_u8 = u8:: try_from ( num_ptrs) . expect ( "infallible" ) ;
1157+ // normalize num_ptrs to range [0, 255] to fit in u8
1158+ let num_ptrs_norm = num_ptrs. checked_sub ( 1 ) . expect ( "infallible" ) ;
1159+ let num_ptrs_u8 = u8:: try_from ( num_ptrs_norm) . expect ( "infallible" ) ;
11301160 write_next ( fd, & num_ptrs_u8) . map_err ( |e| {
11311161 codec_error:: SerializeError ( format ! ( "Failed to serialize .ptr_diff.len(): {e:?}" ) )
11321162 } ) ?;
@@ -1139,6 +1169,22 @@ impl StacksMessageCodec for TrieNodePatch {
11391169 Ok ( ( ) )
11401170 }
11411171
1172+ /// Deserializes a [`TrieNodePatch`] from the given reader.
1173+ ///
1174+ /// This method expects the byte stream to be in the exact format produced by
1175+ /// [`TrieNodePatch::consensus_serialize`] (see that method for the detailed
1176+ /// wire format description)
1177+ ///
1178+ /// During deserialization, the stored diff length is de-normalized by
1179+ /// adding `1`, reversing the `len - 1` normalization applied during
1180+ /// serialization.
1181+ ///
1182+ /// # Errors
1183+ ///
1184+ /// Returns `Err(codec_error::DeserializeError)` if:
1185+ /// * The node identifier does not match [`TrieNodeID::Patch`].
1186+ /// * Reading from `fd` fails.
1187+ /// * The pointer or any pointer diff fails to deserialize.
11421188 fn consensus_deserialize < R : Read > ( fd : & mut R ) -> Result < Self , codec_error > {
11431189 let id: u8 = read_next ( fd) ?;
11441190 if id != TrieNodeID :: Patch as u8 {
@@ -1148,8 +1194,11 @@ impl StacksMessageCodec for TrieNodePatch {
11481194 }
11491195
11501196 let ptr = TriePtr :: read_bytes_compressed ( fd) ?;
1151- let num_ptrs: u8 = read_next ( fd) ?;
1152- let num_ptrs = usize:: try_from ( num_ptrs) . expect ( "infallible" ) ;
1197+ let num_ptrs_u8: u8 = read_next ( fd) ?;
1198+ let num_ptrs_norm = usize:: try_from ( num_ptrs_u8) . expect ( "infallible" ) ;
1199+ // denormalize num_ptrs to range [1, 256] (reversing the -1 introduced during serialization)
1200+ let num_ptrs = num_ptrs_norm. checked_add ( 1 ) . expect ( "infallible" ) ;
1201+
11531202 let mut ptr_diff: Vec < TriePtr > = Vec :: with_capacity ( num_ptrs) ;
11541203 for _ in 0 ..num_ptrs {
11551204 ptr_diff. push ( TriePtr :: read_bytes_compressed ( fd) ?) ;
@@ -1301,6 +1350,7 @@ impl TrieNodePatch {
13011350 new_node : & TrieNodeType ,
13021351 ) -> Option < Self > {
13031352 if clear_ctrl_bits ( old_node. id ( ) ) != clear_ctrl_bits ( new_node. id ( ) ) {
1353+ trace ! ( "Cannot produce TrieNodePatch: old node and new node are not the same type!" ) ;
13041354 return None ;
13051355 }
13061356
@@ -1320,9 +1370,11 @@ impl TrieNodePatch {
13201370 ( _, _) => None ,
13211371 } ;
13221372 let Some ( patch) = patch_opt else {
1373+ trace ! ( "Cannot produce TrieNodePatch: old node and new node are type leaf!" ) ;
13231374 return None ;
13241375 } ;
13251376 if patch. ptr_diff . len ( ) == 0 {
1377+ trace ! ( "Cannot produce TrieNodePatch: patch has no diffs!" ) ;
13261378 return None ;
13271379 }
13281380 Some ( patch)
@@ -1335,6 +1387,7 @@ impl TrieNodePatch {
13351387 new_node : & TrieNodeType ,
13361388 ) -> Option < Self > {
13371389 if clear_ctrl_bits ( old_patch. ptr . id ) != clear_ctrl_bits ( new_node. id ( ) ) {
1390+ trace ! ( "Cannot produce TrieNodePatch: old node and new node are not the same type!" ) ;
13381391 return None ;
13391392 }
13401393
@@ -1344,6 +1397,7 @@ impl TrieNodePatch {
13441397 ptr_diff,
13451398 } ;
13461399 if patch. ptr_diff . len ( ) == 0 {
1400+ trace ! ( "Cannot produce TrieNodePatch: patch has no diffs!" ) ;
13471401 return None ;
13481402 }
13491403 return Some ( patch) ;
0 commit comments