@@ -4,7 +4,7 @@ use std::io::{Cursor, Read, Write};
44
55use thiserror:: Error ;
66
7- #[ derive( Error , Debug ) ]
7+ #[ derive( Error , Debug , PartialEq , Eq ) ]
88pub enum Error {
99 #[ error( "variable-length uint overflow" ) ]
1010 VarUintOverflow ,
@@ -13,20 +13,65 @@ pub enum Error {
1313 UnexpectedEof ,
1414}
1515
16+ #[ derive( Error , Debug , PartialEq , Eq ) ]
17+ pub enum StrictError {
18+ #[ error( "variable-length uint overflow" ) ]
19+ VarUintOverflow ,
20+
21+ #[ error( "unexpected end-of-buffer" ) ]
22+ UnexpectedEof ,
23+
24+ #[ error( "non-canonical variable-length uint encoding" ) ]
25+ NonCanonical ,
26+ }
27+
1628pub fn read ( cursor : & mut Cursor < & [ u8 ] > ) -> Result < u64 , Error > {
29+ read_inner ( cursor, false ) . map_err ( |e| match e {
30+ ReadModeError :: Legacy ( err) => err,
31+ ReadModeError :: Strict ( StrictError :: VarUintOverflow ) => Error :: VarUintOverflow ,
32+ ReadModeError :: Strict ( StrictError :: UnexpectedEof ) => Error :: UnexpectedEof ,
33+ ReadModeError :: Strict ( StrictError :: NonCanonical ) => unreachable ! ( ) ,
34+ } )
35+ }
36+
37+ pub fn read_strict ( cursor : & mut Cursor < & [ u8 ] > ) -> Result < u64 , StrictError > {
38+ read_inner ( cursor, true ) . map_err ( |e| match e {
39+ ReadModeError :: Legacy ( err) => match err {
40+ Error :: VarUintOverflow => StrictError :: VarUintOverflow ,
41+ Error :: UnexpectedEof => StrictError :: UnexpectedEof ,
42+ } ,
43+ ReadModeError :: Strict ( err) => err,
44+ } )
45+ }
46+
47+ enum ReadModeError {
48+ Legacy ( Error ) ,
49+ Strict ( StrictError ) ,
50+ }
51+
52+ fn read_inner ( cursor : & mut Cursor < & [ u8 ] > , strict : bool ) -> Result < u64 , ReadModeError > {
1753 let mut output = 0u128 ;
1854 let mut buf = [ 0u8 ; 1 ] ;
55+ let start = cursor. position ( ) as usize ;
1956
2057 loop {
21- cursor
22- . read_exact ( & mut buf)
23- . map_err ( |_| Error :: UnexpectedEof ) ?;
58+ cursor. read_exact ( & mut buf) . map_err ( |_| {
59+ if strict {
60+ ReadModeError :: Strict ( StrictError :: UnexpectedEof )
61+ } else {
62+ ReadModeError :: Legacy ( Error :: UnexpectedEof )
63+ }
64+ } ) ?;
2465
2566 let byte = buf[ 0 ] ;
2667
2768 output = ( output << 7 ) | ( byte & 0x7F ) as u128 ;
2869
2970 if output > u64:: MAX . into ( ) {
71+ if strict {
72+ return Err ( ReadModeError :: Strict ( StrictError :: VarUintOverflow ) ) ;
73+ }
74+
3075 // Strictly speaking, if we find a value above max u64, an overflow error should
3176 // be returned. The problem is that testnet has some invalid address values
3277 // somehow minted in valid blocks. The node and many explorers, instead of
@@ -38,7 +83,19 @@ pub fn read(cursor: &mut Cursor<&[u8]>) -> Result<u64, Error> {
3883 }
3984
4085 if ( byte & 0x80 ) == 0 {
41- return Ok ( output as u64 ) ;
86+ let output = output as u64 ;
87+
88+ if strict {
89+ let end = cursor. position ( ) as usize ;
90+ let consumed = & cursor. get_ref ( ) [ start..end] ;
91+ let canonical = encode_to_vec ( output) ;
92+
93+ if consumed != canonical. as_slice ( ) {
94+ return Err ( ReadModeError :: Strict ( StrictError :: NonCanonical ) ) ;
95+ }
96+ }
97+
98+ return Ok ( output) ;
4299 }
43100 }
44101}
@@ -54,3 +111,58 @@ pub fn write(cursor: &mut Cursor<Vec<u8>>, mut num: u64) {
54111
55112 cursor. write_all ( & output) . unwrap ( ) ;
56113}
114+
115+ fn encode_to_vec ( num : u64 ) -> Vec < u8 > {
116+ let mut cursor = Cursor :: new ( vec ! [ ] ) ;
117+ write ( & mut cursor, num) ;
118+ cursor. into_inner ( )
119+ }
120+
121+ #[ cfg( test) ]
122+ mod tests {
123+ use super :: * ;
124+
125+ #[ test]
126+ fn read_accepts_noncanonical_zero_for_compatibility ( ) {
127+ let bytes = [ 0x80 , 0x00 ] ;
128+ let mut cursor = Cursor :: new ( bytes. as_slice ( ) ) ;
129+
130+ let value = read ( & mut cursor) . unwrap ( ) ;
131+
132+ assert_eq ! ( value, 0 ) ;
133+ assert_eq ! ( cursor. position( ) , 2 ) ;
134+ }
135+
136+ #[ test]
137+ fn read_strict_rejects_noncanonical_zero ( ) {
138+ let bytes = [ 0x80 , 0x00 ] ;
139+ let mut cursor = Cursor :: new ( bytes. as_slice ( ) ) ;
140+
141+ let value = read_strict ( & mut cursor) ;
142+
143+ assert_eq ! ( value, Err ( StrictError :: NonCanonical ) ) ;
144+ }
145+
146+ #[ test]
147+ fn read_strict_accepts_canonical_zero ( ) {
148+ let bytes = [ 0x00 ] ;
149+ let mut cursor = Cursor :: new ( bytes. as_slice ( ) ) ;
150+
151+ let value = read_strict ( & mut cursor) . unwrap ( ) ;
152+
153+ assert_eq ! ( value, 0 ) ;
154+ assert_eq ! ( cursor. position( ) , 1 ) ;
155+ }
156+
157+ #[ test]
158+ fn read_strict_rejects_overflow ( ) {
159+ let bytes = [
160+ 0x82 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x00 ,
161+ ] ;
162+ let mut cursor = Cursor :: new ( bytes. as_slice ( ) ) ;
163+
164+ let value = read_strict ( & mut cursor) ;
165+
166+ assert_eq ! ( value, Err ( StrictError :: VarUintOverflow ) ) ;
167+ }
168+ }
0 commit comments