Skip to content

Portal: Implementation of finalized history network WIP #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

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
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
5 changes: 4 additions & 1 deletion portal/client/nimbus_portal_client_conf.nim
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,10 @@ type

portalSubnetworks* {.
desc: "Select which networks (Portal sub-protocols) to enable",
defaultValue: {PortalSubnetwork.history, PortalSubnetwork.beacon},
defaultValue: {
PortalSubnetwork.finalizedHistory, PortalSubnetwork.history,
PortalSubnetwork.beacon,
},
name: "portal-subnetworks"
.}: set[PortalSubnetwork]

Expand Down
121 changes: 121 additions & 0 deletions portal/network/finalized_history/content/content_keys.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# 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
# Note: Need to add this unused value as a case object with an enum without
# a 0 valueis not allowed: "low(contentType) must be 0 for discriminant".
# For prefix values that are in the enum gap, the deserialization will fail
# at runtime as is wanted.
# In the future it might be possible that this will fail at compile time for
# the SSZ Union type, but currently it is allowed in the implementation, and
# the SSZ spec is not explicit about disallowing this.
unused = 0x00
blockBody = 0x09
receipts = 0x0A

BlockNumberKey* = object
blockNumber*: uint64

ContentKey* = object
case contentType*: ContentType
of unused:
discard
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)
)

proc readSszBytes*(data: openArray[byte], val: var ContentKey) {.raises: [SszError].} =
mixin readSszValue
if data.len() > 0 and data[0] == ord(unused):
raise newException(MalformedSszError, "SSZ selector is unused value")

readSszValue(data, val)

func encode*(contentKey: ContentKey): ContentKeyByteList =
doAssert(contentKey.contentType != unused)
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 unused:
raiseAssert "ContentKey may not have unused value as content type"
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 unused:
raiseAssert "ContentKey may not have unused value as content type"
of blockBody:
res.add($x.blockBodyKey)
of receipts:
res.add($x.receiptsKey)

res.add(")")

res
16 changes: 16 additions & 0 deletions portal/network/finalized_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
Receipts* = seq[Receipt]
ContentValueType* = BlockBody | Receipts
12 changes: 12 additions & 0 deletions portal/network/finalized_history/finalized_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/finalized_history/finalized_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, ./finalized_history_network

export results, finalized_history_network

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

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