@@ -73,6 +73,7 @@ import {
73
73
decodeEthGas ,
74
74
encodeGasLimit ,
75
75
filterLog ,
76
+ filterLogByAddress ,
76
77
filterLogByTopics ,
77
78
getAllReceiptsAtBlock ,
78
79
getHealthResult ,
@@ -403,10 +404,13 @@ export abstract class BaseProvider extends AbstractProvider {
403
404
_onNewHead = async ( [ header , attempts ] : [ Header , number ] ) => {
404
405
attempts -- ;
405
406
const blockHash = header . hash . toHex ( ) ;
407
+ const blockNumber = header . number . toNumber ( ) ;
408
+
406
409
try {
407
410
const receipts = await getAllReceiptsAtBlock ( this . api , blockHash ) ;
408
411
// update block cache
409
412
this . blockCache . addReceipts ( blockHash , receipts ) ;
413
+ this . blockCache . setlastCachedHeight ( blockNumber ) ;
410
414
411
415
// eth_subscribe
412
416
await this . _notifySubscribers ( header , receipts ) ;
@@ -1742,16 +1746,44 @@ export abstract class BaseProvider extends AbstractProvider {
1742
1746
await this . _isBlockCanonical ( txFromCache . blockHash , txFromCache . blockNumber )
1743
1747
) return txFromCache ;
1744
1748
1745
- // smallest block number to make sure there is no gap between subql and block cache
1746
- const subqlTargetBlock = await this . bestBlockNumber - this . blockCache . cachedBlockHashes . length + 1 ;
1747
- await this . _waitForSubql ( subqlTargetBlock ) ;
1749
+ // make sure there is no gap between subql and cache
1750
+ await this . _checkSubqlHeight ( this . blockCache . cachedBlockHashes . length ) ;
1748
1751
1749
1752
const txFromSubql = await this . subql ?. getTxReceiptByHash ( txHash ) ;
1750
1753
return txFromSubql
1751
1754
? subqlReceiptAdapter ( txFromSubql )
1752
1755
: null ;
1753
1756
} ;
1754
1757
1758
+ _checkSubqlHeight = async ( _maxMissedBlockCount : number ) : Promise < number > => {
1759
+ if ( ! this . subql ) return ;
1760
+
1761
+ /* ---------------
1762
+ usually subql is delayed for a couple blocks
1763
+ so it doesn't make sense to check too small range (less than 5 block)
1764
+ this can also prevent throwing error when provider just started
1765
+ --------------- */
1766
+ const SUBQL_DELAYED_OK_RANGE = 5 ;
1767
+ const maxMissedBlockCount = Math . max ( SUBQL_DELAYED_OK_RANGE , _maxMissedBlockCount ) ;
1768
+
1769
+ const lastProcessedHeight = await this . subql . getLastProcessedHeight ( ) ;
1770
+ const minSubqlHeight = await this . bestBlockNumber - maxMissedBlockCount ;
1771
+ if ( lastProcessedHeight < minSubqlHeight ) {
1772
+ return logger . throwError (
1773
+ 'subql indexer height is less than the minimum height required' ,
1774
+ Logger . errors . SERVER_ERROR ,
1775
+ {
1776
+ lastProcessedHeight,
1777
+ minSubqlHeight,
1778
+ maxMissedBlockCount,
1779
+ curHeight : await this . bestBlockNumber ,
1780
+ }
1781
+ ) ;
1782
+ }
1783
+
1784
+ return lastProcessedHeight ;
1785
+ } ;
1786
+
1755
1787
_sanitizeRawFilter = async ( rawFilter : LogFilter ) : Promise < SanitizedLogFilter > => {
1756
1788
const { fromBlock, toBlock, blockHash, address, topics } = rawFilter ;
1757
1789
const filter : SanitizedLogFilter = {
@@ -1788,6 +1820,33 @@ export abstract class BaseProvider extends AbstractProvider {
1788
1820
return filter ;
1789
1821
} ;
1790
1822
1823
+ _getMaxTargetBlock = async ( toBlock : number ) : Promise < number > => {
1824
+ const upperBound = await this . finalizedBlockNumber ;
1825
+ return Math . min ( toBlock , upperBound ) ;
1826
+ } ;
1827
+
1828
+ _getSubqlMissedLogs = async ( toBlock : number , filter : SanitizedLogFilter ) : Promise < Log [ ] > => {
1829
+ const SUBQL_MAX_MISSED_BLOCKS = 20 ;
1830
+
1831
+ const targetBlock = await this . _getMaxTargetBlock ( toBlock ) ;
1832
+ const lastProcessedHeight = await this . _checkSubqlHeight ( SUBQL_MAX_MISSED_BLOCKS ) ;
1833
+ const missedBlockCount = targetBlock - lastProcessedHeight ;
1834
+ if ( missedBlockCount <= 0 ) return [ ] ;
1835
+
1836
+ const firstMissedBlock = lastProcessedHeight + 1 ;
1837
+ const missedBlocks = Array . from ( { length : missedBlockCount } , ( _ , i ) => firstMissedBlock + i ) ;
1838
+ const missedBlockHashes = await Promise . all ( missedBlocks . map ( this . _getBlockHash . bind ( this ) ) ) ;
1839
+
1840
+ await this . _waitForCache ( targetBlock ) ;
1841
+
1842
+ // all logs should be in cache since missedBlockCount <= 20
1843
+ // no need to filter by blocknumber anymore, since these logs are from missedBlocks directly
1844
+ return missedBlockHashes
1845
+ . map ( this . blockCache . getLogsAtBlock . bind ( this ) )
1846
+ . flat ( )
1847
+ . filter ( log => filterLogByAddress ( log , filter . address ) ) ;
1848
+ } ;
1849
+
1791
1850
// Bloom-filter Queries
1792
1851
getLogs = async ( rawFilter : LogFilter ) : Promise < Log [ ] > => {
1793
1852
if ( ! this . subql ) {
@@ -1798,40 +1857,30 @@ export abstract class BaseProvider extends AbstractProvider {
1798
1857
1799
1858
const filter = await this . _sanitizeRawFilter ( rawFilter ) ;
1800
1859
1801
- await this . _waitForSubql ( filter . toBlock ) ;
1802
-
1803
1860
const subqlLogs = await this . subql . getFilteredLogs ( filter ) ; // only filtered by blockNumber and address
1804
- return subqlLogs
1861
+ const extraLogs = await this . _getSubqlMissedLogs ( filter . toBlock , filter ) ;
1862
+
1863
+ return subqlLogs . concat ( extraLogs )
1805
1864
. filter ( log => filterLogByTopics ( log , filter . topics ) )
1806
1865
. map ( log => this . formatter . filterLog ( log ) ) ;
1807
1866
} ;
1808
1867
1809
- /* ----------
1810
- make sure subql already indexed to target block number
1811
- currently unfinalized blocks indexing is NOT enabled
1812
- so upper bound would be finalized block number
1813
- ---------- */
1814
- _waitForSubql = async ( _targetBlock : number ) => {
1815
- if ( ! this . subql ) return ;
1816
-
1817
- const SUBQL_MAX_WAIT_BLOCKS = 3 ;
1818
-
1819
- const upperBound = await this . finalizedBlockNumber ;
1820
- const targetBlock = Math . min ( _targetBlock , upperBound ) ;
1868
+ _waitForCache = async ( _targetBlock : number ) => {
1869
+ const CACHE_MAX_WAIT_BLOCKS = 2 ;
1821
1870
1822
- let lastProcessedHeight = await this . subql . getLastProcessedHeight ( ) ;
1823
- if ( targetBlock - lastProcessedHeight > SUBQL_MAX_WAIT_BLOCKS ) {
1871
+ const targetBlock = await this . _getMaxTargetBlock ( _targetBlock ) ;
1872
+ let lastCachedHeight = await this . subql . getLastProcessedHeight ( ) ;
1873
+ if ( targetBlock - lastCachedHeight > CACHE_MAX_WAIT_BLOCKS ) {
1824
1874
return logger . throwError (
1825
- `subql indexer is not synced to target block, please wait for it to catch up. Estimated ${ ( targetBlock - lastProcessedHeight ) * 12 } s remaining ...` ,
1875
+ 'blockCache is not synced to target block, please wait for it to catch up' ,
1826
1876
Logger . errors . SERVER_ERROR ,
1827
- { targetBlock, lastProcessedHeight }
1877
+ { targetBlock, lastCachedHeight }
1828
1878
) ;
1829
1879
}
1830
1880
1831
- // wait at most 3 * 12 = 36s
1832
- while ( lastProcessedHeight < targetBlock ) {
1881
+ while ( lastCachedHeight < targetBlock ) {
1833
1882
await sleep ( 1000 ) ;
1834
- lastProcessedHeight = await this . subql . getLastProcessedHeight ( ) ;
1883
+ lastCachedHeight = this . blockCache . lastCachedHeight ;
1835
1884
}
1836
1885
} ;
1837
1886
0 commit comments