@@ -673,4 +673,126 @@ export default class BlockDbRepository implements BlockRepository {
673673
674674 return output ;
675675 }
676+
677+ async getLastBlocksWithDepth (
678+ chainIdsParam : string [ ] ,
679+ minimumDepth : number ,
680+ startingTimestamp : number ,
681+ id ?: string ,
682+ ) : Promise < BlockOutput [ ] > {
683+ const chainIds = chainIdsParam . length ? chainIdsParam . map ( Number ) : await this . getChainIds ( ) ;
684+ const maxHeights = await this . getMaxHeights ( ) ;
685+
686+ const queryParams : any [ ] = [ ] ;
687+ const conditions : string [ ] = [ ] ;
688+
689+ chainIds . forEach ( chainId => {
690+ const maxHeight = maxHeights [ chainId ] ;
691+ if ( maxHeight !== undefined ) {
692+ conditions . push (
693+ `("chainId" = $${ queryParams . length + 1 } AND height <= $${ queryParams . length + 2 } )` ,
694+ ) ;
695+ queryParams . push ( chainId , maxHeight - minimumDepth ) ;
696+ }
697+ } ) ;
698+
699+ queryParams . push ( startingTimestamp . toFixed ( ) ) ;
700+
701+ let query = `
702+ SELECT *
703+ FROM "Blocks"
704+ WHERE ${ conditions . join ( ' OR ' ) }
705+ AND "creationTime" > $${ queryParams . length }
706+ ` ;
707+
708+ if ( id ) {
709+ queryParams . push ( id ) ;
710+ query += `
711+ AND id > $${ queryParams . length }
712+ ORDER BY id DESC
713+ LIMIT 100
714+ ` ;
715+ } else {
716+ query += `
717+ ORDER BY id DESC
718+ LIMIT 5
719+ ` ;
720+ }
721+
722+ const { rows : blockRows } = await rootPgPool . query ( query , queryParams ) ;
723+
724+ const blocksToReturn = blockRows . map ( row => blockValidator . validate ( row ) ) ;
725+ const blockHashToDepth = await this . createBlockDepthMap ( blocksToReturn , 'hash' , minimumDepth ) ;
726+
727+ return blocksToReturn . filter ( block => blockHashToDepth [ block . hash ] >= minimumDepth ) ;
728+ }
729+
730+ async getConfirmationDepth ( blockHash : string , minimumDepth : number ) : Promise < number > {
731+ const query = `
732+ WITH RECURSIVE BlockDescendants AS (
733+ SELECT hash, parent, 0 AS depth, height, "chainId"
734+ FROM "Blocks"
735+ WHERE hash = $1
736+ UNION ALL
737+ SELECT b.hash, b.parent, d.depth + 1 AS depth, b.height, b."chainId"
738+ FROM BlockDescendants d
739+ JOIN "Blocks" b ON d.hash = b.parent AND b.height = d.height + 1 AND b."chainId" = d."chainId"
740+ WHERE d.depth <= $2
741+ )
742+ SELECT MAX(depth) AS depth
743+ FROM BlockDescendants;
744+ ` ;
745+
746+ const { rows } = await rootPgPool . query ( query , [ blockHash , minimumDepth ] ) ;
747+
748+ if ( rows . length && rows [ 0 ] . depth ) {
749+ return Number ( rows [ 0 ] . depth ) ;
750+ } else {
751+ return 0 ;
752+ }
753+ }
754+
755+ /**
756+ *
757+ * @param items - all the items to create a block depth map for
758+ * @param hashProp - the property of the item that contains the block hash
759+ * @returns a map of block hashes to their confirmation depths
760+ */
761+ async createBlockDepthMap < T > (
762+ items : T [ ] ,
763+ hashProp : keyof T ,
764+ minimumDepth : number ,
765+ ) : Promise < Record < string , number > > {
766+ const uniqueBlockHashes = [ ...new Set ( items . map ( item => item [ hashProp ] ) ) ] ;
767+
768+ const confirmationDepths = await Promise . all (
769+ uniqueBlockHashes . map ( blockHash =>
770+ this . getConfirmationDepth ( blockHash as string , minimumDepth ) ,
771+ ) ,
772+ ) ;
773+
774+ return uniqueBlockHashes . reduce ( ( map : Record < string , number > , blockHash , index ) => {
775+ map [ blockHash as string ] = confirmationDepths [ index ] ;
776+ return map ;
777+ } , { } ) ;
778+ }
779+
780+ async getMaxHeights ( ) : Promise < Record < string , number > > {
781+ const chainIds = await this . getChainIds ( ) ;
782+
783+ const maxHeightsByChainIdPromises = chainIds . map ( async chainId => {
784+ const query = `
785+ SELECT MAX(height) as max_height
786+ FROM "Blocks"
787+ WHERE "chainId" = $1
788+ ` ;
789+
790+ const { rows } = await rootPgPool . query ( query , [ chainId ] ) ;
791+ return { [ chainId . toString ( ) ] : parseInt ( rows [ 0 ] . max_height , 10 ) } ;
792+ } ) ;
793+
794+ const maxHeightsArray = await Promise . all ( maxHeightsByChainIdPromises ) ;
795+
796+ return Object . assign ( { } , ...maxHeightsArray ) ;
797+ }
676798}
0 commit comments