diff --git a/logos_delivery/waku/waku_core/message.nim b/logos_delivery/waku/waku_core/message.nim index 3f2d93d8a..c009aefa1 100644 --- a/logos_delivery/waku/waku_core/message.nim +++ b/logos_delivery/waku/waku_core/message.nim @@ -3,6 +3,7 @@ import ./message/default_values, ./message/codec, ./message/digest, + ./message/envelope, ./message/path_counters -export message, default_values, codec, digest, path_counters +export message, default_values, codec, digest, envelope, path_counters diff --git a/logos_delivery/waku/waku_core/message/envelope.nim b/logos_delivery/waku/waku_core/message/envelope.nim new file mode 100644 index 000000000..6aaf86e73 --- /dev/null +++ b/logos_delivery/waku/waku_core/message/envelope.nim @@ -0,0 +1,38 @@ +## Waku message envelope. +## +## Bundles a decoded `WakuMessage` with its `pubsubTopic` and the deterministic +## `WakuMessageHash`, computed **once** at construction. The envelope is the unit +## that flows through the internal relay dispatch (relay topic handler -> +## subscription_manager -> archive / filter / store-sync / app handlers) so that +## the same message is neither re-decoded nor re-hashed by each consumer. +## +## Like `WakuMessage`, a `WakuEnvelope` is a `ref object` and **immutable by +## convention**: construct it once after validation and never mutate it. Under +## `--mm:refc` passing it around is a pointer + refcount, not a deep copy. + +{.push raises: [].} + +import ../topics, ./message, ./digest + +type WakuEnvelope* = ref object + msg*: WakuMessage + pubsubTopic*: PubsubTopic + hash*: WakuMessageHash + +proc init*(T: type WakuEnvelope, pubsubTopic: PubsubTopic, msg: WakuMessage): T = + ## Builds an envelope, computing the message hash once (the single inbound-path + ## hash). `msg` is referenced, not copied. + WakuEnvelope( + msg: msg, pubsubTopic: pubsubTopic, hash: computeMessageHash(pubsubTopic, msg) + ) + +proc shortLog*(envelope: WakuEnvelope): string = + ## Compact chronicles representation: short hash + topic. + if envelope.isNil(): + return "nil" + "hash=" & envelope.hash.to0xHex() & " topic=" & envelope.pubsubTopic + +proc `$`*(envelope: WakuEnvelope): string = + shortLog(envelope) + +{.pop.} diff --git a/tests/all_tests_waku.nim b/tests/all_tests_waku.nim index f8ffc7b20..0e2b6ce4d 100644 --- a/tests/all_tests_waku.nim +++ b/tests/all_tests_waku.nim @@ -8,6 +8,7 @@ import ./waku_core/test_time, ./waku_core/test_message, ./waku_core/test_message_digest, + ./waku_core/test_message_envelope, ./waku_core/test_peers, ./waku_core/test_published_address diff --git a/tests/waku_core/test_all.nim b/tests/waku_core/test_all.nim index f7f4fad38..7ff2545dc 100644 --- a/tests/waku_core/test_all.nim +++ b/tests/waku_core/test_all.nim @@ -2,6 +2,7 @@ import ./test_message_digest, + ./test_message_envelope, ./test_namespaced_topics, ./test_peers, ./test_published_address, diff --git a/tests/waku_core/test_message_envelope.nim b/tests/waku_core/test_message_envelope.nim new file mode 100644 index 000000000..67b37d6fc --- /dev/null +++ b/tests/waku_core/test_message_envelope.nim @@ -0,0 +1,44 @@ +{.used.} + +import std/sequtils, stew/byteutils, testutils/unittests +import logos_delivery/waku/waku_core, ../testlib/wakucore + +suite "Waku Message - Envelope": + test "envelope init computes the same hash as computeMessageHash": + ## Given + let pubsubTopic = DefaultPubsubTopic + let message = fakeWakuMessage( + contentTopic = DefaultContentTopic, + payload = "\x01\x02\x03\x04TEST\x05\x06\x07\x08".toBytes(), + meta = newSeq[byte](), + ts = getNanosecondTime(1681964442), + ) + + ## When + let envelope = WakuEnvelope.init(pubsubTopic, message) + + ## Then + check: + envelope.hash == computeMessageHash(pubsubTopic, message) + envelope.pubsubTopic == pubsubTopic + envelope.msg == message + + test "envelope references the same message (no copy)": + let pubsubTopic = DefaultPubsubTopic + let message = fakeWakuMessage(payload = "abc".toBytes()) + let envelope = WakuEnvelope.init(pubsubTopic, message) + + ## The envelope holds the very same ref, not a clone. + check: + envelope.msg == message + # ref identity: mutating through one is visible through the other + cast[pointer](envelope.msg) == cast[pointer](message) + + test "different topics yield different hashes for the same message": + let message = fakeWakuMessage(payload = "same-payload".toBytes()) + let e1 = WakuEnvelope.init("/waku/2/rs/0/0", message) + let e2 = WakuEnvelope.init("/waku/2/rs/0/1", message) + + check: + e1.hash != e2.hash + e1.msg == e2.msg