@@ -14,10 +14,12 @@ use std::error;
1414use arbitrary:: { Arbitrary , Unstructured } ;
1515use bitcoin:: consensus:: encode:: { self , Decodable , Encodable , ReadExt , WriteExt } ;
1616use bitcoin:: { block, Block , BlockChecked , BlockHash , Transaction } ;
17+ use encoding:: { CompactSizeDecoder , CompactSizeEncoder , Decoder2 , Encoder2 , SliceEncoder , VecDecoder } ;
1718use hashes:: { sha256, siphash24} ;
1819use internals:: array:: ArrayExt as _;
19- use internals:: ToU64 as _ ;
20+ use internals:: write_err ;
2021use io:: { BufRead , Write } ;
22+ use primitives:: block:: { BlockHashDecoder , BlockHashEncoder } ;
2123
2224/// A BIP-0152 error
2325#[ derive( Debug , Clone , PartialEq , Eq ) ]
@@ -314,27 +316,191 @@ impl HeaderAndShortIds {
314316 }
315317}
316318
319+ #[ derive( Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
320+ struct Offset ( usize ) ;
321+
322+ impl encoding:: Encodable for Offset {
323+ type Encoder < ' e > = CompactSizeEncoder ;
324+
325+ fn encoder ( & self ) -> Self :: Encoder < ' _ > {
326+ CompactSizeEncoder :: new ( self . 0 )
327+ }
328+ }
329+
330+ struct OffsetDecoder ( CompactSizeDecoder ) ;
331+
332+ impl encoding:: Decoder for OffsetDecoder {
333+ type Output = Offset ;
334+ type Error = <CompactSizeDecoder as encoding:: Decoder >:: Error ;
335+
336+ #[ inline]
337+ fn push_bytes ( & mut self , bytes : & mut & [ u8 ] ) -> Result < bool , Self :: Error > {
338+ self . 0 . push_bytes ( bytes)
339+ }
340+
341+ #[ inline]
342+ fn end ( self ) -> Result < Self :: Output , Self :: Error > {
343+ Ok ( Offset ( self . 0 . end ( ) ?) )
344+ }
345+
346+ #[ inline]
347+ fn read_limit ( & self ) -> usize { self . 0 . read_limit ( ) }
348+ }
349+
350+ impl encoding:: Decodable for Offset {
351+ type Decoder = OffsetDecoder ;
352+
353+ fn decoder ( ) -> Self :: Decoder {
354+ OffsetDecoder ( CompactSizeDecoder :: new ( ) )
355+ }
356+ }
357+
317358/// A [`BlockTransactionsRequest`] structure is used to list transaction indexes
318359/// in a block being requested.
319360#[ derive( PartialEq , Eq , Clone , Debug , PartialOrd , Ord , Hash ) ]
320361pub struct BlockTransactionsRequest {
321362 /// The blockhash of the block which the transactions being requested are in.
322363 pub block_hash : BlockHash ,
323- /// The indexes of the transactions being requested in the block.
364+ // The run-length between block indices in the request.
365+ offsets : Vec < Offset > ,
366+ }
367+
368+ impl BlockTransactionsRequest {
369+ /// Build a request for a [`BlockHash`] and implicitly sort the indices on construction.
370+ pub fn from_unsorted_indices ( block_hash : BlockHash , mut indexes : Vec < usize > ) -> Self {
371+ indexes. sort_unstable ( ) ;
372+ let mut offsets = Vec :: with_capacity ( indexes. len ( ) ) ;
373+ let mut last_idx = 0 ;
374+ for idx in indexes {
375+ offsets. push ( Offset ( idx - last_idx) ) ;
376+ last_idx = idx + 1 ;
377+ }
378+ Self { block_hash, offsets }
379+ }
380+
381+ /// Build a request for a [`BlockHash`] assuming the indices are already sorted.
382+ ///
383+ /// # Panics
384+ ///
385+ /// If the list is not in ascending order.
386+ pub fn from_indices_unchecked ( block_hash : BlockHash , indexes : Vec < usize > ) -> Self {
387+ let mut offsets = Vec :: with_capacity ( indexes. len ( ) ) ;
388+ let mut last_idx = 0 ;
389+ for idx in indexes {
390+ offsets. push ( Offset ( idx - last_idx) ) ;
391+ last_idx = idx + 1 ;
392+ }
393+ Self { block_hash, offsets }
394+ }
395+
396+ /// Get the list of indices in the block.
397+ ///
398+ /// # Errors
324399 ///
325- /// Warning: Encoding panics with [`u64::MAX`] values. See [`BlockTransactionsRequest::consensus_encode()`]
326- pub indexes : Vec < u64 > ,
400+ /// If the block index is out of range.
401+ pub fn indices ( & self ) -> Result < Vec < usize > , TxIndexOutOfRangeError > {
402+ let mut last_cs: usize = 0 ;
403+ let mut indexes = Vec :: with_capacity ( self . offsets . len ( ) ) ;
404+ for offset in & self . offsets {
405+ last_cs = match last_cs. checked_add ( offset. 0 ) {
406+ Some ( next) => {
407+ indexes. push ( next) ;
408+ next
409+ } ,
410+ None => {
411+ return Err ( TxIndexOutOfRangeError ( last_cs as u64 ) )
412+ }
413+ } ;
414+ last_cs = last_cs. checked_add ( 1 ) . ok_or ( TxIndexOutOfRangeError ( last_cs as u64 ) ) ?;
415+ }
416+ Ok ( indexes)
417+ }
418+ }
419+
420+ encoding:: encoder_newtype! {
421+ /// The encoder for [`BlockTransactionsRequest`].
422+ pub struct BlockTransactionsRequestEncoder <' e>(
423+ Encoder2 <
424+ BlockHashEncoder ,
425+ Encoder2 <CompactSizeEncoder , SliceEncoder <' e, Offset >>
426+ >
427+ ) ;
428+ }
429+
430+ impl encoding:: Encodable for BlockTransactionsRequest {
431+ type Encoder < ' e > = BlockTransactionsRequestEncoder < ' e > ;
432+
433+ fn encoder ( & self ) -> Self :: Encoder < ' _ > {
434+ BlockTransactionsRequestEncoder (
435+ Encoder2 :: new (
436+ self . block_hash . encoder ( ) ,
437+ Encoder2 :: new (
438+ CompactSizeEncoder :: new ( self . offsets . len ( ) ) ,
439+ SliceEncoder :: without_length_prefix ( & self . offsets )
440+ )
441+ )
442+ )
443+ }
444+ }
445+
446+ type BlockTransactionsRequestInnerDecoder = Decoder2 < BlockHashDecoder , VecDecoder < Offset > > ;
447+
448+ /// The encoder type for a [`BlockTransactionsRequest`].
449+ pub struct BlockTransactionsRequestDecoder ( BlockTransactionsRequestInnerDecoder ) ;
450+
451+ impl encoding:: Decoder for BlockTransactionsRequestDecoder {
452+ type Output = BlockTransactionsRequest ;
453+ type Error = BlockTransactionsRequestDecoderError ;
454+
455+ #[ inline]
456+ fn push_bytes ( & mut self , bytes : & mut & [ u8 ] ) -> Result < bool , Self :: Error > {
457+ self . 0 . push_bytes ( bytes) . map_err ( BlockTransactionsRequestDecoderError )
458+ }
459+
460+ #[ inline]
461+ fn end ( self ) -> Result < Self :: Output , Self :: Error > {
462+ let ( block_hash, offsets) = self . 0 . end ( ) . map_err ( BlockTransactionsRequestDecoderError ) ?;
463+ Ok ( BlockTransactionsRequest { block_hash, offsets } )
464+ }
465+
466+ #[ inline]
467+ fn read_limit ( & self ) -> usize { self . 0 . read_limit ( ) }
468+ }
469+
470+ impl encoding:: Decodable for BlockTransactionsRequest {
471+ type Decoder = BlockTransactionsRequestDecoder ;
472+
473+ fn decoder ( ) -> Self :: Decoder {
474+ BlockTransactionsRequestDecoder ( Decoder2 :: new ( BlockHashDecoder :: new ( ) , VecDecoder :: new ( ) ) )
475+ }
476+ }
477+
478+ /// An error decoding a [`BlockTransactionsRequest`] message.
479+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
480+ pub struct BlockTransactionsRequestDecoderError ( <BlockTransactionsRequestInnerDecoder as encoding:: Decoder >:: Error ) ;
481+
482+ impl From < Infallible > for BlockTransactionsRequestDecoderError {
483+ fn from ( never : Infallible ) -> Self { match never { } }
484+ }
485+
486+ impl fmt:: Display for BlockTransactionsRequestDecoderError {
487+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
488+ write_err ! ( f, "blocktxnrequest error" ; self . 0 )
489+ }
490+ }
491+
492+ #[ cfg( feature = "std" ) ]
493+ impl std:: error:: Error for BlockTransactionsRequestDecoderError {
494+ fn source ( & self ) -> Option < & ( dyn std:: error:: Error + ' static ) > { Some ( & self . 0 ) }
327495}
328496
329497impl Encodable for BlockTransactionsRequest {
330498 fn consensus_encode < W : Write + ?Sized > ( & self , w : & mut W ) -> Result < usize , io:: Error > {
331499 let mut len = self . block_hash . consensus_encode ( w) ?;
332500 // Manually encode indexes because they are differentially encoded as CompactSize.
333- len += w. emit_compact_size ( self . indexes . len ( ) ) ?;
334- let mut last_idx = 0 ;
335- for idx in & self . indexes {
336- len += w. emit_compact_size ( * idx - last_idx) ?;
337- last_idx = * idx + 1 ; // can panic here
501+ len += w. emit_compact_size ( self . offsets . len ( ) ) ?;
502+ for idx in & self . offsets {
503+ len += w. emit_compact_size ( idx. 0 ) ?;
338504 }
339505 Ok ( len)
340506 }
@@ -344,7 +510,7 @@ impl Decodable for BlockTransactionsRequest {
344510 fn consensus_decode < R : BufRead + ?Sized > ( r : & mut R ) -> Result < Self , encode:: Error > {
345511 Ok ( Self {
346512 block_hash : BlockHash :: consensus_decode ( r) ?,
347- indexes : {
513+ offsets : {
348514 // Manually decode indexes because they are differentially encoded as CompactSize.
349515 let nb_indexes = r. read_compact_size ( ) ? as usize ;
350516
@@ -361,28 +527,12 @@ impl Decodable for BlockTransactionsRequest {
361527 }
362528 . into ( ) ) ;
363529 }
364-
365- let mut indexes = Vec :: with_capacity ( nb_indexes) ;
366- let mut last_index: u64 = 0 ;
530+ let mut offsets = Vec :: with_capacity ( nb_indexes) ;
367531 for _ in 0 ..nb_indexes {
368532 let differential = r. read_compact_size ( ) ?;
369- last_index = match last_index. checked_add ( differential) {
370- Some ( i) => i,
371- None =>
372- return Err ( crate :: consensus:: parse_failed_error (
373- "block index overflow" ,
374- ) ) ,
375- } ;
376- indexes. push ( last_index) ;
377- last_index = match last_index. checked_add ( 1 ) {
378- Some ( i) => i,
379- None =>
380- return Err ( crate :: consensus:: parse_failed_error (
381- "block index overflow" ,
382- ) ) ,
383- } ;
533+ offsets. push ( Offset ( differential as usize ) ) ;
384534 }
385- indexes
535+ offsets
386536 } ,
387537 } )
388538 }
@@ -435,12 +585,13 @@ impl BlockTransactions {
435585 Ok ( Self {
436586 block_hash : request. block_hash ,
437587 transactions : {
438- let mut txs = Vec :: with_capacity ( request. indexes . len ( ) ) ;
439- for idx in & request. indexes {
440- if * idx >= block. transactions ( ) . len ( ) . to_u64 ( ) {
441- return Err ( TxIndexOutOfRangeError ( * idx) ) ;
588+ let mut txs = Vec :: with_capacity ( request. offsets . len ( ) ) ;
589+ let indexes = request. indices ( ) ?;
590+ for idx in indexes {
591+ if idx >= block. transactions ( ) . len ( ) {
592+ return Err ( TxIndexOutOfRangeError ( idx as u64 ) ) ;
442593 }
443- txs. push ( block. transactions ( ) [ * idx as usize ] . clone ( ) ) ;
594+ txs. push ( block. transactions ( ) [ idx] . clone ( ) ) ;
444595 }
445596 txs
446597 } ,
@@ -479,10 +630,17 @@ impl<'a> Arbitrary<'a> for BlockTransactions {
479630 }
480631}
481632
633+ #[ cfg( feature = "arbitrary" ) ]
634+ impl < ' a > Arbitrary < ' a > for Offset {
635+ fn arbitrary ( u : & mut Unstructured < ' a > ) -> arbitrary:: Result < Self > {
636+ Ok ( Self ( u. arbitrary ( ) ?) )
637+ }
638+ }
639+
482640#[ cfg( feature = "arbitrary" ) ]
483641impl < ' a > Arbitrary < ' a > for BlockTransactionsRequest {
484642 fn arbitrary ( u : & mut Unstructured < ' a > ) -> arbitrary:: Result < Self > {
485- Ok ( Self { block_hash : u. arbitrary ( ) ?, indexes : Vec :: < u64 > :: arbitrary ( u) ? } )
643+ Ok ( Self { block_hash : u. arbitrary ( ) ?, offsets : Vec :: < Offset > :: arbitrary ( u) ? } )
486644 }
487645}
488646
@@ -584,14 +742,11 @@ mod test {
584742 let mut raw: Vec < u8 > = vec ! [ 0u8 ; 32 ] ;
585743 raw. extend ( testcase. 0 . clone ( ) ) ;
586744 let btr: BlockTransactionsRequest = deserialize ( & raw . clone ( ) ) . unwrap ( ) ;
587- assert_eq ! ( testcase. 1 , btr. indexes ) ;
745+ assert_eq ! ( testcase. 1 , btr. indices ( ) . unwrap ( ) ) ;
588746 }
589747 {
590748 // test serialization
591- let raw: Vec < u8 > = serialize ( & BlockTransactionsRequest {
592- block_hash : BlockHash :: from_byte_array ( [ 0 ; 32 ] ) ,
593- indexes : testcase. 1 ,
594- } ) ;
749+ let raw: Vec < u8 > = serialize ( & & BlockTransactionsRequest :: from_indices_unchecked ( BlockHash :: from_byte_array ( [ 0 ; 32 ] ) , testcase. 1 ) ) ;
595750 let mut expected_raw: Vec < u8 > = [ 0u8 ; 32 ] . to_vec ( ) ;
596751 expected_raw. extend ( testcase. 0 ) ;
597752 assert_eq ! ( expected_raw, raw) ;
@@ -602,18 +757,19 @@ mod test {
602757 // test that we return Err() if deserialization fails (and don't panic)
603758 let mut raw: Vec < u8 > = [ 0u8 ; 32 ] . to_vec ( ) ;
604759 raw. extend ( errorcase) ;
605- assert ! ( deserialize:: <BlockTransactionsRequest >( & raw. clone( ) ) . is_err( ) ) ;
760+ let get_block_txn = deserialize :: < BlockTransactionsRequest > ( & raw . clone ( ) ) . unwrap ( ) ;
761+ assert ! ( get_block_txn. indices( ) . is_err( ) ) ;
606762 }
607763 }
608764 }
609765
610766 #[ test]
611- #[ cfg( debug_assertions) ]
612- #[ should_panic( expected = "attempt to add with overflow" ) ]
613767 fn getblocktx_panic_when_encoding_u64_max ( ) {
614- serialize ( & BlockTransactionsRequest {
768+ assert ! ( BlockTransactionsRequest {
615769 block_hash: BlockHash :: from_byte_array( [ 0 ; 32 ] ) ,
616- indexes : vec ! [ u64 :: MAX ] ,
617- } ) ;
770+ offsets: vec![ Offset ( usize :: MAX ) ] ,
771+ }
772+ . indices( )
773+ . is_err( ) ) ;
618774 }
619775}
0 commit comments