UNPKG

@bsv/sdk

Version:

BSV Blockchain Software Development Kit

139 lines (125 loc) 4.28 kB
import ChainTracker from '../ChainTracker.js' import { HttpClient } from '../http/HttpClient.js' import { defaultHttpClient } from '../http/DefaultHttpClient.js' /** Configuration options for the BlockHeadersService ChainTracker. */ export interface BlockHeadersServiceConfig { /** The HTTP client used to make requests to the API. */ httpClient?: HttpClient /** The API key used to authenticate requests to the BlockHeadersService API. */ apiKey?: string } interface MerkleRootVerificationRequest { blockHeight: number merkleRoot: string } interface MerkleRootConfirmation { blockHash: string blockHeight: number merkleRoot: string confirmation: 'CONFIRMED' | 'UNCONFIRMED' } interface MerkleRootVerificationResponse { confirmationState: 'CONFIRMED' | 'UNCONFIRMED' confirmations: MerkleRootConfirmation[] } /** * Represents a chain tracker based on a BlockHeadersService API. * * @example * ```typescript * const chainTracker = new BlockHeadersService('https://headers.spv.money', { * apiKey: '17JxRHcJerGBEbusx56W8o1m8Js73TFGo' * }) * ``` */ export class BlockHeadersService implements ChainTracker { protected readonly baseUrl: string protected readonly httpClient: HttpClient protected readonly apiKey: string /** * Constructs an instance of the BlockHeadersService ChainTracker. * * @param {string} baseUrl - The base URL for the BlockHeadersService API (e.g. https://headers.spv.money) * @param {BlockHeadersServiceConfig} config - Configuration options for the BlockHeadersService ChainTracker. */ constructor( baseUrl: string, config: BlockHeadersServiceConfig = {} ) { const { httpClient, apiKey } = config this.baseUrl = baseUrl this.httpClient = httpClient ?? defaultHttpClient() this.apiKey = apiKey ?? '' } /** * Verifies if a given merkle root is valid for a specific block height. * * @param {string} root - The merkle root to verify. * @param {number} height - The block height to check against. * @returns {Promise<boolean>} - A promise that resolves to true if the merkle root is valid for the specified block height, false otherwise. */ async isValidRootForHeight(root: string, height: number): Promise<boolean> { const requestOptions = { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'Authorization': `Bearer ${this.apiKey}` }, data: [ { blockHeight: height, merkleRoot: root } ] as MerkleRootVerificationRequest[] } try { const response = await this.httpClient.request<MerkleRootVerificationResponse>( `${this.baseUrl}/api/v1/chain/merkleroot/verify`, requestOptions ) if (response.ok) { return response.data.confirmationState === 'CONFIRMED' } else { throw new Error( `Failed to verify merkleroot for height ${height} because of an error: ${JSON.stringify(response.data)}` ) } } catch (error) { throw new Error( `Failed to verify merkleroot for height ${height} because of an error: ${error instanceof Error ? error.message : String(error)}` ) } } /** * Gets the current block height from the BlockHeadersService API. * * @returns {Promise<number>} - A promise that resolves to the current block height. */ async currentHeight(): Promise<number> { const requestOptions = { method: 'GET', headers: { 'Accept': 'application/json', 'Authorization': `Bearer ${this.apiKey}` } } try { const response = await this.httpClient.request<{ height: number }>( `${this.baseUrl}/api/v1/chain/tip/longest`, requestOptions ) if (response.ok && response.data && typeof response.data.height === 'number') { return response.data.height } else { throw new Error( `Failed to get current height because of an error: ${JSON.stringify(response.data)}` ) } } catch (error) { throw new Error( `Failed to get current height because of an error: ${error instanceof Error ? error.message : String(error)}` ) } } }