@@ -4,7 +4,7 @@ import ElectrumClient from 'electrum-client';
44import { checkFeeEstimates } from './checkFeeEstimates' ;
55//API: https://electrumx.readthedocs.io/en/latest/protocol-methods.html
66
7- import { networks , Network } from 'bitcoinjs-lib' ;
7+ import { networks , Network , Block } from 'bitcoinjs-lib' ;
88import {
99 ELECTRUM_BLOCKSTREAM_HOST ,
1010 ELECTRUM_BLOCKSTREAM_PORT ,
@@ -18,6 +18,7 @@ import {
1818} from './constants' ;
1919import {
2020 Explorer ,
21+ BlockStatus ,
2122 IRREV_CONF_THRESH ,
2223 MAX_TX_PER_SCRIPTPUBKEY
2324} from './interface' ;
@@ -71,10 +72,11 @@ function defaultElectrumServer(network: Network = networks.bitcoin): {
7172
7273export class ElectrumExplorer implements Explorer {
7374 #irrevConfThresh: number ;
74- #blockTipHeight ! : number ;
75+ #tipBlockHeight ! : number ;
7576 #maxTxPerScriptPubKey: number ;
7677 #pingInterval! : ReturnType < typeof setTimeout > | undefined ;
7778 #client! : ElectrumClient | undefined ;
79+ #blockStatusMap: Map < number , BlockStatus > = new Map ( ) ;
7880
7981 #host: string ;
8082 #port: number ;
@@ -155,7 +157,7 @@ export class ElectrumExplorer implements Explorer {
155157 } ) ;
156158 this . #client. subscribe . on (
157159 'blockchain.headers.subscribe' ,
158- ( headers : Array < { height : number } > ) => {
160+ ( headers : Array < { height : number ; hex : string } > ) => {
159161 if ( Array . isArray ( headers ) ) {
160162 for ( const header of headers ) {
161163 this . #updateBlockTipHeight( header ) ;
@@ -190,6 +192,24 @@ export class ElectrumExplorer implements Explorer {
190192 } , 60 * 1000 ) ; // 60 * 1000 ms = 1 minute
191193 }
192194
195+ /**
196+ * Implements {@link Explorer#fetchBlockStatus}.
197+ */
198+ async fetchBlockStatus (
199+ blockHeight : number
200+ ) : Promise < BlockStatus | undefined > {
201+ let blockStatus = this . #blockStatusMap. get ( blockHeight ) ;
202+ if ( blockStatus && blockStatus . irreversible ) return blockStatus ;
203+ if ( blockHeight > this . #tipBlockHeight) return ;
204+
205+ const client = await this . #getClient( ) ;
206+ const headerHex = await client . blockchainBlock_header ( blockHeight ) ;
207+ //cache header info to skip queries in fetchBlockStatus
208+ blockStatus = this . #updateBlockStatusMap( blockHeight , headerHex ) ;
209+
210+ return blockStatus ;
211+ }
212+
193213 /**
194214 * Implements {@link Explorer#close}.
195215 */
@@ -213,21 +233,42 @@ export class ElectrumExplorer implements Explorer {
213233 }
214234 }
215235
216- #updateBlockTipHeight( header : { height : number } ) {
236+ #updateBlockStatusMap( blockHeight : number , headerHex : string ) : BlockStatus {
237+ let blockStatus = this . #blockStatusMap. get ( blockHeight ) ;
238+ if ( blockStatus && blockStatus . irreversible ) return blockStatus ;
239+
240+ const headerBuffer = Buffer . from ( headerHex , 'hex' ) ;
241+ const header = Block . fromBuffer ( headerBuffer ) ;
242+
243+ const blockHash = header . getId ( ) ;
244+ const blockTime = header . timestamp ;
245+ const numConfirmations = this . #tipBlockHeight - blockHeight + 1 ;
246+ const irreversible = numConfirmations >= this . #irrevConfThresh;
247+
248+ blockStatus = { blockHeight, blockHash, blockTime, irreversible } ;
249+ this . #blockStatusMap. set ( blockHeight , blockStatus ) ;
250+
251+ return blockStatus ;
252+ }
253+
254+ #updateBlockTipHeight( header : { height : number ; hex : string } ) {
217255 if (
218256 header &&
257+ header . hex &&
219258 header . height &&
220- ( typeof this . #blockTipHeight === 'undefined' ||
221- header . height > this . #blockTipHeight )
259+ ( typeof this . #tipBlockHeight === 'undefined' ||
260+ header . height > this . #tipBlockHeight )
222261 ) {
223- this . #blockTipHeight = header . height ;
224- //this.#blockTime = Math.floor(+new Date() / 1000);
262+ this . #tipBlockHeight = header . height ;
263+
264+ //cache header info to skip queries in fetchBlockStatus
265+ this . #updateBlockStatusMap( header . height , header . hex ) ;
225266 }
226267 }
227268
228- async getBlockHeight ( ) {
229- return this . #blockTipHeight ;
230- }
269+ // async # getBlockHeight() {
270+ // return this.#tipBlockHeight ;
271+ // }
231272
232273 /**
233274 * Implements {@link Explorer#fetchAddress}.
@@ -309,13 +350,13 @@ export class ElectrumExplorer implements Explorer {
309350 */
310351 async fetchBlockHeight ( ) : Promise < number > {
311352 //Get's the client even if we don't need to use it. We call this so that it
312- //throws if it's not connected (and this.#blockTipHeight is erroneous)
353+ //throws if it's not connected (and this.#tipBlockHeight is erroneous)
313354 await this . #getClient( ) ;
314- if ( this . #blockTipHeight === undefined )
355+ if ( this . #tipBlockHeight === undefined )
315356 throw new Error (
316357 `Error: block tip height has not been retrieved yet. Probably not connected`
317358 ) ;
318- return this . #blockTipHeight ;
359+ return this . #tipBlockHeight ;
319360 }
320361
321362 /**
@@ -354,14 +395,14 @@ export class ElectrumExplorer implements Explorer {
354395 const transactionHistory = history . map ( ( { tx_hash, height } ) => {
355396 const txId = tx_hash ;
356397 const blockHeight : number = height || 0 ;
357- if ( blockHeight > this . #blockTipHeight ) {
398+ if ( blockHeight > this . #tipBlockHeight ) {
358399 console . warn (
359- `tx ${ tx_hash } block height ${ blockHeight } larger than the tip ${ this . #blockTipHeight } `
400+ `tx ${ tx_hash } block height ${ blockHeight } larger than the tip ${ this . #tipBlockHeight } `
360401 ) ;
361- this . #blockTipHeight = blockHeight ;
402+ this . #tipBlockHeight = blockHeight ;
362403 }
363404 const numConfirmations = blockHeight
364- ? this . #blockTipHeight - blockHeight + 1
405+ ? this . #tipBlockHeight - blockHeight + 1
365406 : 0 ;
366407 const irreversible = numConfirmations >= this . #irrevConfThresh;
367408 return { txId, blockHeight, irreversible } ;
0 commit comments