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

History: in-memory blockhash index #626

Merged
merged 8 commits into from
Sep 18, 2024
2 changes: 1 addition & 1 deletion packages/cli/src/rpc/modules/ultralight.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export class ultralight {
const [blockNum, blockHash] = params
try {
this.logger(`Indexed block ${BigInt(blockNum)} / ${blockNum} to ${blockHash} `)
await this._history!.indexBlockhash(BigInt(blockNum), blockHash)
await this._history!.indexBlockHash(BigInt(blockNum), blockHash)
return `Added ${blockNum} to block index`
} catch (err: any) {
throw {
Expand Down
6 changes: 5 additions & 1 deletion packages/portalnetwork/src/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,13 +289,17 @@ export class PortalNetwork extends (EventEmitter as { new (): PortalNetworkEvent
public start = async () => {
await this.discv5.start()
await this.db.open()
const storedIndex = await this.db.getBlockIndex()
for (const network of this.networks.values()) {
try {
// Check for stored radius in db
const storedRadius = await network.db.db.get('radius')
await network.setRadius(BigInt(storedRadius))
} catch {
continue
// No action
}
if (network instanceof HistoryNetwork) {
network.blockHashIndex = storedIndex
}
await network.prune()
// Start kbucket refresh on 30 second interval
Expand Down
7 changes: 3 additions & 4 deletions packages/portalnetwork/src/client/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,9 @@ export class ETH {
this.logger.extend('getBlockByHash')(lookupResponse)
if (!lookupResponse || !('content' in lookupResponse)) {
// Header not found by hash, try to find by number if known
const blockIndex = await this.history!.blockIndex()
const blockNumber = blockIndex.get(blockHash)
const blockNumber = this.history!.blockHashToNumber(blockHash)
if (blockNumber !== undefined) {
const block = await this.getBlockByNumber(BigInt(blockNumber), includeTransactions)
const block = await this.getBlockByNumber(blockNumber, includeTransactions)
return block
}
return undefined
Expand Down Expand Up @@ -191,7 +190,7 @@ export class ETH {
}
} else {
// Header not found by number. If block hash is known, search for header by hash
const blockHash = await this.history!.blockNumberToHash(BigInt(blockNumber))
const blockHash = this.history!.blockNumberToHash(BigInt(blockNumber))
if (blockHash !== undefined) {
return this.getBlockByHash(blockHash, includeTransactions)
}
Expand Down
24 changes: 17 additions & 7 deletions packages/portalnetwork/src/networks/history/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,23 @@ export class HistoryNetwork extends BaseNetwork {
networkName = 'HistoryNetwork'
logger: Debugger
gossipManager: GossipManager
public blockHashIndex: Map<string, string>
constructor({ client, db, radius, maxStorage }: BaseNetworkConfig) {
super({ client, networkId: NetworkId.HistoryNetwork, db, radius, maxStorage })
this.networkId = NetworkId.HistoryNetwork
this.logger = debug(this.enr.nodeId.slice(0, 5)).extend('Portal').extend('HistoryNetwork')
this.gossipManager = new GossipManager(this)
this.routingTable.setLogger(this.logger)
this.blockHashIndex = new Map()
}

public blockNumberToHash(blockNumber: bigint): string | undefined {
return this.blockHashIndex.get('0x' + blockNumber.toString(16))
}

public blockHashToNumber(blockHash: string): bigint | undefined {
const blockNumber = this.blockHashIndex.get(blockHash)
return blockNumber === undefined ? undefined : BigInt(blockNumber)
}

/**
Expand All @@ -55,7 +66,7 @@ export class HistoryNetwork extends BaseNetwork {
const contentType = contentKey[0]
if (contentType === HistoryNetworkContentType.BlockHeaderByNumber) {
const blockNumber = decodeHistoryNetworkContentKey(contentKey).keyOpt
const blockHash = await this.blockNumberToHash(<bigint>blockNumber)
const blockHash = this.blockNumberToHash(<bigint>blockNumber)
if (blockHash === undefined) {
return undefined
}
Expand All @@ -68,12 +79,11 @@ export class HistoryNetwork extends BaseNetwork {
return value !== undefined ? hexToBytes(value) : undefined
}

public indexBlockhash = async (number: bigint, blockHash: string) => {
public indexBlockHash = async (number: bigint, blockHash: string) => {
const blockNumber = '0x' + number.toString(16)
const blockindex = await this.blockIndex()
blockindex.set(blockNumber, blockHash)
blockindex.set(blockHash, blockNumber)
await this.setBlockIndex(blockindex)
this.blockHashIndex.set(blockNumber, blockHash)
this.blockHashIndex.set(blockHash, blockNumber)
await this.portal.db.storeBlockIndex(this.blockHashIndex)
}

/**
Expand Down Expand Up @@ -173,7 +183,7 @@ export class HistoryNetwork extends BaseNetwork {
}
}
}
await this.indexBlockhash(header.number, toHexString(header.hash()))
await this.indexBlockHash(header.number, toHexString(header.hash()))
return header.hash()
}

Expand Down
2 changes: 1 addition & 1 deletion packages/portalnetwork/src/networks/history/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ export const addRLPSerializedBlock = async (
header: header.serialize(),
proof: { selector: 0, value: null },
})
await network.indexBlockhash(header.number, toHexString(header.hash()))
await network.indexBlockHash(header.number, toHexString(header.hash()))

await network.store(headerKey, headerProof)
}
Expand Down
20 changes: 1 addition & 19 deletions packages/portalnetwork/src/networks/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,12 @@ export abstract class BaseNetwork extends EventEmitter {
public networkId: NetworkId
abstract networkName: string
public enr: SignableENR
public blockIndex: () => Promise<Map<string, string>>
public setBlockIndex: (blockIndex: Map<string, string>) => Promise<void>

portal: PortalNetwork
constructor({ client, networkId, db, radius, maxStorage }: BaseNetworkConfig) {
super()
this.networkId = networkId
this.logger = client.logger.extend(this.constructor.name)
this.blockIndex = () => {
return client.db.getBlockIndex()
}
this.setBlockIndex = (blockIndex: Map<string, string>) => {
return client.db.storeBlockIndex(blockIndex)
}
this.enr = client.discv5.enr
this.checkIndex = 0
this.nodeRadius = radius ?? 2n ** 256n - 1n
Expand Down Expand Up @@ -117,17 +110,6 @@ export abstract class BaseNetwork extends EventEmitter {
return this.portal.discv5.findEnr(nodeId)
}

public async blockNumberToHash(blockNumber: bigint): Promise<string | undefined> {
const blockIndex = await this.blockIndex()
return blockIndex.get('0x' + blockNumber.toString(16))
}

public async blockHashToNumber(blockHash: string): Promise<bigint | undefined> {
const blockIndex = await this.blockIndex()
const blockNumber = blockIndex.get(blockHash)
return blockNumber === undefined ? undefined : BigInt(blockNumber)
}

public async put(contentKey: string, content: string) {
await this.db.put(contentKey, content)
}
Expand Down
2 changes: 1 addition & 1 deletion packages/portalnetwork/test/client/eth/ethCall.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ describe.skip('ethCall', () => {
// toHexString(block.header.hash()),
// history,
// )
// await history.indexBlockhash(block.header.number, toHexString(block.header.hash()))
// await history.indexBlockHash(block.header.number, toHexString(block.header.hash()))
// const greeterInput = '0xcfae3217'
// const tx: RpcTx = {
// to: address.toString(),
Expand Down
16 changes: 8 additions & 8 deletions packages/portalnetwork/test/networks/history/blockIndex.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,13 @@ describe('BlockIndex', async () => {
it('should store block header', () => {
assert.equal(stored, headerWithProof)
})
it('should save block index', async () => {
it('should save block index', () => {
const numberHex = '0x' + BigInt(1).toString(16)
const index = await history.blockIndex()
assert.isTrue(index.has(numberHex))
assert.equal(index.get(numberHex), hash)
assert.isTrue(history.blockHashIndex.has(numberHex))
assert.equal(history.blockHashIndex.get(numberHex), hash)
})
it('should store blockIndex in DB', async () => {
const expected = Array.from(await history.blockIndex())
const expected = Array.from(history.blockHashIndex)
const stored = JSON.parse(await ultralight.db.db.get('block_index'))
assert.deepEqual(stored, expected)
})
Expand All @@ -45,10 +44,11 @@ describe('BlockIndex', async () => {
supportedNetworks: [{ networkId: NetworkId.HistoryNetwork }],
db: ultralight.db.db,
})
await ultralight2.start()
const history2 = ultralight2.networks.get(NetworkId.HistoryNetwork) as HistoryNetwork
it('should start with blockIndex in DB', async () => {
const expected = await history.blockIndex()
const stored = await history2.blockIndex()
assert.deepEqual(stored, expected)
const expected = history.blockHashIndex
const stored = history2.blockHashIndex
assert.deepEqual(stored, expected, `Expected ${expected.size} but got ${stored.size}`)
})
})
Loading