@@ -494,6 +494,28 @@ impl ChainQuery {
494494 }
495495 }
496496
497+ pub fn get_block_txs (
498+ & self ,
499+ hash : & BlockHash ,
500+ start_index : usize ,
501+ limit : usize ,
502+ ) -> Result < Vec < Transaction > > {
503+ let txids = self . get_block_txids ( hash) . chain_err ( || "block not found" ) ?;
504+ ensure ! ( start_index < txids. len( ) , "start index out of range" ) ;
505+
506+ let txids_with_blockhash = txids
507+ . into_iter ( )
508+ . skip ( start_index)
509+ . take ( limit)
510+ . map ( |txid| ( txid, * hash) )
511+ . collect :: < Vec < _ > > ( ) ;
512+
513+ self . lookup_txns ( & txids_with_blockhash)
514+
515+ // XXX use getblock in lightmode? a single RPC call, but would fetch all txs to get one page
516+ // self.daemon.getblock(hash)?.txdata.into_iter().skip(start_index).take(limit).collect()
517+ }
518+
497519 pub fn get_block_meta ( & self , hash : & BlockHash ) -> Option < BlockMeta > {
498520 let _timer = self . start_timer ( "get_block_meta" ) ;
499521
@@ -519,17 +541,19 @@ impl ChainQuery {
519541 let entry = self . header_by_hash ( hash) ?;
520542 let meta = self . get_block_meta ( hash) ?;
521543 let txids = self . get_block_txids ( hash) ?;
544+ let txids_with_blockhash: Vec < _ > =
545+ txids. into_iter ( ) . map ( |txid| ( txid, * hash) ) . collect ( ) ;
546+ let raw_txs = self . lookup_raw_txns ( & txids_with_blockhash) . ok ( ) ?; // TODO avoid hiding all errors as None, return a Result
522547
523548 // Reconstruct the raw block using the header and txids,
524549 // as <raw header><tx count varint><raw txs>
525550 let mut raw = Vec :: with_capacity ( meta. size as usize ) ;
526551
527552 raw. append ( & mut serialize ( entry. header ( ) ) ) ;
528- raw. append ( & mut serialize ( & VarInt ( txids . len ( ) as u64 ) ) ) ;
553+ raw. append ( & mut serialize ( & VarInt ( raw_txs . len ( ) as u64 ) ) ) ;
529554
530- for txid in txids {
531- // we don't need to provide the blockhash because we know we're not in light mode
532- raw. append ( & mut self . lookup_raw_txn ( & txid, None ) ?) ;
555+ for mut raw_tx in raw_txs {
556+ raw. append ( & mut raw_tx) ;
533557 }
534558
535559 Some ( raw)
@@ -589,7 +613,7 @@ impl ChainQuery {
589613 ) -> Vec < ( Transaction , BlockId ) > {
590614 let _timer_scan = self . start_timer ( "history" ) ;
591615 let headers = self . store . indexed_headers . read ( ) . unwrap ( ) ;
592- let txs_conf = self
616+ let history_iter = self
593617 . history_iter_scan_reverse ( code, hash)
594618 . map ( TxHistoryRow :: from_row)
595619 . map ( |row| ( row. get_txid ( ) , row. key . confirmed_height as usize ) )
@@ -605,16 +629,22 @@ impl ChainQuery {
605629 None => 0 ,
606630 } )
607631 // skip over entries that point to non-existing heights (may happen during reorg handling)
608- . filter_map ( |( txid, height) | Some ( ( txid, headers. header_by_height ( height) ?. into ( ) ) ) )
609- . take ( limit)
610- . collect :: < Vec < ( Txid , BlockId ) > > ( ) ;
632+ . filter_map ( |( txid, height) | Some ( ( txid, headers. header_by_height ( height) ?) ) )
633+ . take ( limit) ;
634+
635+ let mut txids_with_blockhash = Vec :: with_capacity ( limit) ;
636+ let mut blockids = Vec :: with_capacity ( limit) ;
637+ for ( txid, header) in history_iter {
638+ txids_with_blockhash. push ( ( txid, * header. hash ( ) ) ) ;
639+ blockids. push ( BlockId :: from ( header) ) ;
640+ }
611641 drop ( headers) ;
612642
613- self . lookup_txns ( & txs_conf )
643+ self . lookup_txns ( & txids_with_blockhash )
614644 . expect ( "failed looking up txs in history index" )
615645 . into_iter ( )
616- . zip ( txs_conf )
617- . map ( |( tx, ( _ , blockid) ) | ( tx, blockid) )
646+ . zip ( blockids )
647+ . map ( |( tx, blockid) | ( tx, blockid) )
618648 . collect ( )
619649 }
620650
@@ -926,26 +956,40 @@ impl ChainQuery {
926956 . clone ( )
927957 }
928958
929- // TODO: can we pass txids as a "generic iterable"?
930- // TODO: should also use a custom ThreadPoolBuilder?
931- pub fn lookup_txns ( & self , txids : & [ ( Txid , BlockId ) ] ) -> Result < Vec < Transaction > > {
959+ pub fn lookup_txns ( & self , txids : & [ ( Txid , BlockHash ) ] ) -> Result < Vec < Transaction > > {
932960 let _timer = self . start_timer ( "lookup_txns" ) ;
933- txids
934- . par_iter ( )
935- . map ( |( txid, blockid) | {
936- self . lookup_txn ( txid, Some ( & blockid. hash ) )
937- . chain_err ( || "missing tx" )
938- } )
939- . collect :: < Result < Vec < Transaction > > > ( )
961+ Ok ( self
962+ . lookup_raw_txns ( txids) ?
963+ . into_iter ( )
964+ . map ( |rawtx| deserialize ( & rawtx) . expect ( "failed to parse Transaction" ) )
965+ . collect ( ) )
940966 }
941967
942968 pub fn lookup_txn ( & self , txid : & Txid , blockhash : Option < & BlockHash > ) -> Option < Transaction > {
943969 let _timer = self . start_timer ( "lookup_txn" ) ;
944- self . lookup_raw_txn ( txid, blockhash) . map ( |rawtx| {
945- let txn: Transaction = deserialize ( & rawtx) . expect ( "failed to parse Transaction" ) ;
946- assert_eq ! ( * txid, txn. compute_txid( ) ) ;
947- txn
948- } )
970+ let rawtx = self . lookup_raw_txn ( txid, blockhash) ?;
971+ Some ( deserialize ( & rawtx) . expect ( "failed to parse Transaction" ) )
972+ }
973+
974+ pub fn lookup_raw_txns ( & self , txids : & [ ( Txid , BlockHash ) ] ) -> Result < Vec < Bytes > > {
975+ let _timer = self . start_timer ( "lookup_raw_txns" ) ;
976+ if self . light_mode {
977+ txids
978+ . par_iter ( )
979+ . map ( |( txid, blockhash) | {
980+ self . lookup_raw_txn ( txid, Some ( blockhash) )
981+ . chain_err ( || "missing tx" )
982+ } )
983+ . collect ( )
984+ } else {
985+ let keys = txids. iter ( ) . map ( |( txid, _) | TxRow :: key ( & txid[ ..] ) ) ;
986+ self . store
987+ . txstore_db
988+ . multi_get ( keys)
989+ . into_iter ( )
990+ . map ( |val| val. unwrap ( ) . chain_err ( || "missing tx" ) )
991+ . collect ( )
992+ }
949993 }
950994
951995 pub fn lookup_raw_txn ( & self , txid : & Txid , blockhash : Option < & BlockHash > ) -> Option < Bytes > {
0 commit comments