Skip to content

Commit e640849

Browse files
committed
add and handle further modifications and flags to handle skeleton functionality
1 parent a69d63f commit e640849

File tree

5 files changed

+58
-105
lines changed

5 files changed

+58
-105
lines changed

packages/blockchain/src/blockchain.ts

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
import { DBManager } from './db/manager.js'
2626
import { DBTarget } from './db/operation.js'
2727

28+
import type { OptimisticOpts } from './db/operation.js'
2829
import type {
2930
BlockchainEvents,
3031
BlockchainInterface,
@@ -272,8 +273,8 @@ export class Blockchain implements BlockchainInterface {
272273
* heads/hashes are overwritten.
273274
* @param block - The block to be added to the blockchain
274275
*/
275-
async putBlock(block: Block, optimistic: boolean = false) {
276-
await this._putBlockOrHeader(block, optimistic)
276+
async putBlock(block: Block, opts?: OptimisticOpts) {
277+
await this._putBlockOrHeader(block, opts)
277278
}
278279

279280
/**
@@ -344,7 +345,7 @@ export class Blockchain implements BlockchainInterface {
344345
* header using the iterator method.
345346
* @hidden
346347
*/
347-
private async _putBlockOrHeader(item: Block | BlockHeader, optimistic: boolean = false) {
348+
private async _putBlockOrHeader(item: Block | BlockHeader, optimisticOpts?: OptimisticOpts) {
348349
await this.runWithLock<void>(async () => {
349350
// Save the current sane state incase _putBlockOrHeader midway with some
350351
// dirty changes in head trackers
@@ -362,13 +363,13 @@ export class Blockchain implements BlockchainInterface {
362363
if (isGenesis) {
363364
if (equalsBytes(this.genesisBlock.hash(), block.hash())) {
364365
// Try to re-put the existing genesis block, accept this
365-
optimistic = false
366+
// genesis block is not optimistic
367+
optimisticOpts = undefined
366368
return
367369
}
368370
throw new Error(
369371
'Cannot put a different genesis block than current blockchain genesis: create a new Blockchain',
370372
)
371-
// genesis block is not optimistic
372373
}
373374

374375
if (block.common.chainId() !== this.common.chainId()) {
@@ -377,12 +378,12 @@ export class Blockchain implements BlockchainInterface {
377378
)
378379
}
379380

380-
if (this._validateBlocks && !isGenesis && item instanceof Block) {
381+
if (this._validateBlocks && !isGenesis && item instanceof Block && optimisticOpts === undefined) {
381382
// this calls into `getBlock`, which is why we cannot lock yet
382383
await this.validateBlock(block)
383384
}
384385

385-
if (this._validateConsensus) {
386+
if (this._validateConsensus && optimisticOpts === undefined) {
386387
await this.consensus!.validateConsensus(block)
387388
}
388389

@@ -397,20 +398,20 @@ export class Blockchain implements BlockchainInterface {
397398
if (!block.isGenesis()) {
398399
td += parentTd
399400
}
400-
// since its linked its no more optimistic
401-
optimistic = false
402401
} catch (e) {
403402
// opimistic insertion does care about td
404-
if (!optimistic) {
403+
if (optimisticOpts === undefined) {
405404
throw e
406405
}
407406
}
408407

409408
let dbOps: DBOp[] = []
410-
if (optimistic) {
409+
if (optimisticOpts !== undefined) {
411410
dbOps = dbOps.concat(DBSetBlockOrHeader(item))
412411
dbOps.push(DBSetHashToNumber(blockHash, blockNumber))
413-
dbOps.push(DBOp.set(DBTarget.OptimisticNumberToHash, blockHash, { blockNumber }))
412+
if (optimisticOpts.fcUed) {
413+
dbOps.push(DBOp.set(DBTarget.OptimisticNumberToHash, blockHash, { blockNumber }))
414+
}
414415
await this.dbManager.batch(dbOps)
415416
} else {
416417
const currentTd = { header: BIGINT_0, block: BIGINT_0 }
@@ -676,13 +677,16 @@ export class Blockchain implements BlockchainInterface {
676677
* this will be immediately looked up, otherwise it will wait until we have
677678
* unlocked the DB
678679
*/
679-
async getBlock(blockId: Uint8Array | number | bigint): Promise<Block> {
680+
async getBlock(
681+
blockId: Uint8Array | number | bigint,
682+
optimisticOpts?: OptimisticOpts,
683+
): Promise<Block> {
680684
// cannot wait for a lock here: it is used both in `validate` of `Block`
681685
// (calls `getBlock` to get `parentHash`) it is also called from `runBlock`
682686
// in the `VM` if we encounter a `BLOCKHASH` opcode: then a bigint is used we
683687
// need to then read the block from the canonical chain Q: is this safe? We
684688
// know it is OK if we call it from the iterator... (runBlock)
685-
const block = await this.dbManager.getBlock(blockId)
689+
const block = await this.dbManager.getBlock(blockId, optimisticOpts)
686690

687691
if (block === undefined) {
688692
if (typeof blockId === 'object') {

packages/blockchain/src/db/manager.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
import { Cache } from './cache.js'
1313
import { DBOp, DBTarget } from './operation.js'
1414

15-
import type { DatabaseKey } from './operation.js'
15+
import type { DatabaseKey, OptimisticOpts } from './operation.js'
1616
import type { Block, BlockBodyBytes, BlockBytes, BlockOptions } from '@ethereumjs/block'
1717
import type { Common } from '@ethereumjs/common'
1818
import type { BatchDBOp, DB, DBObject, DelBatch, PutBatch } from '@ethereumjs/util'
@@ -47,6 +47,7 @@ export class DBManager {
4747
body: new Cache({ max: 256 }),
4848
numberToHash: new Cache({ max: 2048 }),
4949
hashToNumber: new Cache({ max: 2048 }),
50+
optimisticNumberToHash: new Cache({ max: 2048 }),
5051
}
5152
}
5253

@@ -86,7 +87,7 @@ export class DBManager {
8687
*/
8788
async getBlock(
8889
blockId: Uint8Array | bigint | number,
89-
optimistic: boolean = false,
90+
optimisticOpts?: OptimisticOpts,
9091
): Promise<Block | undefined> {
9192
if (typeof blockId === 'number' && Number.isInteger(blockId)) {
9293
blockId = BigInt(blockId)
@@ -98,13 +99,30 @@ export class DBManager {
9899
if (blockId instanceof Uint8Array) {
99100
hash = blockId
100101
number = await this.hashToNumber(blockId)
102+
if (number === undefined) {
103+
return undefined
104+
}
105+
106+
if (optimisticOpts?.fcUed === true) {
107+
let optimisticHash = await this.optimisticNumberToHash(number)
108+
if (optimisticHash === undefined && optimisticOpts.linked === true) {
109+
optimisticHash = await this.numberToHash(number)
110+
}
111+
if (optimisticHash === undefined || !equalsBytes(optimisticHash, hash)) {
112+
return undefined
113+
}
114+
}
101115
} else if (typeof blockId === 'bigint') {
102116
number = blockId
103-
if (optimistic) {
117+
if (optimisticOpts !== undefined) {
118+
if (!optimisticOpts.fcUed) {
119+
throw Error(`Invalid fcUed optimistic block by number lookup`)
120+
}
104121
hash = await this.optimisticNumberToHash(blockId)
105-
}
106-
// hash will be undefined if it no optimistic lookup was done or if that was not successful
107-
if (hash === undefined) {
122+
if (hash === undefined && optimisticOpts.linked === true) {
123+
hash = await this.numberToHash(blockId)
124+
}
125+
} else {
108126
hash = await this.numberToHash(blockId)
109127
}
110128
} else {

packages/blockchain/src/db/operation.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import {
1414

1515
import type { CacheMap } from './manager.js'
1616

17+
export type OptimisticOpts = { fcUed: boolean; linked?: boolean }
18+
1719
export enum DBTarget {
1820
Heads,
1921
HeadHeader,

packages/blockchain/src/types.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { OptimisticOpts } from './db/operation.js'
12
import type { Blockchain } from './index.js'
23
import type { Block, BlockHeader } from '@ethereumjs/block'
34
import type { Common, ConsensusAlgorithm } from '@ethereumjs/common'
@@ -16,7 +17,7 @@ export interface BlockchainInterface {
1617
*
1718
* @param block - The block to be added to the blockchain.
1819
*/
19-
putBlock(block: Block): Promise<void>
20+
putBlock(block: Block, optimisticOpts?: OptimisticOpts): Promise<void>
2021

2122
/**
2223
* Deletes a block from the blockchain. All child blocks in the chain are
@@ -29,7 +30,7 @@ export interface BlockchainInterface {
2930
/**
3031
* Returns a block by its hash or number.
3132
*/
32-
getBlock(blockId: Uint8Array | number | bigint): Promise<Block>
33+
getBlock(blockId: Uint8Array | number | bigint, optimisticOpts?: OptimisticOpts): Promise<Block>
3334

3435
/**
3536
* Iterates through blocks starting at the specified iterator head and calls

packages/client/src/service/skeleton.ts

Lines changed: 11 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1362,21 +1362,7 @@ export class Skeleton extends MetaDBManager {
13621362
*/
13631363
private async putBlock(block: Block, onlyUnfinalized: boolean = false): Promise<boolean> {
13641364
// Serialize the block with its hardfork so that its easy to load the block latter
1365-
const rlp = this.serialize({ hardfork: block.common.hardfork(), blockRLP: block.serialize() })
1366-
await this.put(DBKey.SkeletonUnfinalizedBlockByHash, block.hash(), rlp)
1367-
1368-
if (!onlyUnfinalized) {
1369-
await this.put(DBKey.SkeletonBlock, bigIntToBytes(block.header.number), rlp)
1370-
// this is duplication of the unfinalized blocks but for now an easy reference
1371-
// will be pruned on finalization changes. this could be simplified and deduped
1372-
// but will anyway will move into blockchain class and db on upcoming skeleton refactor
1373-
await this.put(
1374-
DBKey.SkeletonBlockHashToNumber,
1375-
block.hash(),
1376-
bigIntToBytes(block.header.number),
1377-
)
1378-
}
1379-
1365+
await this.chain.blockchain.putBlock(block, { fcUed: !onlyUnfinalized })
13801366
return true
13811367
}
13821368

@@ -1395,22 +1381,10 @@ export class Skeleton extends MetaDBManager {
13951381
* Gets a block from the skeleton or canonical db by number.
13961382
*/
13971383
async getBlock(number: bigint, onlyCanonical = false): Promise<Block | undefined> {
1398-
try {
1399-
const skeletonBlockRlp = await this.get(DBKey.SkeletonBlock, bigIntToBytes(number))
1400-
if (skeletonBlockRlp === null) {
1401-
throw Error(`SkeletonBlock rlp lookup failed for ${number} onlyCanonical=${onlyCanonical}`)
1402-
}
1403-
return this.skeletonBlockRlpToBlock(skeletonBlockRlp)
1404-
} catch (error: any) {
1405-
// If skeleton is linked, it probably has deleted the block and put it into the chain
1406-
if (onlyCanonical && !this.status.linked) return undefined
1407-
// As a fallback, try to get the block from the canonical chain in case it is available there
1408-
try {
1409-
return await this.chain.getBlock(number)
1410-
} catch (error) {
1411-
return undefined
1412-
}
1413-
}
1384+
return this.chain.blockchain.dbManager.getBlock(number, {
1385+
fcUed: onlyCanonical,
1386+
linked: this.status.linked,
1387+
})
14141388
}
14151389

14161390
/**
@@ -1420,63 +1394,17 @@ export class Skeleton extends MetaDBManager {
14201394
hash: Uint8Array,
14211395
onlyCanonical: boolean = false,
14221396
): Promise<Block | undefined> {
1423-
const number = await this.get(DBKey.SkeletonBlockHashToNumber, hash)
1424-
if (number) {
1425-
const block = await this.getBlock(bytesToBigInt(number), onlyCanonical)
1426-
if (block !== undefined && equalsBytes(block.hash(), hash)) {
1427-
return block
1428-
}
1429-
}
1430-
1431-
if (onlyCanonical === true && !this.status.linked) {
1432-
return undefined
1433-
}
1434-
1435-
let block = onlyCanonical === false ? await this.getUnfinalizedBlock(hash) : undefined
1436-
if (block === undefined && (onlyCanonical === false || this.status.linked)) {
1437-
block = await this.chain.getBlock(hash).catch((_e) => undefined)
1438-
}
1439-
1440-
if (onlyCanonical === false) {
1441-
return block
1442-
} else {
1443-
if (this.status.linked && block !== undefined) {
1444-
const canBlock = await this.chain.getBlock(block.header.number).catch((_e) => undefined)
1445-
if (canBlock !== undefined && equalsBytes(canBlock.hash(), block.hash())) {
1446-
// block is canonical
1447-
return block
1448-
}
1449-
}
1450-
1451-
// no canonical block found or the block was not canonical
1452-
return undefined
1453-
}
1454-
}
1455-
1456-
async getUnfinalizedBlock(hash: Uint8Array): Promise<Block | undefined> {
1457-
try {
1458-
const skeletonBlockRlp = await this.get(DBKey.SkeletonUnfinalizedBlockByHash, hash)
1459-
if (skeletonBlockRlp === null) {
1460-
throw Error(`SkeletonUnfinalizedBlockByHash rlp lookup failed for hash=${short(hash)}`)
1461-
}
1462-
return this.skeletonBlockRlpToBlock(skeletonBlockRlp)
1463-
} catch (_e) {
1464-
return undefined
1465-
}
1397+
return this.chain.blockchain.dbManager.getBlock(hash, {
1398+
fcUed: !onlyCanonical,
1399+
linked: this.status.linked,
1400+
})
14661401
}
14671402

14681403
/**
14691404
* Deletes a skeleton block from the db by number
14701405
*/
1471-
async deleteBlock(block: Block): Promise<boolean> {
1472-
try {
1473-
await this.delete(DBKey.SkeletonBlock, bigIntToBytes(block.header.number))
1474-
await this.delete(DBKey.SkeletonBlockHashToNumber, block.hash())
1475-
await this.delete(DBKey.SkeletonUnfinalizedBlockByHash, block.hash())
1476-
return true
1477-
} catch (error: any) {
1478-
return false
1479-
}
1406+
async deleteBlock(_block: Block): Promise<boolean> {
1407+
return true
14801408
}
14811409

14821410
/**

0 commit comments

Comments
 (0)