Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@ethereumjs/blockchain

Package Overview
Dependencies
Maintainers
4
Versions
35
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ethereumjs/blockchain - npm Package Compare versions

Comparing version 6.3.0 to 7.0.0-rc.1

dist/cjs/blockchain.d.ts

52

package.json
{
"name": "@ethereumjs/blockchain",
"version": "6.3.0",
"version": "7.0.0-rc.1",
"description": "A module to store and interact with blocks",

@@ -19,4 +19,11 @@ "keywords": [

"author": "mjbecze <mjbecze@gmail.com>",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"type": "commonjs",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"exports": {
".": {
"import": "./dist/esm/index.js",
"require": "./dist/cjs/index.js"
}
},
"files": [

@@ -29,4 +36,5 @@ "dist",

"clean": "../../config/cli/clean-package.sh",
"coverage": "../../config/cli/coverage.sh",
"docs:build": "typedoc --options typedoc.js",
"coverage": "npx vitest run --coverage.enabled --coverage.reporter=lcov",
"docs:build": "typedoc --options typedoc.cjs",
"examples": "ts-node ../../scripts/examples-runner.ts -- blockchain",
"lint": "../../config/cli/lint.sh",

@@ -36,31 +44,23 @@ "lint:diff": "../../config/cli/lint-diff.sh",

"prepublishOnly": "../../config/cli/prepublish.sh",
"tape": "tape -r ts-node/register",
"test": "npm run test:node && npm run test:browser",
"test:browser": "karma start karma.conf.js",
"test:node": "npm run tape -- ./test/*.spec.ts",
"test:browser": "npx vitest run --browser.name=webkit --browser.provider=playwright --browser.headless",
"test:node": "npx vitest run",
"tsc": "../../config/cli/ts-compile.sh"
},
"dependencies": {
"@ethereumjs/block": "^4.3.0",
"@ethereumjs/common": "^3.2.0",
"@ethereumjs/ethash": "^2.1.0",
"@ethereumjs/rlp": "^4.0.1",
"@ethereumjs/trie": "^5.1.0",
"@ethereumjs/tx": "^4.2.0",
"@ethereumjs/util": "^8.1.0",
"abstract-level": "^1.0.3",
"@ethereumjs/block": "5.0.0-rc.1",
"@ethereumjs/common": "4.0.0-rc.1",
"@ethereumjs/ethash": "3.0.0-rc.1",
"@ethereumjs/rlp": "5.0.0-rc.1",
"@ethereumjs/trie": "6.0.0-rc.1",
"@ethereumjs/tx": "5.0.0-rc.1",
"@ethereumjs/util": "9.0.0-rc.1",
"debug": "^4.3.3",
"ethereum-cryptography": "^2.0.0",
"level": "^8.0.0",
"lru-cache": "^5.1.1",
"memory-level": "^1.0.0"
"ethereum-cryptography": "^2.1.2",
"lru-cache": "^10.0.0"
},
"devDependencies": {
"@types/async": "^2.4.1",
"@types/level-errors": "^3.0.0",
"@types/lru-cache": "^5.1.0"
},
"devDependencies": {},
"engines": {
"node": ">=14"
"node": ">=18"
}
}

@@ -9,2 +9,4 @@ # @ethereumjs/blockchain

Note: this README has been updated containing the changes from our next breaking release round [UNRELEASED] targeted for Summer 2023. See the README files from the [maintenance-v6](https://github.com/ethereumjs/ethereumjs-monorepo/tree/maintenance-v6/) branch for documentation matching our latest releases.
| A module to store and interact with blocks. |

@@ -37,22 +39,18 @@ | ------------------------------------------- |

The following is an example to iterate through an existing Geth DB (needs `level` to be installed separately).
The following is an example to instantiate a simple Blockchain object, put blocks into the blockchain and then iterate through the blocks added:
This module performs write operations. Making a backup of your data before trying it is recommended. Otherwise, you can end up with a compromised DB state.
```typescript
import { Blockchain } from '@ethereumjs/blockchain'
import { Chain, Common } from '@ethereumjs/common'
import { bytesToHex } from '@ethereumjs/util'
const { Level } = require('level')
const gethDbPath = './chaindata' // Add your own path here. It will get modified, see remarks.
const common = new Common({ chain: Chain.Ropsten })
const db = new Level(gethDbPath)
// Use the safe static constructor which awaits the init method
const blockchain = Blockchain.create({ common, db })
// See @ethereumjs/block on how to create a block
await blockchain.putBlock(block1)
await blockchain.putBlock(block2)
blockchain.iterator('i', (block) => {
const blockNumber = block.header.number.toString()
const blockHash = block.hash().toString('hex')
const blockHash = bytesToHex(block.hash())
console.log(`Block ${blockNumber}: ${blockHash}`)

@@ -62,4 +60,10 @@ })

**WARNING**: Since `@ethereumjs/blockchain` is also doing write operations on the DB for safety reasons only run this on a copy of your database, otherwise this might lead to a compromised DB state.
### Database Abstraction / Removed LevelDB Dependency
With the v7 release the Blockchain library database has gotten an additional abstraction layer which allows to switch the backend to whatever is fitting the best for a use case, see PR [#2669](https://github.com/ethereumjs/ethereumjs-monorepo/pull/2669) and PR [#2673](https://github.com/ethereumjs/ethereumjs-monorepo/pull/2673). The database just needs to conform to the new [DB](https://github.com/ethereumjs/ethereumjs-monorepo/blob/master/packages/util/src/db.ts) interface provided in the `@ethereumjs/util` package (since this is used in other places as well).
By default the blockchain package is now using a [MapDB](https://github.com/ethereumjs/ethereumjs-monorepo/blob/master/packages/util/src/mapDB.ts) non-persistent data storage which is also generically provided in the `@ethereumjs/util` package.
If you need a persistent data store for your use case you can consider using the wrapper we have written within our [client](https://github.com/ethereumjs/ethereumjs-monorepo/blob/master/packages/client/src/execution/level.ts) library.
### Consensus

@@ -75,3 +79,3 @@

#### Custom Conensus Algorithms
#### Custom Consensus Algorithms

@@ -84,4 +88,18 @@ Also part of V6, you can also create a custom consensus class implementing the above interface and pass it into the `Blockchain` constructor using the `consensus` option at instantiation. See [this test script](https://github.com/ethereumjs/ethereumjs-monorepo/blob/master/packages/blockchain/test/customConsensus.spec.ts) for a complete example of how write and use a custom consensus implementation.

Starting with v6 responsibility for setting up a custom genesis state moved from the [Common](../common/) library to the `Blockchain` package, see PR [#1924](https://github.com/ethereumjs/ethereumjs-monorepo/pull/1924) for some work context.
### Genesis in v7 (removed genesis dependency)
Genesis state was huge and had previously been bundled with the `Blockchain` package with the burden going over to the VM, since `Blockchain` is a dependency.
Starting with the v7 release genesis state has been removed from `blockchain` and moved into its own auxiliary package [@ethereumjs/genesis](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/genesis), from which it can be included if needed (for most - especially VM - use cases it is not necessary), see PR [#2844](https://github.com/ethereumjs/ethereumjs-monorepo/pull/2844).
This goes along with some changes in Blockchain and VM API:
- Blockchain: There is a new constructor option `genesisStateRoot` beside `genesisBlock` and `genesisState` for an alternative condensed way to provide the genesis state root directly
- Blockchain: `genesisState(): GenesisState` method has been replaced by the async `getGenesisStateRoot(chainId: Chain): Promise<Uint8Array>` method
- VM: `activateGenesisState?: boolean` constructor option has been replaced with a `genesisState?: GenesisState` option
### Genesis in v6
For the v6 release responsibility for setting up a custom genesis state moved from the [Common](../common/) library to the `Blockchain` package, see PR [#1924](https://github.com/ethereumjs/ethereumjs-monorepo/pull/1924) for some work context.
A genesis state can be set along `Blockchain` creation by passing in a custom `genesisBlock` and `genesisState`. For `mainnet` and the official test networks like `sepolia` or `goerli` genesis is already provided with the block data coming from `@ethereumjs/common`. The genesis state is being integrated in the `Blockchain` library (see `genesisStates` folder).

@@ -110,12 +128,22 @@

## EIP-1559 Support
## Supported Blocks and Tx Types
### EIP-1559 Support
This library supports the handling of `EIP-1559` blocks and transactions starting with the `v5.3.0` release.
### EIP-4844 Shard Blob Transactions Support (experimental)
### EIP-4844 Shard Blob Transactions Support
This library supports an experimental version of the blob transaction type introduced with [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) as being specified in the [01d3209](https://github.com/ethereum/EIPs/commit/01d320998d1d53d95f347b5f43feaf606f230703) EIP version from February 8, 2023 and deployed along `eip4844-devnet-4` (January 2023) starting with `v6.2.0`.
This library supports the blob transaction type introduced with [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) as being specified in the [b9a5a11](https://github.com/ethereum/EIPs/commit/b9a5a117ab7e1dc18f937841d00598b527c306e7) EIP version from July 2023 deployed along [4844-devnet-7](https://github.com/ethpandaops/4844-testnet) (July 2023), see PR [#2349](https://github.com/ethereumjs/ethereumjs-monorepo/pull/2349) and following.
**Note:** 4844 support is not yet completely stable and there will still be (4844-)breaking changes along all types of library releases.
The blockchain library now allows for blob transactions to be validated and included in a chain where EIP-4844 activated either by hardfork or standalone EIP (see latest tx library release for additional details).
## Browser
With the breaking release round in Summer 2023 we have added hybrid ESM/CJS builds for all our libraries (see section below) and have eliminated many of the caveats which had previously prevented a frictionless browser usage.
It is now easily possible to run a browser build of one of the EthereumJS libraries within a modern browser using the provided ESM build. For a setup example see [./examples/browser.html](./examples/browser.html).
## API

@@ -127,2 +155,26 @@

### Hybrid CJS/ESM Builds
With the breaking releases from Summer 2023 we have started to ship our libraries with both CommonJS (`cjs` folder) and ESM builds (`esm` folder), see `package.json` for the detailed setup.
If you use an ES6-style `import` in your code files from the ESM build will be used:
```typescript
import { EthereumJSClass } from '@ethereumjs/[PACKAGE_NAME]'
```
If you use Node.js specific `require` the CJS build will be used:
```typescript
const { EthereumJSClass } = require('@ethereumjs/[PACKAGE_NAME]')
```
Using ESM will give you additional advantages over CJS beyond browser usage like static code analysis / Tree Shaking which CJS can not provide.
### Buffer -> Uint8Array
With the breaking releases from Summer 2023 we have removed all Node.js specific `Buffer` usages from our libraries and replace these with [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) representations, which are available both in Node.js and the browser (`Buffer` is a subclass of `Uint8Array`).
We have converted existing Buffer conversion methods to Uint8Array conversion methods in the [@ethereumjs/util](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/util) `bytes` module, see the respective README section for guidance.
### BigInt Support

@@ -149,3 +201,3 @@

```shell
DEBUG=blockchain:clique ts-node test.ts
DEBUG=ethjs,blockchain:clique ts-node test.ts
```

@@ -152,0 +204,0 @@

import { Block, BlockHeader } from '@ethereumjs/block'
import { Chain, Common, ConsensusAlgorithm, ConsensusType, Hardfork } from '@ethereumjs/common'
import { KECCAK256_RLP, Lock } from '@ethereumjs/util'
import { MemoryLevel } from 'memory-level'
import {
Chain,
ChainGenesis,
Common,
ConsensusAlgorithm,
ConsensusType,
Hardfork,
} from '@ethereumjs/common'
import { genesisStateRoot as genGenesisStateRoot } from '@ethereumjs/trie'
import {
KECCAK256_RLP,
Lock,
MapDB,
bytesToHex,
bytesToUnprefixedHex,
concatBytes,
equalsBytes,
} from '@ethereumjs/util'
import { CasperConsensus, CliqueConsensus, EthashConsensus } from './consensus'
import { DBOp, DBSaveLookups, DBSetBlockOrHeader, DBSetHashToNumber, DBSetTD } from './db/helpers'
import { DBManager } from './db/manager'
import { DBTarget } from './db/operation'
import { genesisStateRoot } from './genesisStates'
import {} from './utils'
import { CasperConsensus, CliqueConsensus, EthashConsensus } from './consensus/index.js'
import {
DBOp,
DBSaveLookups,
DBSetBlockOrHeader,
DBSetHashToNumber,
DBSetTD,
} from './db/helpers.js'
import { DBManager } from './db/manager.js'
import { DBTarget } from './db/operation.js'
import type { Consensus } from './consensus'
import type { GenesisState } from './genesisStates'
import type { BlockchainInterface, BlockchainOptions, OnBlock } from './types'
import type {
BlockchainInterface,
BlockchainOptions,
Consensus,
GenesisOptions,
OnBlock,
} from './types.js'
import type { BlockData } from '@ethereumjs/block'
import type { CliqueConfig } from '@ethereumjs/common'
import type { BigIntLike } from '@ethereumjs/util'
import type { AbstractLevel } from 'abstract-level'
import type { BigIntLike, DB, DBObject, GenesisState } from '@ethereumjs/util'

@@ -26,3 +48,3 @@ /**

consensus: Consensus
db: AbstractLevel<string | Buffer | Uint8Array, string | Buffer, string | Buffer>
db: DB<Uint8Array | string, Uint8Array | string | DBObject>
dbManager: DBManager

@@ -40,5 +62,5 @@

/** The hash of the current head block */
private _headBlockHash?: Buffer
private _headBlockHash?: Uint8Array
/** The hash of the current head header */
private _headHeaderHash?: Buffer
private _headHeaderHash?: Uint8Array

@@ -50,3 +72,3 @@ /**

*/
private _heads: { [key: string]: Buffer }
private _heads: { [key: string]: Uint8Array }

@@ -56,3 +78,3 @@ protected _isInitialized = false

_common: Common
public readonly common: Common
private _hardforkByHeadBlockNumber: boolean

@@ -71,3 +93,4 @@ private readonly _validateConsensus: boolean

const blockchain = new Blockchain(opts)
await blockchain._init(opts.genesisBlock)
await blockchain._init(opts)
return blockchain

@@ -87,4 +110,4 @@ }

const block = Block.fromBlockData(blockData, {
common: blockchain._common,
hardforkByBlockNumber: true,
common: blockchain.common,
setHardfork: true,
})

@@ -108,7 +131,7 @@ await blockchain.putBlock(block)

if (opts.common) {
this._common = opts.common
this.common = opts.common
} else {
const DEFAULT_CHAIN = Chain.Mainnet
const DEFAULT_HARDFORK = Hardfork.Chainstart
this._common = new Common({
this.common = new Common({
chain: DEFAULT_CHAIN,

@@ -124,9 +147,10 @@ hardfork: DEFAULT_HARDFORK,

this.db = opts.db ? opts.db : new MemoryLevel()
this.dbManager = new DBManager(this.db, this._common)
this.db = opts.db !== undefined ? opts.db : new MapDB()
this.dbManager = new DBManager(this.db, this.common)
if (opts.consensus) {
this.consensus = opts.consensus
} else {
switch (this._common.consensusAlgorithm()) {
switch (this.common.consensusAlgorithm()) {
case ConsensusAlgorithm.Casper:

@@ -142,3 +166,3 @@ this.consensus = new CasperConsensus()

default:
throw new Error(`consensus algorithm ${this._common.consensusAlgorithm()} not supported`)
throw new Error(`consensus algorithm ${this.common.consensusAlgorithm()} not supported`)
}

@@ -148,9 +172,9 @@ }

if (this._validateConsensus) {
if (this._common.consensusType() === ConsensusType.ProofOfWork) {
if (this._common.consensusAlgorithm() !== ConsensusAlgorithm.Ethash) {
if (this.common.consensusType() === ConsensusType.ProofOfWork) {
if (this.common.consensusAlgorithm() !== ConsensusAlgorithm.Ethash) {
throw new Error('consensus validation only supported for pow ethash algorithm')
}
}
if (this._common.consensusType() === ConsensusType.ProofOfAuthority) {
if (this._common.consensusAlgorithm() !== ConsensusAlgorithm.Clique) {
if (this.common.consensusType() === ConsensusType.ProofOfAuthority) {
if (this.common.consensusAlgorithm() !== ConsensusAlgorithm.Clique) {
throw new Error(

@@ -183,3 +207,3 @@ 'consensus (signature) validation only supported for poa clique algorithm'

*/
copy(): Blockchain {
shallowCopy(): Blockchain {
const copiedBlockchain = Object.create(

@@ -189,3 +213,3 @@ Object.getPrototypeOf(this),

)
copiedBlockchain._common = this._common.copy()
copiedBlockchain.common = this.common.copy()
return copiedBlockchain

@@ -199,35 +223,30 @@ }

*
* @param opts An options object to provide genesisBlock or ways to contruct it
*
* @hidden
*/
private async _init(genesisBlock?: Block): Promise<void> {
private async _init(opts: GenesisOptions = {}): Promise<void> {
await this.consensus.setup({ blockchain: this })
if (this._isInitialized) return
let dbGenesisBlock
try {
const genesisHash = await this.dbManager.numberToHash(BigInt(0))
dbGenesisBlock = await this.dbManager.getBlock(genesisHash)
} catch (error: any) {
if (error.code !== 'LEVEL_NOT_FOUND') {
throw error
}
}
if (!genesisBlock) {
let stateRoot
if (this._common.chainId() === BigInt(1) && this._customGenesisState === undefined) {
// For mainnet use the known genesis stateRoot to quicken setup
stateRoot = Buffer.from(
'd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544',
'hex'
)
let stateRoot = opts.genesisBlock?.header.stateRoot ?? opts.genesisStateRoot
if (stateRoot === undefined) {
if (this._customGenesisState !== undefined) {
stateRoot = await genGenesisStateRoot(this._customGenesisState)
} else {
stateRoot = await genesisStateRoot(this.genesisState())
// eslint-disable-next-line @typescript-eslint/no-use-before-define
stateRoot = await getGenesisStateRoot(Number(this.common.chainId()) as Chain)
}
genesisBlock = this.createGenesisBlock(stateRoot)
}
const genesisBlock = opts.genesisBlock ?? this.createGenesisBlock(stateRoot)
let genesisHash = await this.dbManager.numberToHash(BigInt(0))
const dbGenesisBlock =
genesisHash !== undefined ? await this.dbManager.getBlock(genesisHash) : undefined
// If the DB has a genesis block, then verify that the genesis block in the
// DB is indeed the Genesis block generated or assigned.
if (dbGenesisBlock && !genesisBlock.hash().equals(dbGenesisBlock.hash())) {
if (dbGenesisBlock !== undefined && !equalsBytes(genesisBlock.hash(), dbGenesisBlock.hash())) {
throw new Error(

@@ -238,3 +257,3 @@ 'The genesis block in the DB has a different hash than the provided genesis block.'

const genesisHash = genesisBlock.hash()
genesisHash = genesisBlock.hash()

@@ -258,33 +277,12 @@ if (!dbGenesisBlock) {

// load verified iterator heads
try {
const heads = await this.dbManager.getHeads()
this._heads = heads
} catch (error: any) {
if (error.code !== 'LEVEL_NOT_FOUND') {
throw error
}
this._heads = {}
}
const heads = await this.dbManager.getHeads()
this._heads = heads !== undefined ? heads : {}
// load headerchain head
try {
const hash = await this.dbManager.getHeadHeader()
this._headHeaderHash = hash
} catch (error: any) {
if (error.code !== 'LEVEL_NOT_FOUND') {
throw error
}
this._headHeaderHash = genesisHash
}
let hash = await this.dbManager.getHeadHeader()
this._headHeaderHash = hash !== undefined ? hash : genesisHash
// load blockchain head
try {
const hash = await this.dbManager.getHeadBlock()
this._headBlockHash = hash
} catch (error: any) {
if (error.code !== 'LEVEL_NOT_FOUND') {
throw error
}
this._headBlockHash = genesisHash
}
hash = await this.dbManager.getHeadBlock()
this._headBlockHash = hash !== undefined ? hash : genesisHash

@@ -320,3 +318,3 @@ if (this._hardforkByHeadBlockNumber) {

*
* This function replaces the old {@link Blockchain.getHead} method. Note that
* This function replaces the old Blockchain.getHead() method. Note that
* the function deviates from the old behavior and returns the

@@ -338,23 +336,2 @@ * genesis hash instead of the current head block if an iterator

/**
* Returns the specified iterator head.
*
* @param name - Optional name of the iterator head (default: 'vm')
*
* @deprecated use {@link Blockchain.getIteratorHead} instead.
* Note that {@link Blockchain.getIteratorHead} doesn't return
* the `headHeader` but the genesis hash as an initial iterator
* head value (now matching the behavior of {@link Blockchain.iterator}
* on a first run)
*/
async getHead(name = 'vm'): Promise<Block> {
return this.runWithLock<Block>(async () => {
// if the head is not found return the headHeader
const hash = this._heads[name] ?? this._headBlockHash
if (hash === undefined) throw new Error('No head found.')
const block = await this.getBlock(hash)
return block
})
}
/**
* Returns the latest header in the canonical chain.

@@ -445,2 +422,5 @@ */

const hash = await this.dbManager.numberToHash(canonicalHead)
if (hash === undefined) {
throw new Error(`no block for ${canonicalHead} found in DB`)
}
const header = await this._getHeader(hash, canonicalHead)

@@ -480,4 +460,4 @@ const td = await this.getParentTD(header)

item instanceof BlockHeader
? new Block(item, undefined, undefined, {
common: item._common,
? new Block(item, undefined, undefined, undefined, {
common: item.common,
})

@@ -499,3 +479,3 @@ : item

if (block._common.chainId() !== this._common.chainId()) {
if (block.common.chainId() !== this.common.chainId()) {
throw new Error('Chain mismatch while trying to put block or header')

@@ -539,3 +519,3 @@ }

td > currentTd.header ||
block._common.consensusType() === ConsensusType.ProofOfStake
block.common.consensusType() === ConsensusType.ProofOfStake
) {

@@ -616,6 +596,6 @@ const foundCommon = await this.findCommonAncestor(header)

if (!(header._common.consensusType() === 'pos')) await this.consensus.validateDifficulty(header)
if (!(header.common.consensusType() === 'pos')) await this.consensus.validateDifficulty(header)
if (this._common.consensusAlgorithm() === ConsensusAlgorithm.Clique) {
const period = (this._common.consensusConfig() as CliqueConfig).period
if (this.common.consensusAlgorithm() === ConsensusAlgorithm.Clique) {
const period = (this.common.consensusConfig() as CliqueConfig).period
// Timestamp diff between blocks is lower than PERIOD (clique)

@@ -640,9 +620,9 @@ if (parentHeader.timestamp + BigInt(period) > header.timestamp) {

// check blockchain dependent EIP1559 values
if (header._common.isActivatedEIP(1559) === true) {
if (header.common.isActivatedEIP(1559) === true) {
// check if the base fee is correct
let expectedBaseFee
const londonHfBlock = this._common.hardforkBlock(Hardfork.London)
const londonHfBlock = this.common.hardforkBlock(Hardfork.London)
const isInitialEIP1559Block = number === londonHfBlock
if (isInitialEIP1559Block) {
expectedBaseFee = header._common.param('gasConfig', 'initialBaseFee')
expectedBaseFee = header.common.param('gasConfig', 'initialBaseFee')
} else {

@@ -656,2 +636,9 @@ expectedBaseFee = parentHeader.calcNextBaseFee()

}
if (header.common.isActivatedEIP(4844) === true) {
const expectedExcessDataGas = parentHeader.calcNextExcessDataGas()
if (header.excessDataGas !== expectedExcessDataGas) {
throw new Error(`expected data gas: ${expectedExcessDataGas}, got: ${header.excessDataGas}`)
}
}
}

@@ -668,2 +655,6 @@

await block.validateData(false)
// TODO: Rethink how validateHeader vs validateBlobTransactions works since the parentHeader is retrieved multiple times
// (one for each uncle header and then for validateBlobTxs).
const parentBlock = await this.getBlock(block.header.parentHash)
block.validateBlobTransactions(parentBlock.header)
}

@@ -715,7 +706,7 @@ /**

// mark block hash as part of the canonical chain
canonicalChainHashes[parentBlock.hash().toString('hex')] = true
canonicalChainHashes[bytesToUnprefixedHex(parentBlock.hash())] = true
// for each of the uncles, mark the uncle as included
parentBlock.uncleHeaders.map((uh) => {
includedUncles[uh.hash().toString('hex')] = true
includedUncles[bytesToUnprefixedHex(uh.hash())] = true
})

@@ -732,4 +723,4 @@

uncleHeaders.map((uh) => {
const uncleHash = uh.hash().toString('hex')
const parentHash = uh.parentHash.toString('hex')
const uncleHash = bytesToUnprefixedHex(uh.hash())
const parentHash = bytesToUnprefixedHex(uh.parentHash)

@@ -760,3 +751,3 @@ if (!canonicalChainHashes[parentHash]) {

*/
async getBlock(blockId: Buffer | number | bigint): Promise<Block> {
async getBlock(blockId: Uint8Array | number | bigint): Promise<Block> {
// cannot wait for a lock here: it is used both in `validate` of `Block`

@@ -767,14 +758,12 @@ // (calls `getBlock` to get `parentHash`) it is also called from `runBlock`

// know it is OK if we call it from the iterator... (runBlock)
try {
return await this.dbManager.getBlock(blockId)
} catch (error: any) {
if (error.code === 'LEVEL_NOT_FOUND') {
if (typeof blockId === 'object') {
error.message = `Block with hash ${blockId.toString('hex')} not found in DB (NotFound)`
} else {
error.message = `Block number ${blockId} not found in DB (NotFound)`
}
const block = await this.dbManager.getBlock(blockId)
if (block === undefined) {
if (typeof blockId === 'object') {
throw new Error(`Block with hash ${bytesToHex(blockId)} not found in DB`)
} else {
throw new Error(`Block number ${blockId} not found in DB`)
}
throw error
}
return block
}

@@ -785,5 +774,8 @@

*/
public async getTotalDifficulty(hash: Buffer, number?: bigint): Promise<bigint> {
public async getTotalDifficulty(hash: Uint8Array, number?: bigint): Promise<bigint> {
if (number === undefined) {
number = await this.dbManager.hashToNumber(hash)
if (number === undefined) {
throw new Error(`Block with hash ${bytesToHex(hash)} not found in DB`)
}
}

@@ -812,3 +804,3 @@ return this.dbManager.getTotalDifficulty(hash, number)

async getBlocks(
blockId: Buffer | bigint | number,
blockId: Uint8Array | bigint | number,
maxBlocks: number,

@@ -822,12 +814,12 @@ skip: number,

const nextBlock = async (blockId: Buffer | bigint | number): Promise<any> => {
const nextBlock = async (blockId: Uint8Array | bigint | number): Promise<any> => {
let block
try {
block = await this.getBlock(blockId)
} catch (error: any) {
if (error.code !== 'LEVEL_NOT_FOUND') {
throw error
}
return
} catch (err: any) {
if (err.message.includes('not found in DB') === true) {
return
} else throw err
}
i++

@@ -855,4 +847,4 @@ const nextBlockNumber = block.header.number + BigInt(reverse ? -1 : 1)

*/
async selectNeededHashes(hashes: Array<Buffer>): Promise<Buffer[]> {
return this.runWithLock<Buffer[]>(async () => {
async selectNeededHashes(hashes: Array<Uint8Array>): Promise<Uint8Array[]> {
return this.runWithLock<Uint8Array[]>(async () => {
let max: number

@@ -869,7 +861,8 @@ let mid: number

number = await this.dbManager.hashToNumber(hashes[mid])
} catch (error: any) {
if (error.code !== 'LEVEL_NOT_FOUND') {
throw error
}
} catch (err: any) {
if (err.message.includes('not found in DB') === true) {
number = undefined
} else throw err
}
if (number !== undefined) {

@@ -897,3 +890,3 @@ min = mid + 1

*/
async delBlock(blockHash: Buffer) {
async delBlock(blockHash: Uint8Array) {
// Q: is it safe to make this not wait for a lock? this is called from

@@ -910,3 +903,3 @@ // `BlockchainTestsRunner` in case `runBlock` throws (i.e. the block is invalid).

*/
private async _delBlock(blockHash: Buffer) {
private async _delBlock(blockHash: Uint8Array) {
const dbOps: DBOp[] = []

@@ -923,3 +916,3 @@

const inCanonical = canonicalHash !== false && canonicalHash.equals(blockHash)
const inCanonical = canonicalHash !== false && equalsBytes(canonicalHash, blockHash)

@@ -954,5 +947,5 @@ // delete the block, and if block is in the canonical chain, delete all

private async _delChild(
blockHash: Buffer,
blockHash: Uint8Array,
blockNumber: bigint,
headHash: Buffer | null,
headHash: Uint8Array | null,
ops: DBOp[]

@@ -970,7 +963,10 @@ ) {

if (this._headHeaderHash?.equals(blockHash) === true) {
if (
this._headHeaderHash !== undefined &&
equalsBytes(this._headHeaderHash, blockHash) === true
) {
this._headHeaderHash = headHash
}
if (this._headBlockHash?.equals(blockHash) === true) {
if (this._headBlockHash !== undefined && equalsBytes(this._headBlockHash, blockHash)) {
this._headBlockHash = headHash

@@ -982,5 +978,5 @@ }

await this._delChild(childHeader.hash(), childHeader.number, headHash, ops)
} catch (error: any) {
if (error.code !== 'LEVEL_NOT_FOUND') {
throw error
} catch (err: any) {
if (err.message.includes('not found in canonical chain') !== true) {
throw err
}

@@ -1015,42 +1011,62 @@ }

let headBlockNumber = await this.dbManager.hashToNumber(headHash)
let nextBlockNumber = headBlockNumber + BigInt(1)
// `headBlockNumber` should always exist since it defaults to the genesis block
let nextBlockNumber = headBlockNumber! + BigInt(1)
let blocksRanCounter = 0
let lastBlock: Block | undefined
while (maxBlocks !== blocksRanCounter) {
try {
let nextBlock = await this.getBlock(nextBlockNumber)
const reorg = lastBlock ? !lastBlock.hash().equals(nextBlock.header.parentHash) : false
if (reorg) {
// If reorg has happened, the _heads must have been updated so lets reload the counters
headHash = this._heads[name] ?? this.genesisBlock.hash()
headBlockNumber = await this.dbManager.hashToNumber(headHash)
nextBlockNumber = headBlockNumber + BigInt(1)
nextBlock = await this.getBlock(nextBlockNumber)
}
this._heads[name] = nextBlock.hash()
lastBlock = nextBlock
if (releaseLockOnCallback === true) {
this._lock.release()
}
try {
while (maxBlocks !== blocksRanCounter) {
try {
await onBlock(nextBlock, reorg)
} finally {
let nextBlock = await this.getBlock(nextBlockNumber)
const reorg = lastBlock
? !equalsBytes(lastBlock.hash(), nextBlock.header.parentHash)
: false
if (reorg) {
// If reorg has happened, the _heads must have been updated so lets reload the counters
headHash = this._heads[name] ?? this.genesisBlock.hash()
headBlockNumber = await this.dbManager.hashToNumber(headHash)
nextBlockNumber = headBlockNumber! + BigInt(1)
nextBlock = await this.getBlock(nextBlockNumber)
}
// While running onBlock with released lock, reorgs can happen via putBlocks
let reorgWhileOnBlock = false
if (releaseLockOnCallback === true) {
await this._lock.acquire()
this._lock.release()
}
try {
await onBlock(nextBlock, reorg)
} finally {
if (releaseLockOnCallback === true) {
await this._lock.acquire()
// If lock was released check if reorg occured
const nextBlockMayBeReorged = await this.getBlock(nextBlockNumber).catch(
(_e) => null
)
reorgWhileOnBlock = nextBlockMayBeReorged
? !equalsBytes(nextBlockMayBeReorged.hash(), nextBlock.hash())
: true
}
}
// if there was no reorg, update head
if (!reorgWhileOnBlock) {
this._heads[name] = nextBlock.hash()
lastBlock = nextBlock
nextBlockNumber++
}
// Successful execution of onBlock, move the head pointer
blocksRanCounter++
} catch (error: any) {
if ((error.message as string).includes('not found in DB')) {
break
} else {
throw error
}
}
nextBlockNumber++
blocksRanCounter++
} catch (error: any) {
if (error.code === 'LEVEL_NOT_FOUND') {
break
} else {
throw error
}
}
return blocksRanCounter
} finally {
await this._saveHeads()
}
await this._saveHeads()
return blocksRanCounter
})

@@ -1065,3 +1081,3 @@ }

*/
async setIteratorHead(tag: string, headHash: Buffer) {
async setIteratorHead(tag: string, headHash: Uint8Array) {
await this.runWithLock<void>(async () => {

@@ -1096,3 +1112,3 @@ this._heads[tag] = headHash

}
while (!header.hash().equals(newHeader.hash()) && header.number > BigInt(0)) {
while (!equalsBytes(header.hash(), newHeader.hash()) && header.number > BigInt(0)) {
header = await this.getCanonicalHeader(header.number - BigInt(1))

@@ -1103,3 +1119,3 @@ ancestorHeaders.add(header)

}
if (!header.hash().equals(newHeader.hash())) {
if (!equalsBytes(header.hash(), newHeader.hash())) {
throw new Error('Failed to find ancient header')

@@ -1128,6 +1144,6 @@ }

blockNumber: bigint,
headHash: Buffer,
headHash: Uint8Array,
ops: DBOp[]
) {
let hash: Buffer | false
let hash: Uint8Array | false

@@ -1143,3 +1159,3 @@ hash = await this.safeNumberToHash(blockNumber)

for (const name of Object.keys(this._heads)) {
if (this._heads[name].equals(hash)) {
if (equalsBytes(this._heads[name], hash)) {
this._heads[name] = headHash

@@ -1149,10 +1165,10 @@ }

// reset stale headHeader to current canonical
if (this._headHeaderHash !== undefined && equalsBytes(this._headHeaderHash, hash) === true) {
this._headHeaderHash = headHash
}
// reset stale headBlock to current canonical
if (this._headBlockHash?.equals(hash) === true) {
if (this._headBlockHash !== undefined && equalsBytes(this._headBlockHash, hash) === true) {
this._headBlockHash = headHash
}
// reset stale headBlock to current canonical
if (this._headHeaderHash?.equals(hash) === true) {
this._headHeaderHash = headHash
}

@@ -1182,7 +1198,7 @@ blockNumber++

let currentNumber = header.number
let currentCanonicalHash: Buffer = header.hash()
let currentCanonicalHash: Uint8Array = header.hash()
// track the staleHash: this is the hash currently in the DB which matches
// the block number of the provided header.
let staleHash: Buffer | false = false
let staleHash: Uint8Array | false = false
let staleHeads: string[] = []

@@ -1194,3 +1210,3 @@ let staleHeadBlock = false

currentCanonicalHash = header.hash()
return staleHash === false || !currentCanonicalHash.equals(staleHash)
return staleHash === false || !equalsBytes(currentCanonicalHash, staleHash)
}

@@ -1214,3 +1230,3 @@

for (const name of Object.keys(this._heads)) {
if (staleHash && this._heads[name].equals(staleHash)) {
if (staleHash && equalsBytes(this._heads[name], staleHash)) {
staleHeads.push(name)

@@ -1220,13 +1236,13 @@ }

// flag stale headBlock for reset
if (staleHash && this._headBlockHash?.equals(staleHash) === true) {
if (
staleHash &&
this._headBlockHash !== undefined &&
equalsBytes(this._headBlockHash, staleHash) === true
) {
staleHeadBlock = true
}
try {
header = await this._getHeader(header.parentHash, --currentNumber)
} catch (error: any) {
header = await this._getHeader(header.parentHash, --currentNumber)
if (header === undefined) {
staleHeads = []
if (error.code !== 'LEVEL_NOT_FOUND') {
throw error
}
break

@@ -1254,4 +1270,10 @@ }

private _saveHeadOps(): DBOp[] {
// Convert DB heads to hex strings for efficient storage in DB
// LevelDB doesn't handle Uint8Arrays properly when they are part
// of a JSON object being stored as a value in the DB
const hexHeads = Object.fromEntries(
Object.entries(this._heads).map((entry) => [entry[0], bytesToUnprefixedHex(entry[1])])
)
return [
DBOp.set(DBTarget.Heads, this._heads),
DBOp.set(DBTarget.Heads, hexHeads),
DBOp.set(DBTarget.HeadHeader, this._headHeaderHash!),

@@ -1277,5 +1299,6 @@ DBOp.set(DBTarget.HeadBlock, this._headBlockHash!),

*/
private async _getHeader(hash: Buffer, number?: bigint) {
private async _getHeader(hash: Uint8Array, number?: bigint) {
if (number === undefined) {
number = await this.dbManager.hashToNumber(hash)
if (number === undefined) throw new Error(`no header for ${bytesToHex(hash)} found in DB`)
}

@@ -1286,7 +1309,11 @@ return this.dbManager.getHeader(hash, number)

async checkAndTransitionHardForkByNumber(
number: bigint,
number: BigIntLike,
td?: BigIntLike,
timestamp?: BigIntLike
): Promise<void> {
this._common.setHardforkByBlockNumber(number, td, timestamp)
this.common.setHardforkBy({
blockNumber: number,
td,
timestamp,
})

@@ -1297,3 +1324,3 @@ // If custom consensus algorithm is used, skip merge hardfork consensus checks

switch (this._common.consensusAlgorithm()) {
switch (this.common.consensusAlgorithm()) {
case ConsensusAlgorithm.Casper:

@@ -1315,3 +1342,3 @@ if (!(this.consensus instanceof CasperConsensus)) {

default:
throw new Error(`consensus algorithm ${this._common.consensusAlgorithm()} not supported`)
throw new Error(`consensus algorithm ${this.common.consensusAlgorithm()} not supported`)
}

@@ -1327,2 +1354,5 @@ await this.consensus.setup({ blockchain: this })

const hash = await this.dbManager.numberToHash(number)
if (hash === undefined) {
throw new Error(`header with number ${number} not found in canonical chain`)
}
return this._getHeader(hash, number)

@@ -1332,17 +1362,10 @@ }

/**
* This method either returns a Buffer if there exists one in the DB or if it
* does not exist (DB throws a `NotFoundError`) then return false If DB throws
* 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
*/
async safeNumberToHash(number: bigint): Promise<Buffer | false> {
try {
const hash = await this.dbManager.numberToHash(number)
return hash
} catch (error: any) {
if (error.code !== 'LEVEL_NOT_FOUND') {
throw error
}
return false
}
async safeNumberToHash(number: bigint): Promise<Uint8Array | false> {
const hash = await this.dbManager.numberToHash(number)
return hash !== undefined ? hash : false
}

@@ -1362,9 +1385,9 @@

*/
createGenesisBlock(stateRoot: Buffer): Block {
const common = this._common.copy()
common.setHardforkByBlockNumber(
0,
BigInt(common.genesis().difficulty),
common.genesis().timestamp
)
createGenesisBlock(stateRoot: Uint8Array): Block {
const common = this.common.copy()
common.setHardforkBy({
blockNumber: 0,
td: BigInt(common.genesis().difficulty),
timestamp: common.genesis().timestamp,
})

@@ -1376,3 +1399,2 @@ const header: BlockData['header'] = {

withdrawalsRoot: common.isActivatedEIP(4895) ? KECCAK256_RLP : undefined,
excessDataGas: common.isActivatedEIP(4844) ? BigInt(0) : undefined,
}

@@ -1385,3 +1407,3 @@ if (common.consensusType() === 'poa') {

// Add required extraData (32 bytes vanity + 65 bytes filled with zeroes
header.extraData = Buffer.concat([Buffer.alloc(32), Buffer.alloc(65).fill(0)])
header.extraData = concatBytes(new Uint8Array(32), new Uint8Array(65))
}

@@ -1394,29 +1416,10 @@ }

}
}
/**
* Returns the genesis state of the blockchain.
* All values are provided as hex-prefixed strings.
*/
genesisState(): GenesisState {
if (this._customGenesisState) {
return this._customGenesisState
}
// Use require statements here in favor of import statements
// to load json files on demand
// (high memory usage by large mainnet.json genesis state file)
switch (this._common.chainName()) {
case 'mainnet':
return require('./genesisStates/mainnet.json')
case 'ropsten':
return require('./genesisStates/ropsten.json')
case 'rinkeby':
return require('./genesisStates/rinkeby.json')
case 'goerli':
return require('./genesisStates/goerli.json')
case 'sepolia':
return require('./genesisStates/sepolia.json')
}
return {}
}
/**
* Returns the genesis state root if chain is well known or an empty state's root otherwise
*/
async function getGenesisStateRoot(chainId: Chain): Promise<Uint8Array> {
const chainGenesis = ChainGenesis[chainId]
return chainGenesis !== undefined ? chainGenesis.stateRoot : genGenesisStateRoot({})
}
import { ConsensusAlgorithm } from '@ethereumjs/common'
import type { Consensus } from './interface'
import type { Consensus } from '../types.js'
import type { BlockHeader } from '@ethereumjs/block'

@@ -5,0 +5,0 @@

import { ConsensusAlgorithm } from '@ethereumjs/common'
import { RLP } from '@ethereumjs/rlp'
import { Address, arrToBufArr, bigIntToBuffer, bufArrToArr, bufferToBigInt } from '@ethereumjs/util'
import { debug as createDebugLogger } from 'debug'
import {
Address,
TypeOutput,
bigIntToBytes,
bytesToBigInt,
equalsBytes,
hexToBytes,
toType,
} from '@ethereumjs/util'
import debugDefault from 'debug'
import type { Blockchain } from '..'
import type { Consensus, ConsensusOptions } from './interface'
import type { Blockchain } from '../index.js'
import type { Consensus, ConsensusOptions } from '../types.js'
import type { Block, BlockHeader } from '@ethereumjs/block'
import type { CliqueConfig } from '@ethereumjs/common'
const { debug: createDebugLogger } = debugDefault

@@ -14,5 +23,5 @@ const debug = createDebugLogger('blockchain:clique')

// Magic nonce number to vote on adding a new signer
export const CLIQUE_NONCE_AUTH = Buffer.from('ffffffffffffffff', 'hex')
export const CLIQUE_NONCE_AUTH = hexToBytes('0xffffffffffffffff')
// Magic nonce number to vote on removing a signer.
export const CLIQUE_NONCE_DROP = Buffer.alloc(8)
export const CLIQUE_NONCE_DROP = new Uint8Array(8)

@@ -28,7 +37,2 @@ const CLIQUE_SIGNERS_KEY = 'CliqueSigners'

const DB_OPTS = {
keyEncoding: 'buffer',
valueEncoding: 'buffer',
}
// Clique Signer State

@@ -41,3 +45,3 @@ type CliqueSignerState = [blockNumber: bigint, signers: Address[]]

blockNumber: bigint,
vote: [signer: Address, beneficiary: Address, cliqueNonce: Buffer]
vote: [signer: Address, beneficiary: Address, cliqueNonce: Uint8Array]
]

@@ -52,2 +56,6 @@ type CliqueLatestVotes = CliqueVote[]

* This class encapsulates Clique-related consensus functionality when used with the Blockchain class.
* Note: reorgs which happen between epoch transitions, which change the internal voting state over the reorg
* will result in failure and is currently not supported.
* The hotfix for this could be: re-load the latest epoch block (this has the clique state in the extraData of the header)
* Now replay all blocks on top of it. This should validate the chain up to the new/reorged tip which previously threw.
*/

@@ -64,3 +72,3 @@ export class CliqueConsensus implements Consensus {

*/
private CLIQUE_SIGNER_HISTORY_BLOCK_LIMIT = 100
private CLIQUE_SIGNER_HISTORY_BLOCK_LIMIT = 200

@@ -119,2 +127,3 @@ /**

this._cliqueLatestSignerStates = await this.getCliqueLatestSignerStates()
this._cliqueLatestSignerStates.sort((a, b) => (a[0] > b[0] ? 1 : -1))
this._cliqueLatestVotes = await this.getCliqueLatestVotes()

@@ -134,3 +143,3 @@ this._cliqueLatestBlockSigners = await this.getCliqueLatestBlockSigners()

const { header } = block
const valid = header.cliqueVerifySignature(this.cliqueActiveSigners())
const valid = header.cliqueVerifySignature(this.cliqueActiveSigners(header.number))
if (!valid) {

@@ -149,3 +158,3 @@ throw new Error('invalid PoA block signature (clique)')

const checkpointSigners = header.cliqueEpochTransitionSigners()
const activeSigners = this.cliqueActiveSigners()
const activeSigners = this.cliqueActiveSigners(header.number)
for (const [i, cSigner] of checkpointSigners.entries()) {

@@ -171,3 +180,3 @@ if (activeSigners[i]?.equals(cSigner) !== true) {

const signers = this.cliqueActiveSigners()
const signers = this.cliqueActiveSigners(header.number)
if (signers.length === 0) {

@@ -226,3 +235,13 @@ // abort if signers are unavailable

if (signerState) {
const blockNumber = signerState[0]
const known = this._cliqueLatestSignerStates.find((value) => {
if (value[0] === blockNumber) {
return true
}
})
if (known !== undefined) {
return
}
this._cliqueLatestSignerStates.push(signerState)
this._cliqueLatestSignerStates.sort((a, b) => (a[0] > b[0] ? 1 : -1))
}

@@ -247,15 +266,16 @@

const formatted = this._cliqueLatestSignerStates.map((state) => [
bigIntToBuffer(state[0]),
state[1].map((a) => a.toBuffer()),
bigIntToBytes(state[0]),
state[1].map((a) => a.toBytes()),
])
await this.blockchain!.db.put(
CLIQUE_SIGNERS_KEY,
Buffer.from(RLP.encode(bufArrToArr(formatted))),
DB_OPTS
)
await this.blockchain!.db.put(CLIQUE_SIGNERS_KEY, RLP.encode(formatted))
// Output active signers for debugging purposes
let i = 0
for (const signer of this.cliqueActiveSigners()) {
debug(`Clique signer [${i}]: ${signer}`)
i++
if (signerState !== undefined) {
let i = 0
try {
for (const signer of this.cliqueActiveSigners(signerState[0])) {
debug(`Clique signer [${i}]: ${signer} (block: ${signerState[0]})`)
i++
}
// eslint-disable-next-line no-empty
} catch (e) {}
}

@@ -284,5 +304,5 @@ }

(header.number %
BigInt((this.blockchain!._common.consensusConfig() as CliqueConfig).epoch))
const limit = this.cliqueSignerLimit()
let activeSigners = this.cliqueActiveSigners()
BigInt((this.blockchain!.common.consensusConfig() as CliqueConfig).epoch))
const limit = this.cliqueSignerLimit(header.number)
let activeSigners = [...this.cliqueActiveSigners(header.number)]
let consensus = false

@@ -296,3 +316,3 @@

vote[1][1].equals(beneficiary) &&
vote[1][2].equals(CLIQUE_NONCE_AUTH)
equalsBytes(vote[1][2], CLIQUE_NONCE_AUTH)
)

@@ -310,3 +330,3 @@ })

let numBeneficiaryVotesAUTH = beneficiaryVotesAUTH.length
if (round === 2 && nonce.equals(CLIQUE_NONCE_AUTH)) {
if (round === 2 && equalsBytes(nonce, CLIQUE_NONCE_AUTH)) {
numBeneficiaryVotesAUTH += 1

@@ -320,4 +340,10 @@ }

activeSigners.sort((a, b) => {
// Sort by buffer size
return a.toBuffer().compare(b.toBuffer())
// Sort by array size
const result =
toType(a.toString(), TypeOutput.BigInt) < toType(b.toString(), TypeOutput.BigInt)
if (result) {
return -1
} else {
return 1
}
})

@@ -336,3 +362,3 @@ // Discard votes for added signer

vote[1][1].equals(beneficiary) &&
vote[1][2].equals(CLIQUE_NONCE_DROP)
equalsBytes(vote[1][2], CLIQUE_NONCE_DROP)
)

@@ -351,3 +377,3 @@ })

if (round === 2 && nonce.equals(CLIQUE_NONCE_DROP)) {
if (round === 2 && equalsBytes(nonce, CLIQUE_NONCE_DROP)) {
numBeneficiaryVotesDROP += 1

@@ -372,3 +398,3 @@ }

`[Block ${header.number}] New clique vote: ${signer} -> ${beneficiary} ${
nonce.equals(CLIQUE_NONCE_AUTH) ? 'AUTH' : 'DROP'
equalsBytes(nonce, CLIQUE_NONCE_AUTH) ? 'AUTH' : 'DROP'
}`

@@ -402,3 +428,3 @@ )

(lastBlockNumber %
BigInt((this.blockchain!._common.consensusConfig() as CliqueConfig).epoch))
BigInt((this.blockchain!.common.consensusConfig() as CliqueConfig).epoch))
const blockLimit = lastEpochBlockNumber - BigInt(limit)

@@ -410,10 +436,6 @@ this._cliqueLatestVotes = this._cliqueLatestVotes.filter((state) => state[0] >= blockLimit)

const formatted = this._cliqueLatestVotes.map((v) => [
bigIntToBuffer(v[0]),
[v[1][0].toBuffer(), v[1][1].toBuffer(), v[1][2]],
bigIntToBytes(v[0]),
[v[1][0].toBytes(), v[1][1].toBytes(), v[1][2]],
])
await this.blockchain!.db.put(
CLIQUE_VOTES_KEY,
Buffer.from(RLP.encode(bufArrToArr(formatted))),
DB_OPTS
)
await this.blockchain!.db.put(CLIQUE_VOTES_KEY, RLP.encode(formatted))
}

@@ -424,3 +446,3 @@

*/
cliqueActiveSigners(): Address[] {
cliqueActiveSigners(blockNum: bigint): Address[] {
const signers = this._cliqueLatestSignerStates

@@ -430,3 +452,8 @@ if (signers.length === 0) {

}
return [...signers[signers.length - 1][1]]
for (let i = signers.length - 1; i >= 0; i--) {
if (signers[i][0] < blockNum) {
return signers[i][1]
}
}
throw new Error(`Could not load signers for block ${blockNum}`)
}

@@ -441,4 +468,4 @@

*/
private cliqueSignerLimit() {
return Math.floor(this.cliqueActiveSigners().length / 2) + 1
private cliqueSignerLimit(blockNum: bigint) {
return Math.floor(this.cliqueActiveSigners(blockNum).length / 2) + 1
}

@@ -457,3 +484,3 @@

}
const limit = this.cliqueSignerLimit()
const limit = this.cliqueSignerLimit(header.number)
// construct recent block signers list with this block

@@ -512,3 +539,3 @@ let signers = this._cliqueLatestBlockSigners

const length = this._cliqueLatestBlockSigners.length
const limit = this.cliqueSignerLimit()
const limit = this.cliqueSignerLimit(header.number)
if (length > limit) {

@@ -524,10 +551,6 @@ this._cliqueLatestBlockSigners = this._cliqueLatestBlockSigners.slice(

const formatted = this._cliqueLatestBlockSigners.map((b) => [
bigIntToBuffer(b[0]),
b[1].toBuffer(),
bigIntToBytes(b[0]),
b[1].toBytes(),
])
await this.blockchain!.db.put(
CLIQUE_BLOCK_SIGNERS_SNAPSHOT_KEY,
Buffer.from(RLP.encode(bufArrToArr(formatted))),
DB_OPTS
)
await this.blockchain!.db.put(CLIQUE_BLOCK_SIGNERS_SNAPSHOT_KEY, RLP.encode(formatted))
}

@@ -540,19 +563,10 @@

private async getCliqueLatestSignerStates(): Promise<CliqueLatestSignerStates> {
try {
const signerStates = await this.blockchain!.db.get<string, Buffer>(
CLIQUE_SIGNERS_KEY,
DB_OPTS
)
const states = arrToBufArr(RLP.decode(Uint8Array.from(signerStates))) as [Buffer, Buffer[]]
return states.map((state) => {
const blockNum = bufferToBigInt(state[0] as Buffer)
const addrs = (<any>state[1]).map((buf: Buffer) => new Address(buf))
return [blockNum, addrs]
}) as CliqueLatestSignerStates
} catch (error: any) {
if (error.code === 'LEVEL_NOT_FOUND') {
return []
}
throw error
}
const signerStates = await this.blockchain!.db.get(CLIQUE_SIGNERS_KEY)
if (signerStates === undefined) return []
const states = RLP.decode(signerStates as Uint8Array) as [Uint8Array, Uint8Array[]]
return states.map((state) => {
const blockNum = bytesToBigInt(state[0] as Uint8Array)
const addrs = (<any>state[1]).map((bytes: Uint8Array) => new Address(bytes))
return [blockNum, addrs]
}) as CliqueLatestSignerStates
}

@@ -565,21 +579,15 @@

private async getCliqueLatestVotes(): Promise<CliqueLatestVotes> {
try {
const signerVotes = await this.blockchain!.db.get<string, Buffer>(CLIQUE_VOTES_KEY, DB_OPTS)
const votes = arrToBufArr(RLP.decode(Uint8Array.from(signerVotes))) as [
Buffer,
[Buffer, Buffer, Buffer]
]
return votes.map((vote) => {
const blockNum = bufferToBigInt(vote[0] as Buffer)
const signer = new Address((vote[1] as any)[0])
const beneficiary = new Address((vote[1] as any)[1])
const nonce = (vote[1] as any)[2]
return [blockNum, [signer, beneficiary, nonce]]
}) as CliqueLatestVotes
} catch (error: any) {
if (error.code === 'LEVEL_NOT_FOUND') {
return []
}
throw error
}
const signerVotes = await this.blockchain!.db.get(CLIQUE_VOTES_KEY)
if (signerVotes === undefined) return []
const votes = RLP.decode(signerVotes as Uint8Array) as [
Uint8Array,
[Uint8Array, Uint8Array, Uint8Array]
]
return votes.map((vote) => {
const blockNum = bytesToBigInt(vote[0] as Uint8Array)
const signer = new Address((vote[1] as any)[0])
const beneficiary = new Address((vote[1] as any)[1])
const nonce = (vote[1] as any)[2]
return [blockNum, [signer, beneficiary, nonce]]
}) as CliqueLatestVotes
}

@@ -592,19 +600,10 @@

private async getCliqueLatestBlockSigners(): Promise<CliqueLatestBlockSigners> {
try {
const blockSigners = await this.blockchain!.db.get<string, Buffer>(
CLIQUE_BLOCK_SIGNERS_SNAPSHOT_KEY,
DB_OPTS
)
const signers = arrToBufArr(RLP.decode(Uint8Array.from(blockSigners))) as [Buffer, Buffer][]
return signers.map((s) => {
const blockNum = bufferToBigInt(s[0] as Buffer)
const signer = new Address(s[1] as any)
return [blockNum, signer]
}) as CliqueLatestBlockSigners
} catch (error: any) {
if (error.code === 'LEVEL_NOT_FOUND') {
return []
}
throw error
}
const blockSigners = await this.blockchain!.db.get(CLIQUE_BLOCK_SIGNERS_SNAPSHOT_KEY)
if (blockSigners === undefined) return []
const signers = RLP.decode(blockSigners as Uint8Array) as [Uint8Array, Uint8Array][]
return signers.map((s) => {
const blockNum = bytesToBigInt(s[0] as Uint8Array)
const signer = new Address(s[1])
return [blockNum, signer]
}) as CliqueLatestBlockSigners
}

@@ -628,4 +627,4 @@

*/
async cliqueSignerInTurn(signer: Address): Promise<boolean> {
const signers = this.cliqueActiveSigners()
async cliqueSignerInTurn(signer: Address, blockNum: bigint): Promise<boolean> {
const signers = this.cliqueActiveSigners(blockNum)
const signerIndex = signers.findIndex((address) => address.equals(signer))

@@ -632,0 +631,0 @@ if (signerIndex === -1) {

import { ConsensusAlgorithm } from '@ethereumjs/common'
import { Ethash } from '@ethereumjs/ethash'
import type { Blockchain } from '..'
import type { Consensus, ConsensusOptions } from './interface'
import type { Blockchain } from '../index.js'
import type { Consensus, ConsensusOptions } from '../types.js'
import type { Block, BlockHeader } from '@ethereumjs/block'
import type { EthashCacheDB } from '@ethereumjs/ethash'

@@ -48,5 +47,5 @@ /**

this.blockchain = blockchain
this._ethash = new Ethash(this.blockchain.db as unknown as EthashCacheDB)
this._ethash = new Ethash(this.blockchain.db as any)
}
public async newBlock(): Promise<void> {}
}

@@ -1,6 +0,5 @@

import { CasperConsensus } from './casper'
import { CliqueConsensus } from './clique'
import { EthashConsensus } from './ethash'
import { Consensus } from './interface'
import { CasperConsensus } from './casper.js'
import { CliqueConsensus } from './clique.js'
import { EthashConsensus } from './ethash.js'
export { CasperConsensus, CliqueConsensus, Consensus, EthashConsensus }
export { CasperConsensus, CliqueConsensus, EthashConsensus }

@@ -1,34 +0,36 @@

import * as LRUCache from 'lru-cache'
import { bytesToUnprefixedHex } from '@ethereumjs/util'
import { LRUCache } from 'lru-cache'
/**
* Simple LRU Cache that allows for keys of type Buffer
* Simple LRU Cache that allows for keys of type Uint8Array
* @hidden
*/
export class Cache<V> {
_cache: LRUCache<string, V>
_cache: LRUCache<string, { value: V }, void>
constructor(opts: LRUCache.Options<string, V>) {
constructor(opts: LRUCache.Options<string, { value: V }, void>) {
this._cache = new LRUCache(opts)
}
set(key: string | Buffer, value: V): void {
if (key instanceof Buffer) {
key = key.toString('hex')
set(key: string | Uint8Array, value: V): void {
if (key instanceof Uint8Array) {
key = bytesToUnprefixedHex(key)
}
this._cache.set(key, value)
this._cache.set(key, { value })
}
get(key: string | Buffer): V | undefined {
if (key instanceof Buffer) {
key = key.toString('hex')
get(key: string | Uint8Array): V | undefined {
if (key instanceof Uint8Array) {
key = bytesToUnprefixedHex(key)
}
return this._cache.get(key)
const elem = this._cache.get(key)
return elem !== undefined ? elem.value : undefined
}
del(key: string | Buffer): void {
if (key instanceof Buffer) {
key = key.toString('hex')
del(key: string | Uint8Array): void {
if (key instanceof Uint8Array) {
key = bytesToUnprefixedHex(key)
}
this._cache.del(key)
this._cache.delete(key)
}
}

@@ -1,2 +0,2 @@

import { bigIntToBuffer } from '@ethereumjs/util'
import { bigIntToBytes, concatBytes, utf8ToBytes } from '@ethereumjs/util'

@@ -20,3 +20,3 @@ // Geth compatible DB keys

*/
const HEADER_PREFIX = Buffer.from('h')
const HEADER_PREFIX = utf8ToBytes('h')

@@ -26,3 +26,3 @@ /**

*/
const TD_SUFFIX = Buffer.from('t')
const TD_SUFFIX = utf8ToBytes('t')

@@ -32,3 +32,3 @@ /**

*/
const NUM_SUFFIX = Buffer.from('n')
const NUM_SUFFIX = utf8ToBytes('n')

@@ -38,3 +38,3 @@ /**

*/
const BLOCK_HASH_PEFIX = Buffer.from('H')
const BLOCK_HASH_PEFIX = utf8ToBytes('H')

@@ -44,3 +44,3 @@ /**

*/
const BODY_PREFIX = Buffer.from('b')
const BODY_PREFIX = utf8ToBytes('b')

@@ -50,16 +50,16 @@ // Utility functions

/**
* Convert bigint to big endian Buffer
* Convert bigint to big endian Uint8Array
*/
const bufBE8 = (n: bigint) => bigIntToBuffer(BigInt.asUintN(64, n))
const bytesBE8 = (n: bigint) => bigIntToBytes(BigInt.asUintN(64, n))
const tdKey = (n: bigint, hash: Buffer) =>
Buffer.concat([HEADER_PREFIX, bufBE8(n), hash, TD_SUFFIX])
const tdKey = (n: bigint, hash: Uint8Array) =>
concatBytes(HEADER_PREFIX, bytesBE8(n), hash, TD_SUFFIX)
const headerKey = (n: bigint, hash: Buffer) => Buffer.concat([HEADER_PREFIX, bufBE8(n), hash])
const headerKey = (n: bigint, hash: Uint8Array) => concatBytes(HEADER_PREFIX, bytesBE8(n), hash)
const bodyKey = (n: bigint, hash: Buffer) => Buffer.concat([BODY_PREFIX, bufBE8(n), hash])
const bodyKey = (n: bigint, hash: Uint8Array) => concatBytes(BODY_PREFIX, bytesBE8(n), hash)
const numberToHashKey = (n: bigint) => Buffer.concat([HEADER_PREFIX, bufBE8(n), NUM_SUFFIX])
const numberToHashKey = (n: bigint) => concatBytes(HEADER_PREFIX, bytesBE8(n), NUM_SUFFIX)
const hashToNumberKey = (hash: Buffer) => Buffer.concat([BLOCK_HASH_PEFIX, hash])
const hashToNumberKey = (hash: Uint8Array) => concatBytes(BLOCK_HASH_PEFIX, hash)

@@ -71,3 +71,3 @@ /**

bodyKey,
bufBE8,
bytesBE8,
hashToNumberKey,

@@ -74,0 +74,0 @@ HEAD_BLOCK_KEY,

import { Block } from '@ethereumjs/block'
import { RLP } from '@ethereumjs/rlp'
import { bufArrToArr } from '@ethereumjs/util'
import { bufBE8 } from './constants'
import { DBOp, DBTarget } from './operation'
import { bytesBE8 } from './constants.js'
import { DBOp, DBTarget } from './operation.js'

@@ -15,4 +14,4 @@ import type { BlockHeader } from '@ethereumjs/block'

function DBSetTD(TD: bigint, blockNumber: bigint, blockHash: Buffer): DBOp {
return DBOp.set(DBTarget.TotalDifficulty, Buffer.from(RLP.encode(TD)), {
function DBSetTD(TD: bigint, blockNumber: bigint, blockHash: Uint8Array): DBOp {
return DBOp.set(DBTarget.TotalDifficulty, RLP.encode(TD), {
blockNumber,

@@ -54,3 +53,3 @@ blockHash,

) {
const bodyValue = Buffer.from(RLP.encode(bufArrToArr(blockBody.raw()).slice(1)))
const bodyValue = RLP.encode(blockBody.raw().slice(1))
dbOps.push(

@@ -67,4 +66,4 @@ DBOp.set(DBTarget.Body, bodyValue, {

function DBSetHashToNumber(blockHash: Buffer, blockNumber: bigint): DBOp {
const blockNumber8Byte = bufBE8(blockNumber)
function DBSetHashToNumber(blockHash: Uint8Array, blockNumber: bigint): DBOp {
const blockNumber8Byte = bytesBE8(blockNumber)
return DBOp.set(DBTarget.HashToNumber, blockNumber8Byte, {

@@ -75,3 +74,3 @@ blockHash,

function DBSaveLookups(blockHash: Buffer, blockNumber: bigint, skipNumIndex?: boolean): DBOp[] {
function DBSaveLookups(blockHash: Uint8Array, blockNumber: bigint, skipNumIndex?: boolean): DBOp[] {
const ops = []

@@ -82,3 +81,3 @@ if (skipNumIndex !== true) {

const blockNumber8Bytes = bufBE8(blockNumber)
const blockNumber8Bytes = bytesBE8(blockNumber)
ops.push(

@@ -85,0 +84,0 @@ DBOp.set(DBTarget.HashToNumber, blockNumber8Bytes, {

import { Block, BlockHeader, valuesArrayToHeaderData } from '@ethereumjs/block'
import { RLP } from '@ethereumjs/rlp'
import { KECCAK256_RLP, KECCAK256_RLP_ARRAY, arrToBufArr, bufferToBigInt } from '@ethereumjs/util'
import {
KECCAK256_RLP,
KECCAK256_RLP_ARRAY,
bytesToBigInt,
bytesToHex,
equalsBytes,
unprefixedHexToBytes,
} from '@ethereumjs/util'
import { Cache } from './cache'
import { DBOp, DBTarget } from './operation'
import { Cache } from './cache.js'
import { DBOp, DBTarget } from './operation.js'
import type { DBOpData, DatabaseKey } from './operation'
import type { BlockBodyBuffer, BlockBuffer, BlockOptions } from '@ethereumjs/block'
import type { DatabaseKey } from './operation.js'
import type { BlockBodyBytes, BlockBytes, BlockOptions } from '@ethereumjs/block'
import type { Common } from '@ethereumjs/common'
import type { AbstractLevel } from 'abstract-level'
import type { BatchDBOp, DB, DBObject, DelBatch, PutBatch } from '@ethereumjs/util'
class NotFoundError extends Error {
public code: string = 'LEVEL_NOT_FOUND'
constructor(blockNumber: bigint) {
super(`Key ${blockNumber.toString()} was not found`)
// `Error.captureStackTrace` is not defined in some browser contexts
if (typeof Error.captureStackTrace !== 'undefined') {
Error.captureStackTrace(this, this.constructor)
}
}
}
/**

@@ -35,3 +29,3 @@ * @hidden

export type CacheMap = { [key: string]: Cache<Buffer> }
export type CacheMap = { [key: string]: Cache<Uint8Array> }

@@ -45,11 +39,8 @@ /**

private _cache: CacheMap
private _common: Common
private _db: AbstractLevel<string | Buffer | Uint8Array, string | Buffer, string | Buffer>
public readonly common: Common
private _db: DB<Uint8Array | string, Uint8Array | string | DBObject>
constructor(
db: AbstractLevel<string | Buffer | Uint8Array, string | Buffer, string | Buffer>,
common: Common
) {
constructor(db: DB<Uint8Array | string, Uint8Array | string | DBObject>, common: Common) {
this._db = db
this._common = common
this.common = common
this._cache = {

@@ -67,8 +58,13 @@ td: new Cache({ max: 1024 }),

*/
async getHeads(): Promise<{ [key: string]: Buffer }> {
const heads = await this.get(DBTarget.Heads)
async getHeads(): Promise<{ [key: string]: Uint8Array }> {
const heads = (await this.get(DBTarget.Heads)) as DBObject
if (heads === undefined) return heads
const decodedHeads: { [key: string]: Uint8Array } = {}
for (const key of Object.keys(heads)) {
heads[key] = Buffer.from(heads[key])
// Heads are stored in DB as hex strings since Level converts Uint8Arrays
// to nested JSON objects when they are included in a value being stored
// in the DB
decodedHeads[key] = unprefixedHexToBytes(heads[key] as string)
}
return heads
return decodedHeads
}

@@ -79,3 +75,3 @@

*/
async getHeadHeader(): Promise<Buffer> {
async getHeadHeader(): Promise<Uint8Array | undefined> {
return this.get(DBTarget.HeadHeader)

@@ -87,3 +83,3 @@ }

*/
async getHeadBlock(): Promise<Buffer> {
async getHeadBlock(): Promise<Uint8Array | undefined> {
return this.get(DBTarget.HeadBlock)

@@ -96,3 +92,3 @@ }

*/
async getBlock(blockId: Buffer | bigint | number): Promise<Block> {
async getBlock(blockId: Uint8Array | bigint | number): Promise<Block | undefined> {
if (typeof blockId === 'number' && Number.isInteger(blockId)) {

@@ -104,3 +100,4 @@ blockId = BigInt(blockId)

let hash
if (Buffer.isBuffer(blockId)) {
if (blockId === undefined) return undefined
if (blockId instanceof Uint8Array) {
hash = blockId

@@ -115,35 +112,32 @@ number = await this.hashToNumber(blockId)

if (hash === undefined || number === undefined) return undefined
const header = await this.getHeader(hash, number)
let body: BlockBodyBuffer
try {
body = await this.getBody(hash, number)
} catch (error: any) {
if (error.code !== 'LEVEL_NOT_FOUND') {
throw error
}
const body = await this.getBody(hash, number)
if (body[0].length === 0 && body[1].length === 0) {
// Do extra validations on the header since we are assuming empty transactions and uncles
if (
!header.transactionsTrie.equals(KECCAK256_RLP) ||
!header.uncleHash.equals(KECCAK256_RLP_ARRAY)
) {
throw error
if (!equalsBytes(header.transactionsTrie, KECCAK256_RLP)) {
throw new Error('transactionsTrie root should be equal to hash of null')
}
body = [[], []]
if (!equalsBytes(header.uncleHash, KECCAK256_RLP_ARRAY)) {
throw new Error('uncle hash should be equal to hash of empty array')
}
// If this block had empty withdrawals push an empty array in body
if (header.withdrawalsRoot !== undefined) {
// Do extra validations for withdrawal before assuming empty withdrawals
if (!header.withdrawalsRoot.equals(KECCAK256_RLP)) {
throw error
if (
!equalsBytes(header.withdrawalsRoot, KECCAK256_RLP) &&
(body.length !== 3 || body[2]?.length === 0)
) {
throw new Error('withdrawals root shoot be equal to hash of null when no withdrawals')
}
body.push([])
if (body.length !== 3) body.push([])
}
}
const blockData = [header.raw(), ...body] as BlockBuffer
const opts: BlockOptions = { common: this._common }
const blockData = [header.raw(), ...body] as BlockBytes
const opts: BlockOptions = { common: this.common }
if (number === BigInt(0)) {
opts.hardforkByTTD = await this.getTotalDifficulty(hash, BigInt(0))
opts.setHardfork = await this.getTotalDifficulty(hash, BigInt(0))
} else {
opts.hardforkByTTD = await this.getTotalDifficulty(header.parentHash, number - BigInt(1))
opts.setHardfork = await this.getTotalDifficulty(header.parentHash, number - BigInt(1))
}

@@ -156,5 +150,8 @@ return Block.fromValuesArray(blockData, opts)

*/
async getBody(blockHash: Buffer, blockNumber: bigint): Promise<BlockBodyBuffer> {
async getBody(blockHash: Uint8Array, blockNumber: bigint): Promise<BlockBodyBytes> {
const body = await this.get(DBTarget.Body, { blockHash, blockNumber })
return arrToBufArr(RLP.decode(Uint8Array.from(body))) as BlockBodyBuffer
if (body === undefined) {
return [[], []]
}
return RLP.decode(body) as BlockBodyBytes
}

@@ -165,17 +162,17 @@

*/
async getHeader(blockHash: Buffer, blockNumber: bigint) {
async getHeader(blockHash: Uint8Array, blockNumber: bigint) {
const encodedHeader = await this.get(DBTarget.Header, { blockHash, blockNumber })
const headerValues = arrToBufArr(RLP.decode(Uint8Array.from(encodedHeader)))
const headerValues = RLP.decode(encodedHeader)
const opts: BlockOptions = { common: this._common }
const opts: BlockOptions = { common: this.common }
if (blockNumber === BigInt(0)) {
opts.hardforkByTTD = await this.getTotalDifficulty(blockHash, BigInt(0))
opts.setHardfork = await this.getTotalDifficulty(blockHash, BigInt(0))
} else {
// Lets fetch the parent hash but not by number since this block might not
// be in canonical chain
const headerData = valuesArrayToHeaderData(headerValues as Buffer[])
const parentHash = headerData.parentHash as Buffer
opts.hardforkByTTD = await this.getTotalDifficulty(parentHash, blockNumber - BigInt(1))
const headerData = valuesArrayToHeaderData(headerValues as Uint8Array[])
const parentHash = headerData.parentHash as Uint8Array
opts.setHardfork = await this.getTotalDifficulty(parentHash, blockNumber - BigInt(1))
}
return BlockHeader.fromValuesArray(headerValues as Buffer[], opts)
return BlockHeader.fromValuesArray(headerValues as Uint8Array[], opts)
}

@@ -186,5 +183,5 @@

*/
async getTotalDifficulty(blockHash: Buffer, blockNumber: bigint): Promise<bigint> {
async getTotalDifficulty(blockHash: Uint8Array, blockNumber: bigint): Promise<bigint> {
const td = await this.get(DBTarget.TotalDifficulty, { blockHash, blockNumber })
return bufferToBigInt(Buffer.from(RLP.decode(Uint8Array.from(td)) as Uint8Array))
return bytesToBigInt(RLP.decode(td) as Uint8Array)
}

@@ -195,5 +192,8 @@

*/
async hashToNumber(blockHash: Buffer): Promise<bigint> {
async hashToNumber(blockHash: Uint8Array): Promise<bigint | undefined> {
const value = await this.get(DBTarget.HashToNumber, { blockHash })
return bufferToBigInt(value)
if (value === undefined) {
throw new Error(`value for ${bytesToHex(blockHash)} not found in DB`)
}
return value !== undefined ? bytesToBigInt(value) : undefined
}

@@ -204,8 +204,5 @@

*/
async numberToHash(blockNumber: bigint): Promise<Buffer> {
if (blockNumber < BigInt(0)) {
throw new NotFoundError(blockNumber)
}
return this.get(DBTarget.NumberToHash, { blockNumber })
async numberToHash(blockNumber: bigint): Promise<Uint8Array | undefined> {
const value = await this.get(DBTarget.NumberToHash, { blockNumber })
return value
}

@@ -223,3 +220,2 @@

const dbKey = dbGetOperation.baseDBOp.key
const dbOpts = dbGetOperation.baseDBOp

@@ -230,8 +226,9 @@ if (cacheString !== undefined) {

}
let value = this._cache[cacheString].get(dbKey)
if (!value) {
value = await this._db.get(dbKey, dbOpts)
if (value) {
if (value === undefined) {
value = (await this._db.get(dbKey, {
keyEncoding: dbGetOperation.baseDBOp.keyEncoding,
valueEncoding: dbGetOperation.baseDBOp.valueEncoding,
})) as Uint8Array | undefined
if (value !== undefined) {
this._cache[cacheString].set(dbKey, value)

@@ -243,4 +240,6 @@ }

}
return this._db.get(dbKey, dbOpts)
return this._db.get(dbKey, {
keyEncoding: dbGetOperation.baseDBOp.keyEncoding,
valueEncoding: dbGetOperation.baseDBOp.valueEncoding,
})
}

@@ -252,8 +251,25 @@

async batch(ops: DBOp[]) {
const convertedOps: DBOpData[] = ops.map((op) => op.baseDBOp)
const convertedOps: BatchDBOp[] = ops.map((op) => {
const type =
op.baseDBOp.type !== undefined
? op.baseDBOp.type
: op.baseDBOp.value !== undefined
? 'put'
: 'del'
const convertedOp = {
key: op.baseDBOp.key,
value: op.baseDBOp.value,
type,
opts: {
keyEncoding: op.baseDBOp.keyEncoding,
valueEncoding: op.baseDBOp.valueEncoding,
},
}
if (type === 'put') return convertedOp as PutBatch
else return convertedOp as DelBatch
})
// update the current cache for each operation
ops.map((op) => op.updateCache(this._cache))
return this._db.batch(convertedOps as any)
return this._db.batch(convertedOps)
}
}

@@ -0,1 +1,3 @@

import { KeyEncoding, ValueEncoding } from '@ethereumjs/util'
import {

@@ -10,5 +12,5 @@ HEADS_KEY,

tdKey,
} from './constants'
} from './constants.js'
import type { CacheMap } from './manager'
import type { CacheMap } from './manager.js'

@@ -34,7 +36,7 @@ export enum DBTarget {

export interface DBOpData {
type?: string
key: Buffer | string
keyEncoding: string
valueEncoding?: string
value?: Buffer | object
type?: 'put' | 'del'
key: Uint8Array | string
keyEncoding: KeyEncoding
valueEncoding?: ValueEncoding
value?: Uint8Array | object
}

@@ -45,3 +47,3 @@

blockNumber?: bigint
blockHash?: Buffer
blockHash?: Uint8Array
}

@@ -62,4 +64,4 @@

key: '',
keyEncoding: 'buffer',
valueEncoding: 'buffer',
keyEncoding: KeyEncoding.Bytes,
valueEncoding: ValueEncoding.Bytes,
}

@@ -70,3 +72,3 @@

this.baseDBOp.key = HEADS_KEY
this.baseDBOp.valueEncoding = 'json'
this.baseDBOp.valueEncoding = ValueEncoding.JSON
break

@@ -76,2 +78,3 @@ }

this.baseDBOp.key = HEAD_HEADER_KEY
this.baseDBOp.keyEncoding = KeyEncoding.String
break

@@ -81,2 +84,3 @@ }

this.baseDBOp.key = HEAD_BLOCK_KEY
this.baseDBOp.keyEncoding = KeyEncoding.String
break

@@ -117,3 +121,7 @@ }

// set operation: note: value/key is not in default order
public static set(operationTarget: DBTarget, value: Buffer | object, key?: DatabaseKey): DBOp {
public static set(
operationTarget: DBTarget,
value: Uint8Array | object,
key?: DatabaseKey
): DBOp {
const dbOperation = new DBOp(operationTarget, key)

@@ -124,5 +132,5 @@ dbOperation.baseDBOp.value = value

if (operationTarget === DBTarget.Heads) {
dbOperation.baseDBOp.valueEncoding = 'json'
dbOperation.baseDBOp.valueEncoding = ValueEncoding.JSON
} else {
dbOperation.baseDBOp.valueEncoding = 'binary'
dbOperation.baseDBOp.valueEncoding = ValueEncoding.Bytes
}

@@ -142,3 +150,3 @@

if (this.baseDBOp.type === 'put') {
Buffer.isBuffer(this.baseDBOp.value) &&
this.baseDBOp.value instanceof Uint8Array &&
cacheMap[this.cacheString].set(this.baseDBOp.key, this.baseDBOp.value)

@@ -145,0 +153,0 @@ } else if (this.baseDBOp.type === 'del') {

@@ -1,4 +0,10 @@

export { Blockchain } from './blockchain'
export { CasperConsensus, CliqueConsensus, Consensus, EthashConsensus } from './consensus'
export { BlockchainInterface, BlockchainOptions } from './types'
export * from './utils'
export { Blockchain } from './blockchain.js'
export { CasperConsensus, CliqueConsensus, EthashConsensus } from './consensus/index.js'
export {
DBOp,
DBSaveLookups,
DBSetBlockOrHeader,
DBSetHashToNumber,
DBSetTD,
} from './db/helpers.js'
export * from './types.js'

@@ -1,6 +0,5 @@

import type { Consensus } from './consensus'
import type { GenesisState } from './genesisStates'
import type { Blockchain } from '.'
import type { Block, BlockHeader } from '@ethereumjs/block'
import type { Common } from '@ethereumjs/common'
import type { AbstractLevel } from 'abstract-level'
import type { Common, ConsensusAlgorithm } from '@ethereumjs/common'
import type { DB, DBObject, GenesisState } from '@ethereumjs/util'

@@ -24,3 +23,3 @@ export type OnBlock = (block: Block, reorg: boolean) => Promise<void> | void

*/
delBlock(blockHash: Buffer): Promise<void>
delBlock(blockHash: Uint8Array): Promise<void>

@@ -30,3 +29,3 @@ /**

*/
getBlock(blockId: Buffer | number | bigint): Promise<Block>
getBlock(blockId: Uint8Array | number | bigint): Promise<Block>

@@ -50,5 +49,5 @@ /**

/**
* Returns a copy of the blockchain
* Returns a shallow copy of the blockchain that may share state with the original
*/
copy(): BlockchainInterface
shallowCopy(): BlockchainInterface

@@ -67,14 +66,16 @@ /**

*/
getIteratorHead?(name?: string): Promise<Block>
getIteratorHead(name?: string): Promise<Block>
/**
* Gets total difficulty for a block specified by hash and 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
*/
getTotalDifficulty?(hash: Buffer, number?: bigint): Promise<bigint>
setIteratorHead(tag: string, headHash: Uint8Array): Promise<void>
/**
* Returns the genesis state of the blockchain.
* All values are provided as hex-prefixed strings.
* Gets total difficulty for a block specified by hash and number
*/
genesisState?(): GenesisState
getTotalDifficulty?(hash: Uint8Array, number?: bigint): Promise<bigint>

@@ -84,9 +85,51 @@ /**

*/
getCanonicalHeadBlock?(): Promise<Block>
getCanonicalHeadBlock(): Promise<Block>
}
export interface GenesisOptions {
/**
* The blockchain only initializes successfully if it has a genesis block. If
* there is no block available in the DB and a `genesisBlock` is provided,
* then the provided `genesisBlock` will be used as genesis. If no block is
* present in the DB and no block is provided, then the genesis block as
* provided from the `common` will be used.
*/
genesisBlock?: Block
/**
* If you are using a custom chain {@link Common}, pass the genesis state.
*
* Pattern 1 (with genesis state see {@link GenesisState} for format):
*
* ```javascript
* {
* '0x0...01': '0x100', // For EoA
* }
* ```
*
* Pattern 2 (with complex genesis state, containing contract accounts and storage).
* Note that in {@link AccountState} there are two
* accepted types. This allows to easily insert accounts in the genesis state:
*
* A complex genesis state with Contract and EoA states would have the following format:
*
* ```javascript
* {
* '0x0...01': '0x100', // For EoA
* '0x0...02': ['0x1', '0xRUNTIME_BYTECODE', [[storageKey1, storageValue1], [storageKey2, storageValue2]]] // For contracts
* }
* ```
*/
genesisState?: GenesisState
/**
* State root of the genesis state
*/
genesisStateRoot?: Uint8Array
}
/**
* This are the options that the Blockchain constructor can receive.
*/
export interface BlockchainOptions {
export interface BlockchainOptions extends GenesisOptions {
/**

@@ -113,10 +156,5 @@ * Specify the chain and hardfork by passing a {@link Common} instance.

* Database to store blocks and metadata.
* Should be an `abstract-leveldown` compliant store
* wrapped with `encoding-down`.
* For example:
* `levelup(encode(leveldown('./db1')))`
* or use the `level` convenience package:
* `new MemoryLevel('./db1')`
* Can be any database implementation that adheres to the `DB` interface
*/
db?: AbstractLevel<string | Buffer | Uint8Array, string | Buffer, string | Buffer>
db?: DB<Uint8Array | string | number, Uint8Array | string | DBObject>

@@ -143,40 +181,49 @@ /**

/**
* The blockchain only initializes successfully if it has a genesis block. If
* there is no block available in the DB and a `genesisBlock` is provided,
* then the provided `genesisBlock` will be used as genesis. If no block is
* present in the DB and no block is provided, then the genesis block as
* provided from the `common` will be used.
* Optional custom consensus that implements the {@link Consensus} class
*/
genesisBlock?: Block
consensus?: Consensus
}
/**
* Interface that a consensus class needs to implement.
*/
export interface Consensus {
algorithm: ConsensusAlgorithm | string
/**
* If you are using a custom chain {@link Common}, pass the genesis state.
*
* Pattern 1 (with genesis state see {@link GenesisState} for format):
*
* ```javascript
* {
* '0x0...01': '0x100', // For EoA
* }
* ```
*
* Pattern 2 (with complex genesis state, containing contract accounts and storage).
* Note that in {@link AccountState} there are two
* accepted types. This allows to easily insert accounts in the genesis state:
*
* A complex genesis state with Contract and EoA states would have the following format:
*
* ```javascript
* {
* '0x0...01': '0x100', // For EoA
* '0x0...02': ['0x1', '0xRUNTIME_BYTECODE', [[storageKey1, storageValue1], [storageKey2, storageValue2]]] // For contracts
* }
* ```
* Initialize genesis for consensus mechanism
* @param genesisBlock genesis block
*/
genesisState?: GenesisState
genesisInit(genesisBlock: Block): Promise<void>
/**
* Optional custom consensus that implements the {@link Consensus} class
* Set up consensus mechanism
*/
consensus?: Consensus
setup({ blockchain }: ConsensusOptions): Promise<void>
/**
* Validate block consensus parameters
* @param block block to be validated
*/
validateConsensus(block: Block): Promise<void>
validateDifficulty(header: BlockHeader): Promise<void>
/**
* Update consensus on new block
* @param block new block
* @param commonAncestor common ancestor block header (optional)
* @param ancientHeaders array of ancestor block headers (optional)
*/
newBlock(
block: Block,
commonAncestor?: BlockHeader,
ancientHeaders?: BlockHeader[]
): Promise<void>
}
/**
* Options when initializing a class that implements the Consensus interface.
*/
export interface ConsensusOptions {
blockchain: Blockchain
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc