Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

blockchain: move chain,skeleton to blockchain #3548

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
import { BlockHeader, createBlockFromValuesArray } from '@ethereumjs/block'
import { CliqueConsensus, createBlockchain } from '@ethereumjs/blockchain'
import { ConsensusAlgorithm, Hardfork } from '@ethereumjs/common'
import { BIGINT_0, BIGINT_1, equalsBytes } from '@ethereumjs/util'

import { LevelDB } from '../execution/level.js'
import { Event } from '../types.js'
import { CliqueConsensus } from './consensus/index.js'
import { createBlockchain } from './constructors.js'
import { LevelDB } from './level.js'

import type { Config } from '../config.js'
import type { Blockchain } from './blockchain.js'
import type { ConsensusDict } from './types.js'
import type { Block } from '@ethereumjs/block'
import type { Blockchain, ConsensusDict } from '@ethereumjs/blockchain'
import type { Common } from '@ethereumjs/common'
import type { DB, DBObject, GenesisState } from '@ethereumjs/util'
import type { AbstractLevel } from 'abstract-level'
import type { Logger as WinstonLogger } from 'winston'
export type Logger = WinstonLogger

export type ChainConfig = {
syncTargetHeight?: bigint
syncedStateRemovalPeriod: number
engineParentLookupMaxDepth: number
snapTransitionSafeDepth: bigint
skeletonSubchainMergeMinimum: number
numBlocksPerIteration: number
skeletonFillCanonicalBackStep: number
maxPerRequest: number
chainCommon: Common
logger?: Logger
}

/**
* The options that the Blockchain constructor can receive.
Expand All @@ -19,7 +35,7 @@ export interface ChainOptions {
/**
* Client configuration instance
*/
config: Config
config: ChainConfig

/**
* Database to store blocks and metadata. Should be an abstract-leveldown compliant store.
Expand Down Expand Up @@ -123,7 +139,7 @@ type BlockCache = {
* @memberof module:blockchain
*/
export class Chain {
public config: Config
public config: ChainConfig
public chainDB: DB<string | Uint8Array, string | Uint8Array | DBObject>
public blockchain: Blockchain
public blockCache: BlockCache
Expand Down Expand Up @@ -203,6 +219,21 @@ export class Chain {
this.opened = false
}

superMsg(msgs: string | string[], meta?: any) {
if (typeof msgs === 'string') {
msgs = [msgs]
}
let len = 0
for (const msg of msgs) {
len = msg.length > len ? msg.length : len
}
this.config.logger?.info('-'.repeat(len), meta)
for (const msg of msgs) {
this.config.logger?.info(msg, meta)
}
this.config.logger?.info('-'.repeat(len), meta)
}

/**
* Resets _header, _blocks
*/
Expand Down Expand Up @@ -265,7 +296,7 @@ export class Chain {

this.config.chainCommon.events.on('hardforkChanged', async (hardfork: string) => {
const block = this.config.chainCommon.hardforkBlock()
this.config.superMsg(
this.superMsg(
`New hardfork reached 🪢 ! hardfork=${hardfork} ${block !== null ? `block=${block}` : ''}`
)
})
Expand Down Expand Up @@ -351,34 +382,30 @@ export class Chain {
td: headers.td,
})
if (this.config.chainCommon.hardforkGteHardfork(nextBlockHf, Hardfork.Paris)) {
this.config.logger.info('*'.repeat(85))
this.config.logger.info(
this.config.logger?.info('*'.repeat(85))
this.config.logger?.info(
`Paris (Merge) hardfork reached 🐼 👉 👈 🐼 ! block=${headers.height} td=${headers.td}`
)
this.config.logger.info('-'.repeat(85))
this.config.logger.info(' ')
this.config.logger.info('Consensus layer client (CL) needed for continued sync:')
this.config.logger.info(
this.config.logger?.info('-'.repeat(85))
this.config.logger?.info(' ')
this.config.logger?.info('Consensus layer client (CL) needed for continued sync:')
this.config.logger?.info(
'https://ethereum.org/en/developers/docs/nodes-and-clients/#consensus-clients'
)
this.config.logger.info(' ')
this.config.logger.info(
this.config.logger?.info(' ')
this.config.logger?.info(
'Make sure to have the JSON RPC (--rpc) and Engine API (--rpcEngine) endpoints exposed'
)
this.config.logger.info('and JWT authentication configured (see client README).')
this.config.logger.info(' ')
this.config.logger.info('*'.repeat(85))
this.config.logger.info(
this.config.logger?.info('and JWT authentication configured (see client README).')
this.config.logger?.info(' ')
this.config.logger?.info('*'.repeat(85))
this.config.logger?.info(
`Transitioning to PoS! First block for CL-framed execution: block=${
headers.height + BIGINT_1
}`
)
}
}

if (emit) {
this.config.events.emit(Event.CHAIN_UPDATED)
}
}

/**
Expand Down
2 changes: 2 additions & 0 deletions packages/blockchain/src/db/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ const BLOCK_HASH_PEFIX = utf8ToBytes('H')
*/
const BODY_PREFIX = utf8ToBytes('b')

export const SKELETON_PREFIX = utf8ToBytes('s')

// Utility functions

/**
Expand Down
2 changes: 2 additions & 0 deletions packages/blockchain/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { Blockchain } from './blockchain.js'
export { Chain, ChainConfig } from './chain.js'
export { CasperConsensus, CliqueConsensus, EthashConsensus } from './consensus/index.js'
export * from './constructors.js'
export {
Expand All @@ -9,4 +10,5 @@ export {
DBSetTD,
} from './db/helpers.js'
export * from './helpers.js'
export { errSyncMerged, PutStatus, Skeleton } from './skeleton.js'
export * from './types.js'
122 changes: 122 additions & 0 deletions packages/blockchain/src/level.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { KeyEncoding, ValueEncoding } from '@ethereumjs/util'
import { MemoryLevel } from 'memory-level'

import type { BatchDBOp, DB, DBObject, EncodingOpts } from '@ethereumjs/util'
import type { AbstractLevel } from 'abstract-level'

// Helper to infer the `valueEncoding` option for `putting` a value in a levelDB
const getEncodings = (opts: EncodingOpts = {}) => {
const encodings = { keyEncoding: '', valueEncoding: '' }
switch (opts.valueEncoding) {
case ValueEncoding.String:
encodings.valueEncoding = 'utf8'
break
case ValueEncoding.Bytes:
encodings.valueEncoding = 'view'
break
case ValueEncoding.JSON:
encodings.valueEncoding = 'json'
break
default:
encodings.valueEncoding = 'view'
}
switch (opts.keyEncoding) {
case KeyEncoding.Bytes:
encodings.keyEncoding = 'view'
break
case KeyEncoding.Number:
case KeyEncoding.String:
encodings.keyEncoding = 'utf8'
break
default:
encodings.keyEncoding = 'utf8'
}

return encodings
}

/**
* LevelDB is a thin wrapper around the underlying levelup db,
* corresponding to the {@link DB}
*/
export class LevelDB<
TKey extends Uint8Array | string = Uint8Array | string,
TValue extends Uint8Array | string | DBObject = Uint8Array | string | DBObject
> implements DB<TKey, TValue>
{
_leveldb: AbstractLevel<string | Uint8Array, string | Uint8Array, string | Uint8Array>

/**
* Initialize a DB instance. If `leveldb` is not provided, DB
* defaults to an [in-memory store](https://github.com/Level/memdown).
* @param leveldb - An abstract-leveldown compliant store
*/
constructor(
leveldb?: AbstractLevel<string | Uint8Array, string | Uint8Array, string | Uint8Array>
) {
this._leveldb = leveldb ?? new MemoryLevel()
}

/**
* @inheritDoc
*/
async get(key: TKey, opts?: EncodingOpts): Promise<TValue | undefined> {
let value
const encodings = getEncodings(opts)

try {
value = await this._leveldb.get(key, encodings)
if (value === null) return undefined
} catch (error: any) {
// https://github.com/Level/abstract-level/blob/915ad1317694d0ce8c580b5ab85d81e1e78a3137/abstract-level.js#L309
// This should be `true` if the error came from LevelDB
// so we can check for `NOT true` to identify any non-404 errors
if (error.notFound !== true) {
throw error
}
}
// eslint-disable-next-line
if (value instanceof Buffer) value = Uint8Array.from(value)
return value as TValue
}

/**
* @inheritDoc
*/
async put(key: TKey, val: TValue, opts?: {}): Promise<void> {
const encodings = getEncodings(opts)
await this._leveldb.put(key, val, encodings)
}

/**
* @inheritDoc
*/
async del(key: TKey): Promise<void> {
await this._leveldb.del(key)
}

/**
* @inheritDoc
*/
async batch(opStack: BatchDBOp<TKey, TValue>[]): Promise<void> {
const levelOps = []
for (const op of opStack) {
const encodings = getEncodings(op.opts)
levelOps.push({ ...op, ...encodings })
}

// TODO: Investigate why as any is necessary
await this._leveldb.batch(levelOps as any)
}

/**
* @inheritDoc
*/
shallowCopy(): DB<TKey, TValue> {
return new LevelDB<TKey, TValue>(this._leveldb)
}

open() {
return this._leveldb.open()
}
}
Loading