Skip to content
Draft
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
6 changes: 3 additions & 3 deletions packages/client/src/util/inclineClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@
>() as unknown as AbstractLevel<string | Uint8Array, string | Uint8Array, string | Uint8Array>
} else {
chainDB = new Level<string | Uint8Array, string | Uint8Array>(
`${datadir}/${common.chainName()}/chainDB`,
`${datadir}/${common.chainName()}/chain`,

Check warning on line 42 in packages/client/src/util/inclineClient.ts

View check run for this annotation

Codecov / codecov/patch

packages/client/src/util/inclineClient.ts#L42

Added line #L42 was not covered by tests
) as unknown as AbstractLevel<string | Uint8Array, string | Uint8Array, string | Uint8Array>

stateDB = new Level<string | Uint8Array, string | Uint8Array>(
`${datadir}/${common.chainName()}/stateDB`,
`${datadir}/${common.chainName()}/state`,

Check warning on line 46 in packages/client/src/util/inclineClient.ts

View check run for this annotation

Codecov / codecov/patch

packages/client/src/util/inclineClient.ts#L46

Added line #L46 was not covered by tests
) as unknown as AbstractLevel<string | Uint8Array, string | Uint8Array, string | Uint8Array>
metaDB = new Level<string | Uint8Array, string | Uint8Array>(
`${datadir}/${common.chainName()}/metaDB`,
`${datadir}/${common.chainName()}/meta`,

Check warning on line 49 in packages/client/src/util/inclineClient.ts

View check run for this annotation

Codecov / codecov/patch

packages/client/src/util/inclineClient.ts#L49

Added line #L49 was not covered by tests
) as unknown as AbstractLevel<string | Uint8Array, string | Uint8Array, string | Uint8Array>
}
let validateConsensus = false
Expand Down
1 change: 1 addition & 0 deletions packages/client/src/util/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { bytesToHex } from '@ethereumjs/util'
export * from './inclineClient.ts'
export * from './parse.ts'
export * from './rpc.ts'
export * from './purge.ts'
// See: https://stackoverflow.com/a/50053801
const __dirname = dirname(fileURLToPath(import.meta.url))

Expand Down
78 changes: 78 additions & 0 deletions packages/client/src/util/purge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { DBOp } from '@ethereumjs/blockchain'
import { type BatchDBOp, type DelBatch, concatBytes, intToBytes } from '@ethereumjs/util'
import { Level } from 'level'
import { DBKey } from './metaDBManager.ts'

export const DBTarget = {
NumberToHash: 4,
Body: 6,
Header: 7,
Receipts: 8,
} as const

async function initDBs(dataDir: string, chain: string) {
const chainDir = `${dataDir}/${chain}`

Check warning on line 14 in packages/client/src/util/purge.ts

View check run for this annotation

Codecov / codecov/patch

packages/client/src/util/purge.ts#L13-L14

Added lines #L13 - L14 were not covered by tests

// Chain DB
const chainDataDir = `${chainDir}/chain`
const chainDB = new Level<string | Uint8Array, string | Uint8Array>(chainDataDir)
await chainDB.open()

Check failure on line 19 in packages/client/src/util/purge.ts

View workflow job for this annotation

GitHub Actions / client / test-client

test/integration/purge.spec.ts > should mine blocks and then purge a few > should work

Error: Database failed to open ❯ ../../node_modules/classic-level/node_modules/abstract-level/abstract-level.js:183:17 ❯ ClassicLevel.open ../../node_modules/classic-level/node_modules/abstract-level/abstract-level.js:231:9 ❯ initDBs src/util/purge.ts:19:3 ❯ purgeHistory src/util/purge.ts:35:31 ❯ test/integration/purge.spec.ts:116:5 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'LEVEL_DATABASE_NOT_OPEN' } Caused by: Caused by: Error: IO error: lock ./datadir/devnet/chain/LOCK: Resource temporarily unavailable ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'LEVEL_LOCKED' }

Check failure on line 19 in packages/client/src/util/purge.ts

View workflow job for this annotation

GitHub Actions / client / test-client

test/integration/purge.spec.ts > should mine blocks and then purge a few > should work

Error: Database failed to open ❯ ../../node_modules/classic-level/node_modules/abstract-level/abstract-level.js:183:17 ❯ ClassicLevel.open ../../node_modules/classic-level/node_modules/abstract-level/abstract-level.js:231:9 ❯ initDBs src/util/purge.ts:19:3 ❯ purgeHistory src/util/purge.ts:35:31 ❯ test/integration/purge.spec.ts:116:5 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'LEVEL_DATABASE_NOT_OPEN' } Caused by: Caused by: Error: IO error: lock ./datadir/devnet/chain/LOCK: Resource temporarily unavailable ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'LEVEL_LOCKED' }

Check failure on line 19 in packages/client/src/util/purge.ts

View workflow job for this annotation

GitHub Actions / client / test-client

test/integration/purge.spec.ts > should mine blocks and then purge a few > should work

Error: Database failed to open ❯ ../../node_modules/classic-level/node_modules/abstract-level/abstract-level.js:183:17 ❯ ClassicLevel.open ../../node_modules/classic-level/node_modules/abstract-level/abstract-level.js:231:9 ❯ initDBs src/util/purge.ts:19:3 ❯ purgeHistory src/util/purge.ts:35:31 ❯ test/integration/purge.spec.ts:116:5 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'LEVEL_DATABASE_NOT_OPEN' } Caused by: Caused by: Error: IO error: lock ./datadir/devnet/chain/LOCK: Resource temporarily unavailable ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'LEVEL_LOCKED' }

Check warning on line 19 in packages/client/src/util/purge.ts

View check run for this annotation

Codecov / codecov/patch

packages/client/src/util/purge.ts#L17-L19

Added lines #L17 - L19 were not covered by tests

// Meta DB (receipts, logs, indexes, skeleton chain)
const metaDataDir = `${chainDir}/meta`
const metaDB = new Level<string | Uint8Array, string | Uint8Array>(metaDataDir)
await metaDB.open()

Check warning on line 24 in packages/client/src/util/purge.ts

View check run for this annotation

Codecov / codecov/patch

packages/client/src/util/purge.ts#L22-L24

Added lines #L22 - L24 were not covered by tests

return { chainDB, metaDB }
}

Check warning on line 27 in packages/client/src/util/purge.ts

View check run for this annotation

Codecov / codecov/patch

packages/client/src/util/purge.ts#L26-L27

Added lines #L26 - L27 were not covered by tests

export async function purgeHistory(
dataDir: string,
chain: string = 'mainnet',
before: bigint = 15537393n,
headers: boolean = false,
) {
const { chainDB, metaDB } = await initDBs(dataDir, chain)

Check warning on line 35 in packages/client/src/util/purge.ts

View check run for this annotation

Codecov / codecov/patch

packages/client/src/util/purge.ts#L29-L35

Added lines #L29 - L35 were not covered by tests

const dbOps: DBOp[] = []
const metaDBOps: {

Check warning on line 38 in packages/client/src/util/purge.ts

View check run for this annotation

Codecov / codecov/patch

packages/client/src/util/purge.ts#L37-L38

Added lines #L37 - L38 were not covered by tests
type: 'del'
key: Uint8Array
}[] = []
let blockNumber = before
while (blockNumber > 0n) {
const blockHashDBOp = DBOp.get(DBTarget.NumberToHash, { blockNumber })
const blockHash = await chainDB.get(blockHashDBOp.baseDBOp.key, {
keyEncoding: blockHashDBOp.baseDBOp.keyEncoding,
valueEncoding: blockHashDBOp.baseDBOp.valueEncoding,
})

Check warning on line 48 in packages/client/src/util/purge.ts

View check run for this annotation

Codecov / codecov/patch

packages/client/src/util/purge.ts#L41-L48

Added lines #L41 - L48 were not covered by tests

if (!(blockHash instanceof Uint8Array)) {
blockNumber--
continue
}
dbOps.push(DBOp.del(DBTarget.Body, { blockHash, blockNumber }))
if (headers) {
dbOps.push(DBOp.del(DBTarget.Header, { blockHash, blockNumber }))
}
const receiptsKey = concatBytes(intToBytes(DBKey.Receipts), blockHash)
metaDBOps.push({
type: 'del',
key: receiptsKey,
})
blockNumber--
}
const convertedOps: BatchDBOp[] = dbOps.map((op) => {
const convertedOp = {
key: op.baseDBOp.key,
type: 'del',
opts: {
keyEncoding: op.baseDBOp.keyEncoding,
},
}
return convertedOp as DelBatch
})

Check warning on line 74 in packages/client/src/util/purge.ts

View check run for this annotation

Codecov / codecov/patch

packages/client/src/util/purge.ts#L50-L74

Added lines #L50 - L74 were not covered by tests

await chainDB.batch(convertedOps)
await metaDB.batch(metaDBOps, { keyEncoding: 'view' })
}

Check warning on line 78 in packages/client/src/util/purge.ts

View check run for this annotation

Codecov / codecov/patch

packages/client/src/util/purge.ts#L76-L78

Added lines #L76 - L78 were not covered by tests
123 changes: 123 additions & 0 deletions packages/client/test/integration/purge.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { rmSync } from 'fs'
import { resolve } from 'path'
import { Hardfork, createCommonFromGethGenesis } from '@ethereumjs/common'
import {
Address,
bytesToHex,
concatBytes,
hexToBytes,
parseGethGenesisState,
} from '@ethereumjs/util'
import { assert, afterAll, beforeAll, describe, it } from 'vitest'
import type { EthereumClient } from '../../src/client.ts'
import { Config } from '../../src/config.ts'
import { getLogger } from '../../src/logging.ts'
import { Event } from '../../src/types.ts'
import { createInlineClient, purgeHistory } from '../../src/util/index.ts'

async function setupDevnet(prefundAddress: Address) {
const addr = prefundAddress.toString().slice(2)
const consensusConfig = {
clique: {
period: 1,
epoch: 30000,
},
}
const defaultChainData = {
config: {
chainId: 123456,
homesteadBlock: 0,
eip150Block: 0,
eip150Hash: '0x0000000000000000000000000000000000000000000000000000000000000000',
eip155Block: 0,
eip158Block: 0,
byzantiumBlock: 0,
constantinopleBlock: 0,
petersburgBlock: 0,
istanbulBlock: 0,
berlinBlock: 0,
londonBlock: 0,
...consensusConfig,
},
nonce: '0x0',
timestamp: '0x614b3731',
gasLimit: '0x47b760',
difficulty: '0x1',
mixHash: '0x0000000000000000000000000000000000000000000000000000000000000000',
coinbase: '0x0000000000000000000000000000000000000000',
number: '0x0',
gasUsed: '0x0',
parentHash: '0x0000000000000000000000000000000000000000000000000000000000000000',
baseFeePerGas: 7,
}
const extraData = concatBytes(new Uint8Array(32), prefundAddress.toBytes(), new Uint8Array(65))

const chainData = {
...defaultChainData,
extraData: bytesToHex(extraData),
alloc: { [addr]: { balance: '0x10000000000000000000' } },
}

const common = createCommonFromGethGenesis(chainData, {
chain: 'devnet',
hardfork: Hardfork.London,
})
const customGenesisState = parseGethGenesisState(chainData)
return { common, customGenesisState }
}

const accounts: [Address, Uint8Array][] = [
[
new Address(hexToBytes('0x0b90087d864e82a284dca15923f3776de6bb016f')),
hexToBytes('0x64bf9cc30328b0e42387b3c82c614e6386259136235e20c1357bd11cdee86993'),
],
]

async function minerSetup(): Promise<EthereumClient[]> {
const { common, customGenesisState } = await setupDevnet(accounts[0][0])
const config1 = new Config({
common,
accountCache: 10000,
storageCache: 1000,
mine: true,
accounts,
logger: getLogger({ logLevel: 'warn' }),
})

const miner = await createInlineClient(config1, common, customGenesisState, './datadir', false)

return [miner]
}

describe('should mine blocks and then purge a few', () => {
beforeAll(() => {
rmSync(resolve(__dirname, '../../datadir/devnet'), { recursive: true, force: true })
})
it('should work', async () => {
const [miner] = await minerSetup()

const targetHeight = BigInt(5)
await new Promise((resolve) => {
miner.config.events.on(Event.SYNC_SYNCHRONIZED, (chainHeight) => {
if (chainHeight === targetHeight) {
assert.equal(miner.chain.blocks.height, targetHeight, 'synced blocks successfully')
resolve(undefined)
}
})
})

await miner.stop()
// We have to manually close the dbs or we'll get a db lock error
// @ts-expect-error leveldb is not visible in interface (but it's there!)
await miner.chain.chainDB['_leveldb'].close()
await miner.service.execution['metaDB']?.close()

// Purge history prior to block 3 and delete headers
await purgeHistory('./datadir', 'devnet', 2n, true)

assert.throws(async () => miner.chain.getBlock(1n), 'should not have block 1')
}, 60000)
afterAll(() => {
rmSync(resolve(__dirname, '../../datadir/devnet'), { recursive: true, force: true })
})
})
Loading