Skip to content

Portal: Implementation of new history network #3427

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

Merged
merged 10 commits into from
Jul 24, 2025
Merged
110 changes: 110 additions & 0 deletions portal/network/history/content/content_keys.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Nimbus
# Copyright (c) 2025 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.

{.push raises: [].}

import results, stint, ssz_serialization, ../../../common/common_types

export ssz_serialization, common_types, results

type
ContentType* = enum
blockBody = 0x00
receipts = 0x01

BlockNumberKey* = object
blockNumber*: uint64

ContentKey* = object
case contentType*: ContentType
of blockBody:
blockBodyKey*: BlockNumberKey
of receipts:
receiptsKey*: BlockNumberKey

func blockBodyContentKey*(blockNumber: uint64): ContentKey =
ContentKey(
contentType: blockBody, blockBodyKey: BlockNumberKey(blockNumber: blockNumber)
)

func receiptsContentKey*(blockNumber: uint64): ContentKey =
ContentKey(
contentType: receipts, receiptsKey: BlockNumberKey(blockNumber: blockNumber)
)

template blockNumber*(contentKey: ContentKey): uint64 =
## Returns the block number for the given content key
case contentKey.contentType
of blockBody: contentKey.blockBodyKey.blockNumber
of receipts: contentKey.receiptsKey.blockNumber

proc readSszBytes*(data: openArray[byte], val: var ContentKey) {.raises: [SszError].} =
mixin readSszValue

readSszValue(data, val)

func encode*(contentKey: ContentKey): ContentKeyByteList =
ContentKeyByteList.init(SSZ.encode(contentKey))

func decode*(contentKey: ContentKeyByteList): Opt[ContentKey] =
try:
Opt.some(SSZ.decode(contentKey.asSeq(), ContentKey))
except SerializationError:
return Opt.none(ContentKey)

func reverseBits(n: uint64, width: int): uint64 =
## Reverse the lowest `width` bits of `n`
# TODO: can improve
var res: uint64 = 0
for i in 0 ..< width:
if ((n shr i) and 1) != 0:
res = res or (1'u64 shl (width - 1 - i))
res

const
CYCLE_BITS = 16
OFFSET_BITS = 256 - CYCLE_BITS # 240
REVERSED_OFFSET_BITS = 64 - CYCLE_BITS # 48

func toContentId*(blockNumber: uint64, contentType: ContentType): UInt256 =
## Returns the content id for a given block number
let
cycleBits = blockNumber mod (1'u64 shl CYCLE_BITS)
offsetBits = blockNumber div (1'u64 shl CYCLE_BITS)

reversedOffsetBits = reverseBits(offsetBits, REVERSED_OFFSET_BITS)

(cycleBits.stuint(256) shl OFFSET_BITS) or
(reversedOffsetBits.stuint(256) shl (OFFSET_BITS - REVERSED_OFFSET_BITS)) or
ord(contentType).stuint(256)

func toContentId*(contentKey: ContentKey): ContentId =
case contentKey.contentType
of blockBody:
toContentId(contentKey.blockBodyKey.blockNumber, contentKey.contentType)
of receipts:
toContentId(contentKey.receiptsKey.blockNumber, contentKey.contentType)

func toContentId*(bytes: ContentKeyByteList): Opt[ContentId] =
let contentKey = ?bytes.decode()
Opt.some(contentKey.toContentId())

func `$`*(x: BlockNumberKey): string =
"block_number: " & $x.blockNumber

func `$`*(x: ContentKey): string =
var res = "(type: " & $x.contentType & ", "

case x.contentType
of blockBody:
res.add($x.blockBodyKey)
of receipts:
res.add($x.receiptsKey)

res.add(")")

res
16 changes: 16 additions & 0 deletions portal/network/history/content/content_values.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Nimbus
# Copyright (c) 2025 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.

{.push raises: [].}

import eth/common/blocks_rlp, eth/common/receipts_rlp

export blocks_rlp, receipts_rlp

type
StoredReceipts* = seq[StoredReceipt]
ContentValueType* = BlockBody | StoredReceipts
12 changes: 12 additions & 0 deletions portal/network/history/history_content.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Nimbus
# Copyright (c) 2025 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.

{.push raises: [].}

import ./content/content_keys, ./content/content_values

export content_keys, content_values
22 changes: 22 additions & 0 deletions portal/network/history/history_endpoints.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Nimbus
# Copyright (c) 2025 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.

{.push raises: [].}

import results, chronicles, chronos, ./history_network

export results, history_network

proc getBlockBody*(
n: HistoryNetwork, header: Header
): Future[Opt[BlockBody]] {.async: (raises: [CancelledError], raw: true).} =
n.getContent(blockBodyContentKey(header.number), BlockBody, header)

proc getReceipts*(
n: HistoryNetwork, header: Header
): Future[Opt[StoredReceipts]] {.async: (raises: [CancelledError], raw: true).} =
n.getContent(receiptsContentKey(header.number), StoredReceipts, header)
Loading
Loading