@bsv/wallet-toolbox
Version:
BRC100 conforming wallet, wallet storage and wallet signer components
338 lines (337 loc) • 13.4 kB
TypeScript
import { HeightRange } from '../util/HeightRange';
import { BaseBlockHeader, BlockHeader, LiveBlockHeader } from './BlockHeaderApi';
import { Chain } from '../../../../sdk/types';
import { BulkHeaderFileInfo } from '../util/BulkHeaderFile';
import { BulkFileDataManager } from '../util/BulkFileDataManager';
export interface ChaintracksStorageBaseOptions {
/**
* Which chain is being tracked: main, test, or stn.
*/
chain: Chain;
/**
* How much of recent history is required to be kept in "live" block header storage.
*
* Headers with height less than active chain tip height minus `liveHeightThreshold`
* are not required to be kept in "live" storage and may be migrated to "bulk" storage.
*
* As no forks, orphans, or reorgs can affect "bulk" block header storage, an
* aggressively high number is recommended: At least an order of magnitude more than
* the deepest actual reorg you can imagine.
*/
liveHeightThreshold: number;
/**
* How much of recent history must be processed with full validation and reorg support.
*
* Must be less than or equal to `liveHeightThreshold`.
*
* Headers with height older than active chain tip height minus `reorgHeightThreshold`
* may use batch processing when ingesting headers.
*/
reorgHeightThreshold: number;
/**
* How many excess "live" headers to accumulate before migrating them as a chunk to the
* bulk header storage.
*/
bulkMigrationChunkSize: number;
/**
* Maximum number of headers per call to batchInsert
*/
batchInsertLimit: number;
/**
* Controls in memory caching and retrieval of missing bulk header data.
*/
bulkFileDataManager: BulkFileDataManager | undefined;
}
export interface ChaintracksStorageQueryApi {
log: (...args: any[]) => void;
/**
* Returns the active chain tip header
* Throws an error if there is no tip.
*/
findChainTipHeader(): Promise<LiveBlockHeader>;
/**
* Returns the block hash of the active chain tip.
*/
findChainTipHash(): Promise<string>;
/**
* Returns the active chain tip header or undefined if there is no tip.
*/
findChainTipHeaderOrUndefined(): Promise<LiveBlockHeader | undefined>;
/**
* Returns the chainWork value of the active chain tip
*/
findChainTipWork(): Promise<string>;
/**
* Returns block header for a given block height on active chain.
* @param hash block hash
*/
findHeaderForHeight(height: number): Promise<LiveBlockHeader | BlockHeader>;
/**
* Returns block header for a given block height on active chain.
* @param hash block hash
*/
findHeaderForHeightOrUndefined(height: number): Promise<LiveBlockHeader | BlockHeader | undefined>;
/**
* Given two chain tip headers in a chain reorg scenario,
* return their common ancestor header.
* @param header1 First header in live part of the chain.
* @param header2 Second header in live part of the chain.
*/
findCommonAncestor(header1: LiveBlockHeader, header2: LiveBlockHeader): Promise<LiveBlockHeader>;
/**
* This is an original API. Proposed deprecation in favor of `findCommonAncestor`
* Given two headers that are both chain tips in a reorg scenario, returns
* the depth of the reorg (the greater of the heights of the two provided
* headers, minus the height of their last common ancestor)
*/
findReorgDepth(header1: LiveBlockHeader, header2: LiveBlockHeader): Promise<number>;
/**
* Returns true if the given merkleRoot is found in a block header on the active chain.
* @param merkleRoot of block header
*/
isMerkleRootActive(merkleRoot: string): Promise<boolean>;
/**
* Returns serialized headers as a Uint8Array.
* Only adds bulk and active live headers.
*
* @param height is the minimum header height to return, must be >= zero.
* @param count height + count - 1 is the maximum header height to return.
* @returns serialized headers as a Uint8Array.
*/
getHeadersUint8Array(height: number, count: number): Promise<Uint8Array>;
/**
* Returns an array of deserialized headers.
* Only adds bulk and active live headers.
*
* @param height is the minimum header height to return, must be >= zero.
* @param count height + count - 1 is the maximum header height to return.
* @returns array of deserialized headers
*/
getHeaders(height: number, count: number): Promise<BaseBlockHeader[]>;
/**
* Returns active `LiveBlockHeaders` with height in the given range.
*
* @param range
* @returns array of active `LiveBlockHeaders`
*/
getLiveHeaders(range: HeightRange): Promise<LiveBlockHeader[]>;
/**
* Returns serialized bulk headers in the given range.
*
* @param range
* @returns serialized headers as a Uint8Array.
*/
getBulkHeaders(range: HeightRange): Promise<Uint8Array>;
/**
* Returns block header for a given block height on active chain.
* @param hash block hash
*/
findLiveHeaderForHeight(height: number): Promise<LiveBlockHeader | null>;
/**
* Returns block header for a given headerId.
* Only from the "live" portion of the chain.
* @param headerId
*/
findLiveHeaderForHeaderId(headerId: number): Promise<LiveBlockHeader>;
/**
* Returns block header for a given block hash.
* Only from the "live" portion of the chain.
* Returns null if not found.
* @param hash block hash
*/
findLiveHeaderForBlockHash(hash: string): Promise<LiveBlockHeader | null>;
/**
* Returns block header for a given merkleRoot.
* Only from the "live" portion of the chain.
* @param merkleRoot
*/
findLiveHeaderForMerkleRoot(merkleRoot: string): Promise<LiveBlockHeader | null>;
/**
* Returns the height range of both bulk and live storage.
* Verifies that the ranges meet these requirements:
* - Both may be empty.
* - If bulk is empty, live must be empty or start with height zero.
* - If bulk is not empty it must start with height zero.
* - If bulk is not empty and live is not empty, live must start with the height after bulk.
*/
getAvailableHeightRanges(): Promise<{
bulk: HeightRange;
live: HeightRange;
}>;
/**
* @returns The current minimum and maximum height active LiveBlockHeaders in the "live" database.
*/
findLiveHeightRange(): Promise<HeightRange>;
/**
* @returns The maximum headerId value used by existing records or -1 if there are none.
*/
findMaxHeaderId(): Promise<number>;
/**
* Which chain is being tracked: "main" or "test".
*/
chain: Chain;
/**
* How much of recent history is required to be kept in "live" block header storage.
*
* Headers with height older than active chain tip height minus `liveHeightThreshold`
* are not required to be kept in "live" storage and may be migrated to "bulk" storage.
*/
liveHeightThreshold: number;
/**
* How much of recent history must be processed with full validation and reorg support.
*
* May be less than `liveHeightThreshold`.
*
* Headers with height older than active chain tip height minus ``
* may use batch processing when ingesting headers.
*/
reorgHeightThreshold: number;
/**
* How many excess "live" headers to accumulate before migrating them as a chunk to the
* bulk header storage.
*/
bulkMigrationChunkSize: number;
/**
* Maximum number of headers per call to batchInsert
*/
batchInsertLimit: number;
}
export type InsertHeaderResult = {
/**
* true only if the new header was inserted
*/
added: boolean;
/**
* true only if the header was not inserted because a matching hash already exists in the database.
*/
dupe: boolean;
/**
* true only if the new header became the active chain tip.
*/
isActiveTip: boolean;
/**
* zero if the insertion of the new header did not cause a reorg.
* If isActiveTip is true, and priorTip is not the new headers previous header,
* then the minimum height difference from the common active ancestor to this header (new tip) and priorTip.
*/
reorgDepth: number;
/**
* If `added` is true, this header was the active chain tip before the insert. It may or may not still be the active chain tip after the insert.
*/
priorTip: LiveBlockHeader | undefined;
/**
* If a reorg has occurred, these headers where active and are now deactivated.
*/
deactivatedHeaders: LiveBlockHeader[];
/**
* header's previousHash was not found in database
*/
noPrev: boolean;
/**
* header matching previousHash does not have height - 1
*/
badPrev: boolean;
/**
* an active ancestor was not found in live storage or prev header.
*/
noActiveAncestor: boolean;
/**
* a current chain tip was not found in live storage or prev header.
*/
noTip: boolean;
};
export interface ChaintracksStorageBulkFileApi {
insertBulkFile(file: BulkHeaderFileInfo): Promise<number>;
updateBulkFile(fileId: number, file: BulkHeaderFileInfo): Promise<number>;
deleteBulkFile(fileId: number): Promise<number>;
getBulkFiles(): Promise<BulkHeaderFileInfo[]>;
getBulkFileData(fileId: number, offset?: number, length?: number): Promise<Uint8Array | undefined>;
}
export interface ChaintracksStorageIngestApi {
log: (...args: any[]) => void;
/**
* Attempts to insert a block header into the chain.
*
* Returns 'added' false and 'dupe' true if header's hash already exists in the live database
* Returns 'added' false and 'dupe' false if header's previousHash wasn't found in the live database, or height doesn't increment previous' height.
*
* Computes the header's chainWork from its bits and the previous header's chainWork.
*
* Returns 'added' true if the header was added to the live database.
* Returns 'isActiveTip' true if header's chainWork is greater than current active chain tip's chainWork.
*
* If the addition of this header caused a reorg (did not directly extend old active chain tip):
* Returns 'reorgDepth' the minimum height difference of the common ancestor to the two chain tips.
* Returns 'priorTip' the old active chain tip.
* If not a reorg:
* Returns 'reorgDepth' of zero.
* Returns 'priorTip' the active chain tip before this insert. May be unchanged.
*
* Implementation must call `pruneLiveBlockHeaders` after adding new header.
*
* @param header to insert
* @param prev if not undefined, the last bulk storage header with total bulk chainWork
*/
insertHeader(header: BlockHeader, prev?: LiveBlockHeader): Promise<InsertHeaderResult>;
/**
* Must be called after the addition of new LiveBlockHeaders.
*
* Checks the `StorageEngine` configuration options to see
* if BulkStorage is configured and if there is at least one
* `bulkMigrationChunkSize` woth of headers in excess of
* `liveHeightThreshold` available.
*
* If yes, then calls `migrateLiveToBulk` one or more times.
* @param activeTipHeight height of active tip after adds
*/
pruneLiveBlockHeaders(activeTipHeight: number): Promise<void>;
/**
* Migrates the oldest `count` LiveBlockHeaders to BulkStorage.
* BulkStorage must be configured.
* `count` must not exceed `bulkMigrationChunkSize`.
* `count` must leave at least `liveHeightThreshold` LiveBlockHeaders.
*
* @param count
*
* Steps:
* - Copy count oldest active LiveBlockHeaders from live database to buffer.
* - Append the buffer of headers to BulkStorage
* - Add the buffer's BlockHash, Height pairs to corresponding index table.
* - Add the buffer's MerkleRoot, Height pairs to corresponding index table.
* - Delete the records from the live database.
*/
migrateLiveToBulk(count: number): Promise<void>;
/**
* Delete live headers with height less or equal to `maxHeight`
* after they have been migrated to bulk storage.
*
* @param maxHeight delete all records with less or equal `height`
*/
deleteOlderLiveBlockHeaders(maxHeight: number): Promise<number>;
/**
* Async initialization method.
*
* May be called prior to other async methods to control when initialization occurs.
*/
makeAvailable(): Promise<void>;
/**
* Migrate storage schema to latest schema changes.
*
* Typically invoked automatically by `makeAvailable`.
*/
migrateLatest(): Promise<void>;
dropAllData(): Promise<void>;
/**
* Release all resources. Makes the instance unusable.
*/
destroy(): Promise<void>;
}
export interface ChaintracksStorageApi extends ChaintracksStorageQueryApi, ChaintracksStorageIngestApi {
log: (...args: any[]) => void;
bulkManager: BulkFileDataManager;
/**
* Close and release all resources.
*/
destroy(): Promise<void>;
}
//# sourceMappingURL=ChaintracksStorageApi.d.ts.map