@ethereumjs/blockchain
Version:
A module to store and interact with blocks
368 lines • 17.4 kB
TypeScript
import { Block, BlockHeader } from '@ethereumjs/block';
import { Common } from '@ethereumjs/common';
import { EventEmitter } from 'eventemitter3';
import { DBManager } from './db/manager.ts';
import type { BigIntLike, DB, DBObject } from '@ethereumjs/util';
import type { BlockchainEvent, BlockchainInterface, BlockchainOptions, Consensus, OnBlock } from './types.ts';
/**
* Blockchain implementation to create and maintain a valid canonical chain
* of block headers or blocks with support for reorgs and the ability to provide
* custom DB backends.
*
* By default consensus validation is not provided since with the switch to
* Proof-of-Stake consensus is validated by the Ethereum consensus layer.
* If consensus validation is desired for Ethash or Clique blockchains the
* optional `consensusDict` option can be used to pass in validation objects.
*/
export declare class Blockchain implements BlockchainInterface {
db: DB<Uint8Array | string, Uint8Array | string | DBObject>;
dbManager: DBManager;
events: EventEmitter<BlockchainEvent>;
private _genesisBlock?; /** The genesis block of this blockchain */
private _customGenesisState?; /** Custom genesis state */
/**
* The following two heads and the heads stored within the `_heads` always point
* to a hash in the canonical chain and never to a stale hash.
* With the exception of `_headHeaderHash` this does not necessarily need to be
* the hash with the highest total difficulty.
*/
/** The hash of the current head block */
private _headBlockHash?;
/** The hash of the current head header */
private _headHeaderHash?;
/**
* A Map which stores the head of each key (for instance the "vm" key) which is
* updated along a {@link Blockchain.iterator} method run and can be used to (re)run
* non-verified blocks (for instance in the VM).
*/
private _heads;
private _lock;
readonly common: Common;
private _hardforkByHeadBlockNumber;
private readonly _validateBlocks;
private readonly _validateConsensus;
private _consensusDict;
/**
* This is used to track which canonical blocks are deleted. After a method calls
* `_deleteCanonicalChainReferences`, if this array has any items, the
* `deletedCanonicalBlocks` event is emitted with the array as argument.
*/
private _deletedBlocks;
private DEBUG;
private _debug;
/**
* Creates new Blockchain object.
*
* @deprecated The direct usage of this constructor is discouraged since
* non-finalized async initialization might lead to side effects. Please
* use the async {@link createBlockchain} constructor instead (same API).
*
* @param opts An object with the options that this constructor takes. See
* {@link BlockchainOptions}.
*/
constructor(opts?: BlockchainOptions);
private _consensusCheck;
/**
* Returns an eventual consensus object matching the current consensus algorithm from Common
* or undefined if non available
*/
get consensus(): Consensus | undefined;
/**
* Returns a deep copy of this {@link Blockchain} instance.
*
* Note: this does not make a copy of the underlying db
* since it is unknown if the source is on disk or in memory.
* This should not be a significant issue in most usage since
* the queries will only reflect the instance's known data.
* If you would like this copied blockchain to use another db
* set the {@link db} of this returned instance to a copy of
* the original.
*/
shallowCopy(): Blockchain;
/**
* Run a function after acquiring a lock. It is implied that we have already
* initialized the module (or we are calling this from the init function, like
* `_setCanonicalGenesisBlock`)
* @param action - function to run after acquiring a lock
* @hidden
*/
private runWithLock;
/**
* Returns the specified iterator head.
*
* This function replaces the old Blockchain.getHead() method. Note that
* the function deviates from the old behavior and returns the
* genesis hash instead of the current head block if an iterator
* has not been run. This matches the behavior of {@link Blockchain.iterator}.
*
* @param name - Optional name of the iterator head (default: 'vm')
*/
getIteratorHead(name?: string): Promise<Block>;
/**
* This method differs from `getIteratorHead`. If the head is not found, it returns `undefined`.
* @param name - Optional name of the iterator head (default: 'vm')
* @returns
*/
getIteratorHeadSafe(name?: string): Promise<Block | undefined>;
private getHead;
/**
* Returns the latest header in the canonical chain.
*/
getCanonicalHeadHeader(): Promise<BlockHeader>;
/**
* Returns the latest full block in the canonical chain.
*/
getCanonicalHeadBlock(): Promise<Block>;
/**
* Adds blocks to the blockchain.
*
* If an invalid block is met the function will throw, blocks before will
* nevertheless remain in the DB. If any of the saved blocks has a higher
* total difficulty than the current max total difficulty the canonical
* chain is rebuilt and any stale heads/hashes are overwritten.
* @param blocks - The blocks to be added to the blockchain
*/
putBlocks(blocks: Block[]): Promise<void>;
/**
* Adds a block to the blockchain.
*
* If the block is valid and has a higher total difficulty than the current
* max total difficulty, the canonical chain is rebuilt and any stale
* heads/hashes are overwritten.
* @param block - The block to be added to the blockchain
*/
putBlock(block: Block): Promise<void>;
/**
* Adds many headers to the blockchain.
*
* If an invalid header is met the function will throw, headers before will
* nevertheless remain in the DB. If any of the saved headers has a higher
* total difficulty than the current max total difficulty the canonical
* chain is rebuilt and any stale heads/hashes are overwritten.
* @param headers - The headers to be added to the blockchain
*/
putHeaders(headers: Array<any>): Promise<void>;
/**
* Adds a header to the blockchain.
*
* If this header is valid and it has a higher total difficulty than the current
* max total difficulty, the canonical chain is rebuilt and any stale
* heads/hashes are overwritten.
* @param header - The header to be added to the blockchain
*/
putHeader(header: BlockHeader): Promise<void>;
/**
* Resets the canonical chain to canonicalHead number
*
* This updates the head hashes (if affected) to the hash corresponding to
* canonicalHead and cleans up canonical references greater than canonicalHead
* @param canonicalHead - The number to which chain should be reset to
*/
resetCanonicalHead(canonicalHead: bigint): Promise<void>;
/**
* Entrypoint for putting any block or block header. Verifies this block,
* checks the total TD: if this TD is higher than the current highest TD, we
* have thus found a new canonical block and have to rewrite the canonical
* chain. This also updates the head block hashes. If any of the older known
* canonical chains just became stale, then we also reset every _heads header
* which points to a stale header to the last verified header which was in the
* old canonical chain, but also in the new canonical chain. This thus rolls
* back these headers so that these can be updated to the "new" canonical
* header using the iterator method.
* @hidden
*/
private _putBlockOrHeader;
/**
* Validates a block header, throwing if invalid. It is being validated against the reported `parentHash`.
* It verifies the current block against the `parentHash`:
* - The `parentHash` is part of the blockchain (it is a valid header)
* - Current block number is parent block number + 1
* - Current block has a strictly higher timestamp
* - Additional PoW checks ->
* - Current block has valid difficulty and gas limit
* - In case that the header is an uncle header, it should not be too old or young in the chain.
* - Additional PoA clique checks ->
* - Checks on coinbase and mixHash
* - Current block has a timestamp diff greater or equal to PERIOD
* - Current block has difficulty correctly marked as INTURN or NOTURN
* @param header - header to be validated
* @param height - If this is an uncle header, this is the height of the block that is including it
*/
validateHeader(header: BlockHeader, height?: bigint): Promise<void>;
/**
* Validates a block, by validating the header against the current chain, any uncle headers, and then
* whether the block is internally consistent
* @param block block to be validated
*/
validateBlock(block: Block): Promise<void>;
/**
* The following rules are checked in this method:
* Uncle Header is a valid header.
* Uncle Header is an orphan, i.e. it is not one of the headers of the canonical chain.
* Uncle Header has a parentHash which points to the canonical chain. This parentHash is within the last 7 blocks.
* Uncle Header is not already included as uncle in another block.
* @param block - block for which uncles are being validated
*/
private _validateUncleHeaders;
/**
* Gets a block by its hash or number. If a number is provided, the returned
* block will be the canonical block at that number in the chain
*
* @param blockId - The block's hash or number. If a hash is provided, then
* this will be immediately looked up, otherwise it will wait until we have
* unlocked the DB
*/
getBlock(blockId: Uint8Array | number | bigint): Promise<Block>;
/**
* Gets total difficulty for a block specified by hash and number
*/
getTotalDifficulty(hash: Uint8Array, number?: bigint): Promise<bigint>;
/**
* Gets total difficulty for a header's parent, helpful for determining terminal block
* @param header - Block header whose parent td is desired
*/
getParentTD(header: BlockHeader): Promise<bigint>;
/**
* Looks up many blocks relative to blockId Note: due to `GetBlockHeaders
* (0x03)` (ETH wire protocol) we have to support skip/reverse as well.
* @param blockId - The block's hash or number
* @param maxBlocks - Max number of blocks to return
* @param skip - Number of blocks to skip apart
* @param reverse - Fetch blocks in reverse
*/
getBlocks(blockId: Uint8Array | bigint | number, maxBlocks: number, skip: number, reverse: boolean): Promise<Block[]>;
/**
* Given an ordered array, returns an array of hashes that are not in the
* blockchain yet. Uses binary search to find out what hashes are missing.
* Therefore, the array needs to be ordered upon number.
* @param hashes - Ordered array of hashes (ordered on `number`).
*/
selectNeededHashes(hashes: Array<Uint8Array>): Promise<Uint8Array[]>;
/**
* Completely deletes a block from the blockchain including any references to
* this block. If this block was in the canonical chain, then also each child
* block of this block is deleted Also, if this was a canonical block, each
* head header which is part of this now stale chain will be set to the
* parentHeader of this block An example reason to execute is when running the
* block in the VM invalidates this block: this will then reset the canonical
* head to the past block (which has been validated in the past by the VM, so
* we can be sure it is correct).
* @param blockHash - The hash of the block to be deleted
*/
delBlock(blockHash: Uint8Array): Promise<void>;
/**
* @hidden
*/
private _delBlock;
/**
* Updates the `DatabaseOperation` list to delete a block from the DB,
* identified by `blockHash` and `blockNumber`. Deletes fields from `Header`,
* `Body`, `HashToNumber` and `TotalDifficulty` tables. If child blocks of
* this current block are in the canonical chain, delete these as well. Does
* not actually commit these changes to the DB. Sets `_headHeaderHash` and
* `_headBlockHash` to `headHash` if any of these matches the current child to
* be deleted.
* @param blockHash - the block hash to delete
* @param blockNumber - the number corresponding to the block hash
* @param headHash - the current head of the chain (if null, do not update
* `_headHeaderHash` and `_headBlockHash`)
* @param ops - the `DatabaseOperation` list to add the delete operations to
* @hidden
*/
private _delChild;
/**
* Iterates through blocks starting at the specified iterator head and calls
* the onBlock function on each block. The current location of an iterator
* head can be retrieved using {@link Blockchain.getIteratorHead}.
*
* @param name - Name of the state root head
* @param onBlock - Function called on each block with params (block, reorg)
* @param maxBlocks - How many blocks to run. By default, run all unprocessed blocks in the canonical chain.
* @param releaseLockOnCallback - Do not lock the blockchain for running the callback (default: `false`)
* @returns number of blocks actually iterated
*/
iterator(name: string, onBlock: OnBlock, maxBlocks?: number, releaseLockOnCallback?: boolean): Promise<number>;
/**
* Set header hash of a certain `tag`.
* When calling the iterator, the iterator will start running the first child block after the header hash currently stored.
* @param tag - The tag to save the headHash to
* @param headHash - The head hash to save
*/
setIteratorHead(tag: string, headHash: Uint8Array): Promise<void>;
/**
* Find the common ancestor of the new block and the old block.
* @param newHeader - the new block header
*/
private findCommonAncestor;
/**
* Pushes DB operations to delete canonical number assignments for specified
* block number and above. This only deletes `NumberToHash` references and not
* the blocks themselves. Note: this does not write to the DB but only pushes
* to a DB operations list.
* @param blockNumber - the block number from which we start deleting
* canonical chain assignments (including this block)
* @param headHash - the hash of the current canonical chain head. The _heads
* reference matching any hash of any of the deleted blocks will be set to
* this
* @param ops - the DatabaseOperation list to write DatabaseOperations to
* @hidden
*/
private _deleteCanonicalChainReferences;
/**
* Given a `header`, put all operations to change the canonical chain directly
* into `ops`. This walks the supplied `header` backwards. It is thus assumed
* that this header should be canonical header. For each header the
* corresponding hash corresponding to the current canonical chain in the DB
* is checked. If the number => hash reference does not correspond to the
* reference in the DB, we overwrite this reference with the implied number =>
* hash reference Also, each `_heads` member is checked; if these point to a
* stale hash, then the hash which we terminate the loop (i.e. the first hash
* which matches the number => hash of the implied chain) is put as this stale
* head hash. The same happens to _headBlockHash.
* @param header - The canonical header.
* @param ops - The database operations list.
* @hidden
*/
private _rebuildCanonical;
/**
* Builds the `DatabaseOperation[]` list which describes the DB operations to
* write the heads, head header hash and the head header block to the DB
* @hidden
*/
private _saveHeadOps;
/**
* Gets the `DatabaseOperation[]` list to save `_heads`, `_headHeaderHash` and
* `_headBlockHash` and writes these to the DB
* @hidden
*/
private _saveHeads;
/**
* Gets a header by hash and number. Header can exist outside the canonical
* chain
*
* @hidden
*/
private _getHeader;
checkAndTransitionHardForkByNumber(number: BigIntLike, timestamp?: BigIntLike): Promise<void>;
/**
* Gets a header by number. Header must be in the canonical chain
*/
getCanonicalHeader(number: bigint): Promise<BlockHeader>;
/**
* This method either returns a Uint8Array if there exists one in the DB or if it
* does not exist then return false If DB throws
* any other error, this function throws.
* @param number
*/
safeNumberToHash(number: bigint): Promise<Uint8Array | false>;
/**
* The genesis {@link Block} for the blockchain.
*/
get genesisBlock(): Block;
/**
* Creates a genesis {@link Block} for the blockchain with params from {@link Common.genesis}
* @param stateRoot The genesis stateRoot
*/
createGenesisBlock(stateRoot: Uint8Array): Block;
}
//# sourceMappingURL=blockchain.d.ts.map