From dded8643d9a6003d3a5a23c21ec3235cbe4d67d0 Mon Sep 17 00:00:00 2001 From: Kim De Mey Date: Tue, 19 Dec 2023 19:59:38 +0100 Subject: [PATCH] Update the beacon and state network content key prefixes (#1950) --- fluffy/network/beacon/beacon_content.nim | 23 ++++++++--- fluffy/network/beacon/beacon_db.nim | 4 ++ fluffy/network/beacon/beacon_network.nim | 2 + fluffy/network/state/state_content.nim | 34 ++++++++++++++--- .../test_beacon_content.nim | 25 ++++++++++++ .../mainnet/test_state_content.nim | 38 +++++++++++++++---- vendor/portal-spec-tests | 2 +- 7 files changed, 109 insertions(+), 19 deletions(-) diff --git a/fluffy/network/beacon/beacon_content.nim b/fluffy/network/beacon/beacon_content.nim index a49ab190c..3a52090b7 100644 --- a/fluffy/network/beacon/beacon_content.nim +++ b/fluffy/network/beacon/beacon_content.nim @@ -1,4 +1,4 @@ -# Nimbus - Portal Network +# Fluffy - Portal Network # Copyright (c) 2022-2023 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). @@ -30,10 +30,12 @@ const type ContentType* = enum - lightClientBootstrap = 0x00 - lightClientUpdate = 0x01 - lightClientFinalityUpdate = 0x02 - lightClientOptimisticUpdate = 0x03 + # Note: See same note as for state_content.nim + unused = 0x00 + lightClientBootstrap = 0x10 + lightClientUpdate = 0x11 + lightClientFinalityUpdate = 0x12 + lightClientOptimisticUpdate = 0x13 # TODO: Consider how we will gossip bootstraps? # In consensus light client operation a node trusts only one bootstrap hash, @@ -59,6 +61,8 @@ type ContentKey* = object case contentType*: ContentType + of unused: + discard of lightClientBootstrap: lightClientBootstrapKey*: LightClientBootstrapKey of lightClientUpdate: @@ -84,8 +88,17 @@ func forkDigestAtEpoch*( forkDigests.atEpoch(epoch, cfg) func encode*(contentKey: ContentKey): ByteList = + doAssert(contentKey.contentType != unused) ByteList.init(SSZ.encode(contentKey)) +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 unused value") + + readSszValue(data, val) + func decode*(contentKey: ByteList): Opt[ContentKey] = try: Opt.some(SSZ.decode(contentKey.asSeq(), ContentKey)) diff --git a/fluffy/network/beacon/beacon_db.nim b/fluffy/network/beacon/beacon_db.nim index a3edc50b4..2b84c86fa 100644 --- a/fluffy/network/beacon/beacon_db.nim +++ b/fluffy/network/beacon/beacon_db.nim @@ -253,6 +253,8 @@ proc createGetHandler*(db: BeaconDb): DbGetHandler = return Opt.none(seq[byte]) case contentKey.contentType: + of unused: + raiseAssert "Should not be used and fail at decoding" of lightClientBootstrap: db.get(contentId) of lightClientUpdate: @@ -309,6 +311,8 @@ proc createStoreHandler*(db: BeaconDb): DbStoreHandler = return case contentKey.contentType: + of unused: + raiseAssert "Should not be used and fail at decoding" of lightClientBootstrap: db.put(contentId, content) of lightClientUpdate: diff --git a/fluffy/network/beacon/beacon_network.nim b/fluffy/network/beacon/beacon_network.nim index 3d318d17f..adc8da23b 100644 --- a/fluffy/network/beacon/beacon_network.nim +++ b/fluffy/network/beacon/beacon_network.nim @@ -192,6 +192,8 @@ proc validateContent( return err("Error decoding content key") case key.contentType: + of unused: + raiseAssert "Should not be used and fail at decoding" of lightClientBootstrap: let decodingResult = decodeLightClientBootstrapForked( n.forkDigests, content) diff --git a/fluffy/network/state/state_content.nim b/fluffy/network/state/state_content.nim index a7a7bb6c0..09e24d413 100644 --- a/fluffy/network/state/state_content.nim +++ b/fluffy/network/state/state_content.nim @@ -1,4 +1,4 @@ -# Nimbus +# Fluffy # Copyright (c) 2021-2023 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). @@ -23,11 +23,19 @@ type Address* = array[20, byte] ContentType* = enum - accountTrieNode = 0x00 - contractStorageTrieNode = 0x01 - accountTrieProof = 0x02 - contractStorageTrieProof = 0x03 - contractBytecode = 0x04 + # 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 + accountTrieNode = 0x20 + contractStorageTrieNode = 0x21 + accountTrieProof = 0x22 + contractStorageTrieProof = 0x23 + contractBytecode = 0x24 AccountTrieNodeKey* = object path*: ByteList @@ -55,6 +63,8 @@ type ContentKey* = object case contentType*: ContentType + of unused: + discard of accountTrieNode: accountTrieNodeKey*: AccountTrieNodeKey of contractStorageTrieNode: @@ -67,8 +77,18 @@ type contractBytecodeKey*: ContractBytecodeKey func encode*(contentKey: ContentKey): ByteList = + doAssert(contentKey.contentType != unused) ByteList.init(SSZ.encode(contentKey)) +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 decode*(contentKey: ByteList): Opt[ContentKey] = try: Opt.some(SSZ.decode(contentKey.asSeq(), ContentKey)) @@ -84,6 +104,8 @@ template computeContentId*(digestCtxType: type, body: untyped): ContentId = func toContentId*(contentKey: ContentKey): ContentId = case contentKey.contentType: + of unused: + raiseAssert "Should not be used and fail at decoding" of accountTrieNode: # sha256(path | node_hash) let key = contentKey.accountTrieNodeKey computeContentId sha256: diff --git a/fluffy/tests/beacon_network_tests/test_beacon_content.nim b/fluffy/tests/beacon_network_tests/test_beacon_content.nim index ecf3b3937..821de7f50 100644 --- a/fluffy/tests/beacon_network_tests/test_beacon_content.nim +++ b/fluffy/tests/beacon_network_tests/test_beacon_content.nim @@ -281,3 +281,28 @@ suite "Beacon Content Encodings": decodeLightClientBootstrapForked(forkDigests, @[]).isErr() decodeLightClientBootstrapForked(forkDigests, encodedTooEarlyFork).isErr() decodeLightClientBootstrapForked(forkDigests, encodedUnknownFork).isErr() + +suite "Beacon ContentKey Encodings ": + test "Invalid prefix - 0 value": + let encoded = ByteList.init(@[byte 0x00]) + let decoded = decode(encoded) + + check decoded.isNone() + + test "Invalid prefix - before valid range": + let encoded = ByteList.init(@[byte 0x01]) + let decoded = decode(encoded) + + check decoded.isNone() + + test "Invalid prefix - after valid range": + let encoded = ByteList.init(@[byte 0x14]) + let decoded = decode(encoded) + + check decoded.isNone() + + test "Invalid key - empty input": + let encoded = ByteList.init(@[]) + let decoded = decode(encoded) + + check decoded.isNone() diff --git a/fluffy/tests/portal_spec_tests/mainnet/test_state_content.nim b/fluffy/tests/portal_spec_tests/mainnet/test_state_content.nim index 7154c5948..a14999958 100644 --- a/fluffy/tests/portal_spec_tests/mainnet/test_state_content.nim +++ b/fluffy/tests/portal_spec_tests/mainnet/test_state_content.nim @@ -1,5 +1,5 @@ -# Nimbus -# Copyright (c) 2021-2022 Status Research & Development GmbH +# Fluffy +# Copyright (c) 2021-2023 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). @@ -31,7 +31,7 @@ suite "State ContentKey Encodings": # Output contentKeyHex = - "0044000000b8be7903aee73b8f6a59cd44a1f52c62148e1f376c0dfa1f5f773a98666efc2bd1c390624d3bd4e409a61a858e5dcc5517729a9170d014a6c96530d64dd8621d01020001" + "2044000000b8be7903aee73b8f6a59cd44a1f52c62148e1f376c0dfa1f5f773a98666efc2bd1c390624d3bd4e409a61a858e5dcc5517729a9170d014a6c96530d64dd8621d01020001" contentId = "41237096982860596884042712109427867048220765019203857308279863638242761605893" # or @@ -67,7 +67,7 @@ suite "State ContentKey Encodings": # Output contentKeyHex = - "01829bd824b016326a401d083b33d092293333a830580000003e190b68719aecbcb28ed2271014dd25f2aa633184988eb414189ce0899cade5d1c390624d3bd4e409a61a858e5dcc5517729a9170d014a6c96530d64dd8621d01000f0e0c00" + "21829bd824b016326a401d083b33d092293333a830580000003e190b68719aecbcb28ed2271014dd25f2aa633184988eb414189ce0899cade5d1c390624d3bd4e409a61a858e5dcc5517729a9170d014a6c96530d64dd8621d01000f0e0c00" contentId = "43529358882110548041037387588279806363134301284609868141745095118932570363585" # or @@ -100,7 +100,7 @@ suite "State ContentKey Encodings": # Output const contentKeyHex = - "02829bd824b016326a401d083b33d092293333a830d1c390624d3bd4e409a61a858e5dcc5517729a9170d014a6c96530d64dd8621d" + "22829bd824b016326a401d083b33d092293333a830d1c390624d3bd4e409a61a858e5dcc5517729a9170d014a6c96530d64dd8621d" contentId = "45301550050471302973396879294932122279426162994178563319590607565171451545101" # or @@ -135,7 +135,7 @@ suite "State ContentKey Encodings": # Output contentKeyHex = - "03829bd824b016326a401d083b33d092293333a830c8a6030000000000000000000000000000000000000000000000000000000000d1c390624d3bd4e409a61a858e5dcc5517729a9170d014a6c96530d64dd8621d" + "23829bd824b016326a401d083b33d092293333a830c8a6030000000000000000000000000000000000000000000000000000000000d1c390624d3bd4e409a61a858e5dcc5517729a9170d014a6c96530d64dd8621d" contentId = "80413803151602881485894828440259195604313253842905231566803078625935967002376" # or @@ -172,7 +172,7 @@ suite "State ContentKey Encodings": # Output const contentKeyHex = - "04829bd824b016326a401d083b33d092293333a830d1c390624d3bd4e409a61a858e5dcc5517729a9170d014a6c96530d64dd8621d" + "24829bd824b016326a401d083b33d092293333a830d1c390624d3bd4e409a61a858e5dcc5517729a9170d014a6c96530d64dd8621d" contentId = "9243655320250466575533858917172702581481192615849913473767356296630272634800" # or @@ -199,3 +199,27 @@ suite "State ContentKey Encodings": toContentId(contentKey) == parse(contentId, StUint[256], 10) # In stint this does BE hex string toContentId(contentKey).toHex() == contentIdHexBE + + test "Invalid prefix - 0 value": + let encoded = ByteList.init(@[byte 0x00]) + let decoded = decode(encoded) + + check decoded.isNone() + + test "Invalid prefix - before valid range": + let encoded = ByteList.init(@[byte 0x01]) + let decoded = decode(encoded) + + check decoded.isNone() + + test "Invalid prefix - after valid range": + let encoded = ByteList.init(@[byte 0x25]) + let decoded = decode(encoded) + + check decoded.isNone() + + test "Invalid key - empty input": + let encoded = ByteList.init(@[]) + let decoded = decode(encoded) + + check decoded.isNone() diff --git a/vendor/portal-spec-tests b/vendor/portal-spec-tests index ae43c56ea..eb9edb34e 160000 --- a/vendor/portal-spec-tests +++ b/vendor/portal-spec-tests @@ -1 +1 @@ -Subproject commit ae43c56eabd021406b8afb41f124d63e3cf5adf9 +Subproject commit eb9edb34eb3ea599a5b14407615cef0e7e48e8b9