Merge branch '70-feat-blockchain-add-method-to-get-the-current-blockheight' into 'development'
Resolve "feat(blockchain): Add method to get the current blockheight" Closes #70 See merge request GeneralProtocols/xo/stack!107
This commit is contained in:
@@ -5,6 +5,7 @@ import {
|
||||
type BlockchainUTXOs,
|
||||
type BlockchainTransactions,
|
||||
type BlockchainUnsubscribeCallback,
|
||||
type BlockchainChainTip,
|
||||
} from './types.js';
|
||||
|
||||
import { BlockHeader, Transaction, TransactionHash } from '@xocash/primitives';
|
||||
@@ -89,6 +90,14 @@ export abstract class BaseBlockchain<
|
||||
*/
|
||||
abstract fetchBlockHeader(blockHeight: number): Promise<BlockHeader>;
|
||||
|
||||
/**
|
||||
* Retrieves the current chain tip block.
|
||||
*
|
||||
* @returns {Promise<BlockchainChainTip>} A promise that resolves with the current chain tip
|
||||
* @throws {Error} If the chain tip cannot be retrieved
|
||||
*/
|
||||
abstract fetchChainTip(): Promise<BlockchainChainTip>;
|
||||
|
||||
/**
|
||||
* Retrieves detailed information about a specific transaction.
|
||||
*
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
fetchHistory,
|
||||
fetchTransaction,
|
||||
subscribeToBlockheaderUpdates,
|
||||
fetchCurrentChainTip,
|
||||
} from '@electrum-cash/protocol';
|
||||
|
||||
import { hexToBin } from '@bitauth/libauth';
|
||||
@@ -181,8 +182,11 @@ export class BlockchainElectrum extends BaseBlockchain<
|
||||
this.handleTransactionStatusUpdates.bind(this),
|
||||
);
|
||||
|
||||
// Subscribe to block header notifications.
|
||||
await subscribeToBlockheaderUpdates(this.electrumClient);
|
||||
// Subscribe to block header notifications and fetch the chain tip.
|
||||
await Promise.all([
|
||||
subscribeToBlockheaderUpdates(this.electrumClient),
|
||||
this.fetchChainTip(),
|
||||
]);
|
||||
|
||||
// Return to prevent further execution.
|
||||
return;
|
||||
@@ -338,6 +342,37 @@ export class BlockchainElectrum extends BaseBlockchain<
|
||||
return blockHeader;
|
||||
}
|
||||
|
||||
// TODO: ElectrumClient provides a chainHeight property that is populated after a `blockchain.headers.subscribe` call.
|
||||
// We may be able to leverage this side-effect to avoid fetching the chain tip.
|
||||
async fetchChainTip() {
|
||||
// Wait for connection to be established before attempting to subscribe.
|
||||
// TODO: Consider doing this at a higher level.
|
||||
if (!this.isConnected) {
|
||||
await this.waitFor('isConnectedUpdated', (isConnected) => isConnected);
|
||||
}
|
||||
|
||||
// If we don't currently have a block height, fetch the current chain tip.
|
||||
// NOTE: We don't use our cache here because the key will be the same and so on-boot, we will have a stale value.
|
||||
if (!this.blockHeight) {
|
||||
const chainTip = await fetchCurrentChainTip(this.electrumClient);
|
||||
|
||||
this.blockHeight = chainTip.height;
|
||||
}
|
||||
|
||||
// Get the block header
|
||||
// NOTE: If we didn't have the block height, we are performing double-fetch here
|
||||
const blockHeader = await this.fetchBlockHeader(this.blockHeight);
|
||||
|
||||
// Create a chain tip object.
|
||||
const chainTip = {
|
||||
height: this.blockHeight,
|
||||
blockHeader: blockHeader,
|
||||
};
|
||||
|
||||
// Return the chain tip.
|
||||
return chainTip;
|
||||
}
|
||||
|
||||
async broadcastTransaction(
|
||||
tx: Transaction | Uint8Array | string,
|
||||
opts: BroadcastOpts = { timeoutMs: 10_000, waitUntilSeen: true },
|
||||
@@ -546,13 +581,19 @@ export class BlockchainElectrum extends BaseBlockchain<
|
||||
return response;
|
||||
}
|
||||
|
||||
protected handleNewBlockchainHeaders(
|
||||
protected async handleNewBlockchainHeaders(
|
||||
notification: HeadersSubscribeNotification,
|
||||
) {
|
||||
// Extract height from the first param.
|
||||
// Extract height and hex from the first param.
|
||||
// NOTE: This is an object.
|
||||
// https://electrum-cash-protocol.readthedocs.io/en/latest/protocol-methods.html#blockchain-headers-subscribe
|
||||
const { height } = notification.params[0];
|
||||
const { height, hex } = notification.params[0];
|
||||
|
||||
// Decode the raw data into our Block Header primitive.
|
||||
const blockHeader = BlockHeader.fromHex(hex);
|
||||
|
||||
// Update the cache with the new block header.
|
||||
await this.store.set(`blockHeader.${height}`, blockHeader.toRaw());
|
||||
|
||||
// Assign the block height.
|
||||
this.blockHeight = height;
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
type BlockchainTransactions,
|
||||
type BlockchainUTXO,
|
||||
type BlockchainUTXOs,
|
||||
type BlockchainChainTip,
|
||||
} from './types.js';
|
||||
|
||||
import {
|
||||
@@ -103,6 +104,12 @@ export class BlockchainInMemory extends BaseBlockchain {
|
||||
return blockHeader;
|
||||
}
|
||||
|
||||
// TODO: Implement a method for fetching the chain tip.
|
||||
// This is a placeholder for now.
|
||||
async fetchChainTip(): Promise<BlockchainChainTip> {
|
||||
throw new Error('Not implemented yet');
|
||||
}
|
||||
|
||||
async broadcastTransaction(
|
||||
tx: Transaction | Uint8Array | string,
|
||||
opts: { skipValidation: boolean } = { skipValidation: false },
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { Transaction, TransactionHash, UTXO, ExtMap } from '@xocash/primitives';
|
||||
import {
|
||||
Transaction,
|
||||
TransactionHash,
|
||||
UTXO,
|
||||
ExtMap,
|
||||
BlockHeader,
|
||||
} from '@xocash/primitives';
|
||||
|
||||
export type BlockchainCallback<Payload = undefined> = (
|
||||
/** The transaction hash, address, lockscript or scriptHash of this subscription. */
|
||||
@@ -43,3 +49,8 @@ export type BlockHeightPayload = {
|
||||
type: 'block-height';
|
||||
height: number | null;
|
||||
};
|
||||
|
||||
export type BlockchainChainTip = {
|
||||
height: number;
|
||||
blockHeader: BlockHeader;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user