@@ -745,6 +745,52 @@ mod data {
745745 . collect ( ) )
746746 }
747747
748+ /// Return the parent block pointer for the block with the given hash.
749+ /// Only reads header columns, not the data column.
750+ pub ( super ) async fn block_parent_ptr (
751+ & self ,
752+ conn : & mut AsyncPgConnection ,
753+ chain : & str ,
754+ hash : & BlockHash ,
755+ ) -> Result < Option < BlockPtr > , Error > {
756+ let result = match self {
757+ Storage :: Shared => {
758+ use public:: ethereum_blocks as b;
759+
760+ b:: table
761+ . select ( ( b:: parent_hash, b:: number) )
762+ . filter ( b:: network_name. eq ( chain) )
763+ . filter ( b:: hash. eq ( format ! ( "{:x}" , hash) ) )
764+ . first :: < ( Option < String > , i64 ) > ( conn)
765+ . await
766+ . optional ( ) ?
767+ . and_then ( |( parent_hash, number) | {
768+ parent_hash. map ( |ph| {
769+ Ok :: < _ , Error > ( BlockPtr :: new (
770+ ph. parse ( ) ?,
771+ i32:: try_from ( number) . unwrap ( ) - 1 ,
772+ ) )
773+ } )
774+ } )
775+ . transpose ( ) ?
776+ }
777+ Storage :: Private ( Schema { blocks, .. } ) => blocks
778+ . table ( )
779+ . select ( ( blocks. parent_hash ( ) , blocks. number ( ) ) )
780+ . filter ( blocks. hash ( ) . eq ( hash. as_slice ( ) ) )
781+ . first :: < ( Vec < u8 > , i64 ) > ( conn)
782+ . await
783+ . optional ( ) ?
784+ . map ( |( parent_hash, number) | {
785+ BlockPtr :: new (
786+ BlockHash :: from ( parent_hash) ,
787+ i32:: try_from ( number) . unwrap ( ) - 1 ,
788+ )
789+ } ) ,
790+ } ;
791+ Ok ( result)
792+ }
793+
748794 pub ( super ) async fn block_hashes_by_block_number (
749795 & self ,
750796 conn : & mut AsyncPgConnection ,
@@ -1222,6 +1268,89 @@ mod data {
12221268 Ok ( data_and_ptr)
12231269 }
12241270
1271+ /// Like `ancestor_block` but returns only the `BlockPtr` without
1272+ /// fetching the `data` column.
1273+ pub ( super ) async fn ancestor_block_ptr (
1274+ & self ,
1275+ conn : & mut AsyncPgConnection ,
1276+ block_ptr : BlockPtr ,
1277+ offset : BlockNumber ,
1278+ root : Option < BlockHash > ,
1279+ ) -> Result < Option < BlockPtr > , Error > {
1280+ let short_circuit_predicate = match root {
1281+ Some ( _) => "and b.parent_hash <> $3" ,
1282+ None => "" ,
1283+ } ;
1284+
1285+ match self {
1286+ Storage :: Shared => {
1287+ let query =
1288+ self . ancestor_block_query ( short_circuit_predicate, "ethereum_blocks" ) ;
1289+
1290+ #[ derive( QueryableByName ) ]
1291+ struct BlockHashAndNumber {
1292+ #[ diesel( sql_type = Text ) ]
1293+ hash : String ,
1294+ #[ diesel( sql_type = BigInt ) ]
1295+ number : i64 ,
1296+ }
1297+
1298+ let block = match root {
1299+ Some ( root) => sql_query ( query)
1300+ . bind :: < Text , _ > ( block_ptr. hash_hex ( ) )
1301+ . bind :: < BigInt , _ > ( offset as i64 )
1302+ . bind :: < Text , _ > ( root. hash_hex ( ) )
1303+ . get_result :: < BlockHashAndNumber > ( conn) ,
1304+ None => sql_query ( query)
1305+ . bind :: < Text , _ > ( block_ptr. hash_hex ( ) )
1306+ . bind :: < BigInt , _ > ( offset as i64 )
1307+ . get_result :: < BlockHashAndNumber > ( conn) ,
1308+ }
1309+ . await
1310+ . optional ( ) ?;
1311+
1312+ match block {
1313+ None => Ok ( None ) ,
1314+ Some ( block) => Ok ( Some ( BlockPtr :: new (
1315+ BlockHash :: from_str ( & block. hash ) ?,
1316+ i32:: try_from ( block. number ) . unwrap ( ) ,
1317+ ) ) ) ,
1318+ }
1319+ }
1320+ Storage :: Private ( Schema { blocks, .. } ) => {
1321+ let query =
1322+ self . ancestor_block_query ( short_circuit_predicate, blocks. qname . as_str ( ) ) ;
1323+
1324+ #[ derive( QueryableByName ) ]
1325+ struct BlockHashAndNumber {
1326+ #[ diesel( sql_type = Bytea ) ]
1327+ hash : Vec < u8 > ,
1328+ #[ diesel( sql_type = BigInt ) ]
1329+ number : i64 ,
1330+ }
1331+
1332+ let block = match & root {
1333+ Some ( root) => sql_query ( query)
1334+ . bind :: < Bytea , _ > ( block_ptr. hash_slice ( ) )
1335+ . bind :: < BigInt , _ > ( offset as i64 )
1336+ . bind :: < Bytea , _ > ( root. as_slice ( ) )
1337+ . get_result :: < BlockHashAndNumber > ( conn) ,
1338+ None => sql_query ( query)
1339+ . bind :: < Bytea , _ > ( block_ptr. hash_slice ( ) )
1340+ . bind :: < BigInt , _ > ( offset as i64 )
1341+ . get_result :: < BlockHashAndNumber > ( conn) ,
1342+ }
1343+ . await
1344+ . optional ( ) ?;
1345+
1346+ match block {
1347+ None => Ok ( None ) ,
1348+ Some ( block) => Ok ( Some ( BlockPtr :: from ( ( block. hash , block. number ) ) ) ) ,
1349+ }
1350+ }
1351+ }
1352+ }
1353+
12251354 pub ( super ) async fn delete_blocks_before (
12261355 & self ,
12271356 conn : & mut AsyncPgConnection ,
@@ -2925,6 +3054,47 @@ impl ChainStoreTrait for ChainStore {
29253054 Ok ( result)
29263055 }
29273056
3057+ async fn ancestor_block_ptr (
3058+ self : Arc < Self > ,
3059+ block_ptr : BlockPtr ,
3060+ offset : BlockNumber ,
3061+ root : Option < BlockHash > ,
3062+ ) -> Result < Option < BlockPtr > , Error > {
3063+ ensure ! (
3064+ block_ptr. number >= offset,
3065+ "block offset {} for block `{}` points to before genesis block" ,
3066+ offset,
3067+ block_ptr. hash_hex( )
3068+ ) ;
3069+
3070+ // Check the in-memory cache first.
3071+ if let Some ( ( ptr, _) ) = self . recent_blocks_cache . get_ancestor ( & block_ptr, offset) {
3072+ return Ok ( Some ( ptr) ) ;
3073+ }
3074+
3075+ // Cache miss, query the database (CTE only, no data fetch).
3076+ let mut conn = self . pool . get_permitted ( ) . await ?;
3077+ self . storage
3078+ . ancestor_block_ptr ( & mut conn, block_ptr, offset, root)
3079+ . await
3080+ }
3081+
3082+ async fn block_parent_ptr (
3083+ self : Arc < Self > ,
3084+ hash : & BlockHash ,
3085+ ) -> Result < Option < BlockPtr > , Error > {
3086+ // Check the in-memory cache first.
3087+ if let Some ( parent_ptr) = self . recent_blocks_cache . get_parent_ptr_by_hash ( hash) {
3088+ return Ok ( Some ( parent_ptr) ) ;
3089+ }
3090+
3091+ // Cache miss, query the database.
3092+ let mut conn = self . pool . get_permitted ( ) . await ?;
3093+ self . storage
3094+ . block_parent_ptr ( & mut conn, & self . chain , hash)
3095+ . await
3096+ }
3097+
29283098 async fn cleanup_cached_blocks (
29293099 & self ,
29303100 ancestor_count : BlockNumber ,
@@ -3121,6 +3291,13 @@ mod recent_blocks_cache {
31213291 . and_then ( |block| block. data . as_ref ( ) . map ( |data| ( & block. ptr , data) ) )
31223292 }
31233293
3294+ fn get_parent_ptr_by_hash ( & self , hash : & BlockHash ) -> Option < BlockPtr > {
3295+ self . blocks
3296+ . values ( )
3297+ . find ( |block| & block. ptr . hash == hash)
3298+ . map ( |block| BlockPtr :: new ( block. parent_hash . clone ( ) , block. ptr . number - 1 ) )
3299+ }
3300+
31243301 fn get_block_by_number ( & self , number : BlockNumber ) -> Option < & CacheBlock > {
31253302 self . blocks . get ( & number)
31263303 }
@@ -3237,6 +3414,17 @@ mod recent_blocks_cache {
32373414 block_opt
32383415 }
32393416
3417+ pub fn get_parent_ptr_by_hash ( & self , hash : & BlockHash ) -> Option < BlockPtr > {
3418+ let inner = self . inner . read ( ) ;
3419+ let result = inner. get_parent_ptr_by_hash ( hash) ;
3420+ if result. is_some ( ) {
3421+ inner. metrics . record_cache_hit ( & inner. network ) ;
3422+ } else {
3423+ inner. metrics . record_cache_miss ( & inner. network ) ;
3424+ }
3425+ result
3426+ }
3427+
32403428 pub fn get_blocks_by_hash ( & self , hashes : & [ BlockHash ] ) -> Vec < ( BlockPtr , CachedBlock ) > {
32413429 let inner = self . inner . read ( ) ;
32423430 let blocks: Vec < _ > = hashes
0 commit comments