From 1f9b0650b9ecdb9b569f73f32ef17ee44f036b33 Mon Sep 17 00:00:00 2001 From: Matt Haggard Date: Wed, 3 Dec 2025 10:36:14 -0500 Subject: [PATCH] RelayMessages have a resp_id to associate them with RelayCommand that caused the message --- ...ew-RelayMessages-have-a-20251203-103527.md | 1 + src/bucketsrelay/v2/objs.nim | 57 ++++-- src/bucketsrelay/v2/proto2.nim | 64 ++++--- tests/tfunctional.nim | 2 +- tests/tproto2.nim | 173 +++++++++++++++++- tests/tserde2.nim | 43 +++-- 6 files changed, 280 insertions(+), 60 deletions(-) create mode 100644 changes/new-RelayMessages-have-a-20251203-103527.md diff --git a/changes/new-RelayMessages-have-a-20251203-103527.md b/changes/new-RelayMessages-have-a-20251203-103527.md new file mode 100644 index 0000000..eb3335c --- /dev/null +++ b/changes/new-RelayMessages-have-a-20251203-103527.md @@ -0,0 +1 @@ +RelayMessages have a resp_id to associate them with RelayCommand that caused the message diff --git a/src/bucketsrelay/v2/objs.nim b/src/bucketsrelay/v2/objs.nim index 1209d87..f7714ec 100644 --- a/src/bucketsrelay/v2/objs.nim +++ b/src/bucketsrelay/v2/objs.nim @@ -48,6 +48,7 @@ type InvalidParams = 5 RelayMessage* = object + resp_id*: int case kind*: MessageKind of Who: who_challenge*: Challenge @@ -82,6 +83,7 @@ type HasChunks RelayCommand* = object + resp_id*: int case kind*: CommandKind of Iam: iam_pubkey*: PublicKey @@ -186,7 +188,7 @@ proc `$`*(msg: RelayMessage): string = result.add ")" proc `==`*(a, b: RelayMessage): bool = - if a.kind != b.kind: + if a.kind != b.kind or a.resp_id != b.resp_id: return false else: case a.kind @@ -231,7 +233,7 @@ proc `$`*(cmd: RelayCommand): string = result.add ")" proc `==`*(a, b: RelayCommand): bool = - if a.kind != b.kind: + if a.kind != b.kind or a.resp_id != b.resp_id: return false else: case a.kind @@ -419,6 +421,9 @@ proc deserialize*(typ: typedesc[seq[string]], val: string): seq[string] = proc serialize*(msg: RelayMessage): string = result &= msg.kind.serialize() + # For Who and Data messages, resp_id is always omitted (always 0) + if msg.kind notin {Who, Data}: + result &= nsencode($msg.resp_id) case msg.kind of Who: result &= msg.who_challenge.serialize() @@ -448,37 +453,45 @@ proc serialize*(msg: RelayMessage): string = proc deserialize*(typ: typedesc[RelayMessage], s: string): RelayMessage = if s.len == 0: raise ValueError.newException("Empty RelayMessage") - let kind = MessageKind.deserialize(s[0]) + var idx = 0 + let kind = MessageKind.deserialize(s[idx]) + idx.inc() + # For Who and Data messages, resp_id is always 0 and not serialized + let resp_id = if kind in {Who, Data}: + 0 + else: + s.nsdecode(idx).parseInt() case kind of Who: - return RelayMessage(kind: Who, who_challenge: Challenge.deserialize(s[1..^1])) + return RelayMessage(kind: Who, resp_id: resp_id, who_challenge: Challenge.deserialize(s[idx..^1])) of Okay: - return RelayMessage(kind: Okay, ok_cmd: CommandKind.deserialize(s[1])) + return RelayMessage(kind: Okay, resp_id: resp_id, ok_cmd: CommandKind.deserialize(s[idx])) of Error: return RelayMessage( kind: Error, - err_cmd: CommandKind.deserialize(s[1]), - err_code: ErrorCode.deserialize(s[2]), - err_message: s[3..^1] + resp_id: resp_id, + err_cmd: CommandKind.deserialize(s[idx]), + err_code: ErrorCode.deserialize(s[idx+1]), + err_message: s[(idx+2)..^1] ) of Note: - var idx = 1 return RelayMessage( kind: Note, + resp_id: resp_id, note_topic: s.nsdecode(idx), note_data: s.nsdecode(idx), ) of Data: - var idx = 1 return RelayMessage( kind: Data, + resp_id: resp_id, data_src: s.nsdecode(idx).PublicKey, data_val: s.nsdecode(idx), ) of Chunk: - var idx = 1 return RelayMessage( kind: Chunk, + resp_id: resp_id, chunk_src: s.nsdecode(idx).PublicKey, chunk_key: s.nsdecode(idx), chunk_val: if idx >= s.len: @@ -487,9 +500,9 @@ proc deserialize*(typ: typedesc[RelayMessage], s: string): RelayMessage = some(s.nsdecode(idx)), ) of ChunkStatus: - var idx = 1 return RelayMessage( kind: ChunkStatus, + resp_id: resp_id, status_src: s.nsdecode(idx).PublicKey, present: deserialize(seq[string], s.nsdecode(idx)), absent: deserialize(seq[string], s.nsdecode(idx)), @@ -497,6 +510,7 @@ proc deserialize*(typ: typedesc[RelayMessage], s: string): RelayMessage = proc serialize*(cmd: RelayCommand): string = result &= cmd.kind.serialize + result &= nsencode($cmd.resp_id) case cmd.kind of Iam: result &= cmd.iam_pubkey.string.nsencode @@ -523,54 +537,57 @@ proc serialize*(cmd: RelayCommand): string = proc deserialize*(typ: typedesc[RelayCommand], s: string): RelayCommand = if s.len == 0: raise ValueError.newException("Empty RelayCommand") - let kind = CommandKind.deserialize(s[0]) + var idx = 0 + let kind = CommandKind.deserialize(s[idx]) + idx.inc() + let resp_id = s.nsdecode(idx).parseInt() case kind of Iam: - var idx = 1 return RelayCommand( kind: Iam, + resp_id: resp_id, iam_pubkey: s.nsdecode(idx).PublicKey, iam_answer: ChallengeAnswer.deserialize(s.nsdecode(idx)), ) of PublishNote: - var idx = 1 return RelayCommand( kind: PublishNote, + resp_id: resp_id, pub_topic: s.nsdecode(idx), pub_data: s.nsdecode(idx), ) of FetchNote: - var idx = 1 return RelayCommand( kind: FetchNote, + resp_id: resp_id, fetch_topic: s.nsdecode(idx), ) of SendData: - var idx = 1 return RelayCommand( kind: SendData, + resp_id: resp_id, send_dst: s.nsdecode(idx).PublicKey, send_val: s.nsdecode(idx), ) of StoreChunk: - var idx = 1 return RelayCommand( kind: StoreChunk, + resp_id: resp_id, chunk_dst: deserializePubKeys(s.nsdecode(idx)), chunk_key: s.nsdecode(idx), chunk_val: s.nsdecode(idx), ) of GetChunks: - var idx = 1 return RelayCommand( kind: GetChunks, + resp_id: resp_id, chunk_src: s.nsdecode(idx).PublicKey, chunk_keys: deserialize(seq[string], s.nsdecode(idx)), ) of HasChunks: - var idx = 1 return RelayCommand( kind: HasChunks, + resp_id: resp_id, has_src: s.nsdecode(idx).PublicKey, has_keys: deserialize(seq[string], s.nsdecode(idx)), ) diff --git a/src/bucketsrelay/v2/proto2.nim b/src/bucketsrelay/v2/proto2.nim index 3042135..3be39f0 100644 --- a/src/bucketsrelay/v2/proto2.nim +++ b/src/bucketsrelay/v2/proto2.nim @@ -275,18 +275,20 @@ proc newRelay*[T](db: DbConn): Relay[T] = result.clients = newTable[PublicKey, RelayConnection[T]]() db.updateSchema() -template sendError*[T](conn: RelayConnection[T], msg: string, cmd: CommandKind, code: ErrorCode) = +template sendError*[T](conn: RelayConnection[T], cmd: RelayCommand, msg: string, code: ErrorCode) = conn.sendMessage(RelayMessage( kind: Error, + resp_id: cmd.resp_id, err_code: code, err_message: msg, - err_cmd: cmd, + err_cmd: cmd.kind, )) -template sendOkay*[T](conn: RelayConnection[T], cmd: CommandKind) = +template sendOkay*[T](conn: RelayConnection[T], cmd: RelayCommand) = conn.sendMessage(RelayMessage( kind: Okay, - ok_cmd: cmd, + resp_id: cmd.resp_id, + ok_cmd: cmd.kind, )) proc is_valid*(x: PublicKey): bool = @@ -308,6 +310,7 @@ proc initAuth*[T](relay: Relay[T], client: T): RelayConnection[T] = result.challenge = some(generateChallenge()) result.sendMessage(RelayMessage( kind: Who, + resp_id: 0, # Who messages are not triggered by a command who_challenge: result.challenge.get(), )) result.relay = relay @@ -480,6 +483,7 @@ proc nextMessage(relay: Relay, dst: PublicKey): Option[RelayMessage] = let row = orow.get() result = some(RelayMessage( kind: Data, + resp_id: 0, # Data messages are not triggered by recipient's command data_src: PublicKey.fromDB(row[0].b), data_val: row[1].b.string, )) @@ -502,23 +506,23 @@ proc handleCommand*[T](relay: Relay[T], conn: var RelayConnection[T], cmd: Relay when LOG_COMMS: info "[" & conn.pubkey.abbr & "] DO " & $cmd if conn.pubkey.isNone and cmd.kind != Iam: - conn.sendError("Not allowed", cmd.kind, NotAllowed) + conn.sendError(cmd, "Not allowed", NotAllowed) return case cmd.kind of Iam: if conn.challenge.isNone: - conn.sendError("Already authenticated", cmd.kind, Generic) + conn.sendError(cmd, "Already authenticated", Generic) return let challenge = conn.challenge.get() conn.challenge = none[Challenge]() # disable future authentication attempts - + try: if not is_valid_answer(cmd.iam_pubkey, challenge, cmd.iam_answer): - conn.sendError("Invalid answer", cmd.kind, Generic) + conn.sendError(cmd, "Invalid answer", Generic) return except CatchableError: - conn.sendError("Invalid answer", cmd.kind, Generic) + conn.sendError(cmd, "Invalid answer", Generic) return # successful connection @@ -526,7 +530,7 @@ proc handleCommand*[T](relay: Relay[T], conn: var RelayConnection[T], cmd: Relay conn.pubkey = some(pubkey) relay.clients[pubkey] = conn info &"[{conn.pubkey.abbr}] connected" - conn.sendOkay cmd.kind + conn.sendOkay(cmd) relay.db.record_event_stat( ip = conn.ip, pubkey = pubkey, @@ -550,13 +554,13 @@ proc handleCommand*[T](relay: Relay[T], conn: var RelayConnection[T], cmd: Relay break of PublishNote: if cmd.pub_topic.len > RELAY_MAX_TOPIC_SIZE: - conn.sendError("Topic too long", cmd.kind, TooLarge) + conn.sendError(cmd, "Topic too long", TooLarge) elif cmd.pub_data.len > RELAY_MAX_NOTE_SIZE: - conn.sendError("Data too long", cmd.kind, TooLarge) + conn.sendError(cmd, "Data too long", TooLarge) else: let pubkey = conn.pubkey.get() if relay.noteCount(pubkey) >= RELAY_MAX_NOTES: - conn.sendError("Too many notes", cmd.kind, StorageLimitExceeded) + conn.sendError(cmd, "Too many notes", StorageLimitExceeded) else: relay.db.record_transfer_stat( ip = conn.ip, @@ -572,9 +576,10 @@ proc handleCommand*[T](relay: Relay[T], conn: var RelayConnection[T], cmd: Relay if opubkey.isSome: # someone is waiting var other_conn = relay.clients[opubkey.get()] - conn.sendOkay cmd.kind + conn.sendOkay(cmd) other_conn.sendMessage(RelayMessage( kind: Note, + resp_id: 0, # Not triggered by other_conn's command note_data: cmd.pub_data, note_topic: cmd.pub_topic, )) @@ -592,12 +597,12 @@ proc handleCommand*[T](relay: Relay[T], conn: var RelayConnection[T], cmd: Relay cmd.pub_data.DbBlob, pubkey, ) - conn.sendOkay cmd.kind + conn.sendOkay(cmd) except: - conn.sendError("Duplicate topic", cmd.kind, Generic) + conn.sendError(cmd, "Duplicate topic", Generic) of FetchNote: if cmd.fetch_topic.len > RELAY_MAX_TOPIC_SIZE: - conn.sendError("Topic too long", cmd.kind, TooLarge) + conn.sendError(cmd, "Topic too long", TooLarge) else: let odata = relay.popNote(cmd.fetch_topic) if odata.isSome(): @@ -605,6 +610,7 @@ proc handleCommand*[T](relay: Relay[T], conn: var RelayConnection[T], cmd: Relay let data = odata.get() conn.sendMessage(RelayMessage( kind: Note, + resp_id: cmd.resp_id, # Response to FetchNote command note_data: data, note_topic: cmd.fetch_topic, )) @@ -618,13 +624,13 @@ proc handleCommand*[T](relay: Relay[T], conn: var RelayConnection[T], cmd: Relay relay.addNoteSub(cmd.fetch_topic, conn.pubkey.get()) of SendData: if cmd.send_val.len > RELAY_MAX_MESSAGE_SIZE: - conn.sendError("Data too long", cmd.kind, TooLarge) + conn.sendError(cmd, "Data too long", TooLarge) elif not cmd.send_dst.is_valid(): - conn.sendError("Invalid pubkey", cmd.kind, InvalidParams) + conn.sendError(cmd, "Invalid pubkey", InvalidParams) else: let pubkey = conn.pubkey.get() if relay.max_transfer_rate != 0 and relay.db.current_data_in(pubkey) > relay.max_transfer_rate: - conn.sendError("Rate limit exceeded", cmd.kind, TransferLimitExceeeded) + conn.sendError(cmd, "Rate limit exceeded", TransferLimitExceeeded) else: relay.db.record_transfer_stat( ip = conn.ip, @@ -641,6 +647,7 @@ proc handleCommand*[T](relay: Relay[T], conn: var RelayConnection[T], cmd: Relay var other_conn = relay.clients[cmd.send_dst] other_conn.sendMessage(RelayMessage( kind: Data, + resp_id: 0, # Not triggered by other_conn's command data_src: pubkey, data_val: cmd.send_val, )) @@ -655,17 +662,17 @@ proc handleCommand*[T](relay: Relay[T], conn: var RelayConnection[T], cmd: Relay pubkey, cmd.send_dst, cmd.send_val.DbBlob) of StoreChunk: if cmd.chunk_key.len > RELAY_MAX_CHUNK_KEY_SIZE: - conn.sendError("Key too long", cmd.kind, TooLarge) + conn.sendError(cmd, "Key too long", TooLarge) elif cmd.chunk_val.len > RELAY_MAX_CHUNK_SIZE: - conn.sendError("Value too long", cmd.kind, TooLarge) + conn.sendError(cmd, "Value too long", TooLarge) elif cmd.chunk_dst.len > RELAY_MAX_CHUNK_DSTS: - conn.sendError("Too many recipients", cmd.kind, TooLarge) + conn.sendError(cmd, "Too many recipients", TooLarge) elif cmd.chunk_dst.any_invalid(): - conn.sendError("Invalid pubkey", cmd.kind, InvalidParams) + conn.sendError(cmd, "Invalid pubkey", InvalidParams) else: let pubkey = conn.pubkey.get() if relay.max_chunk_space > 0 and relay.db.chunk_space_used(pubkey) > relay.max_chunk_space: - conn.sendError("Too much chunk data", cmd.kind, StorageLimitExceeded) + conn.sendError(cmd, "Too much chunk data", StorageLimitExceeded) else: relay.db.record_event_stat( ip = conn.ip, @@ -696,7 +703,7 @@ proc handleCommand*[T](relay: Relay[T], conn: var RelayConnection[T], cmd: Relay of GetChunks: for key in cmd.chunk_keys: if key.len > RELAY_MAX_CHUNK_KEY_SIZE: - conn.sendError("Key too long", cmd.kind, TooLarge) + conn.sendError(cmd, "Key too long", TooLarge) return relay.delExpiredChunks() let pubkey = conn.pubkey.get() @@ -718,6 +725,7 @@ proc handleCommand*[T](relay: Relay[T], conn: var RelayConnection[T], cmd: Relay let row = orow.get() conn.sendMessage(RelayMessage( kind: Chunk, + resp_id: cmd.resp_id, chunk_src: cmd.chunk_src, chunk_key: key, chunk_val: some(row[0].b.string), @@ -725,6 +733,7 @@ proc handleCommand*[T](relay: Relay[T], conn: var RelayConnection[T], cmd: Relay else: conn.sendMessage(RelayMessage( kind: Chunk, + resp_id: cmd.resp_id, chunk_src: cmd.chunk_src, chunk_key: key, chunk_val: none[string](), @@ -732,7 +741,7 @@ proc handleCommand*[T](relay: Relay[T], conn: var RelayConnection[T], cmd: Relay of HasChunks: for key in cmd.has_keys: if key.len > RELAY_MAX_CHUNK_KEY_SIZE: - conn.sendError("Key too long", cmd.kind, TooLarge) + conn.sendError(cmd, "Key too long", TooLarge) return relay.delExpiredChunks() let pubkey = conn.pubkey.get() @@ -768,6 +777,7 @@ proc handleCommand*[T](relay: Relay[T], conn: var RelayConnection[T], cmd: Relay absent.add(key) conn.sendMessage(RelayMessage( kind: ChunkStatus, + resp_id: cmd.resp_id, status_src: cmd.has_src, present: present, absent: absent, diff --git a/tests/tfunctional.nim b/tests/tfunctional.nim index 85f06f7..dba3d31 100644 --- a/tests/tfunctional.nim +++ b/tests/tfunctional.nim @@ -27,7 +27,7 @@ proc startServer(port: Port): Process = let compileProcess = startProcess( "nim", workingDir = currentSourcePath().parentDir().parentDir(), - args = ["c", "-d:testmode", "-o:" & bin, "src/bucketsrelay/server2.nim"], + args = ["c", "-d:testmode", "-o:" & bin, "src/bucketsrelay/v2/server2.nim"], options = {poStdErrToStdOut, poUsePath} ) let output = compileProcess.outputStream.readAll() diff --git a/tests/tproto2.nim b/tests/tproto2.nim index e33f63e..d5bcd37 100644 --- a/tests/tproto2.nim +++ b/tests/tproto2.nim @@ -80,12 +80,14 @@ proc authenticatedConn(relay: Relay, keys: KeyPair): RelayConnection[TestClient] let answer = who.who_challenge.answer(client.sk) relay.handleCommand(conn, RelayCommand( kind: Iam, + resp_id: 1, iam_answer: answer, iam_pubkey: client.pk )) let ok = conn.pop() doAssert ok.kind == Okay doAssert ok.ok_cmd == Iam + doAssert ok.resp_id == 1 return conn proc authenticatedConn(relay: Relay): RelayConnection[TestClient] = @@ -846,4 +848,173 @@ suite "stats": check db.stats_transfer_total(period="2010-01") == (1000+2000, 500+250, "", "".PublicKey, "2010-01") check db.stats_transfer_total(period="2010-02") == (3000+500, 100+100, "", "".PublicKey, "2010-02") - \ No newline at end of file +suite "resp_id": + + test "Who message has resp_id 0": + let relay = testRelay() + let client = newTestClient(genkeys()) + var conn = relay.initAuth(client) + let who = conn.pop(Who) + check who.resp_id == 0 + + test "Iam command response has matching resp_id": + let relay = testRelay() + let client = newTestClient(genkeys()) + var conn = relay.initAuth(client) + let who = conn.pop(Who) + let answer = who.who_challenge.answer(client.sk) + + relay.handleCommand(conn, RelayCommand( + kind: Iam, + resp_id: 42, + iam_answer: answer, + iam_pubkey: client.pk + )) + let ok = conn.pop(Okay) + check ok.resp_id == 42 + + test "PublishNote response has matching resp_id": + let relay = testRelay() + var alice = relay.authenticatedConn() + + relay.handleCommand(alice, RelayCommand( + kind: PublishNote, + resp_id: 123, + pub_topic: "test", + pub_data: "data", + )) + let ok = alice.pop(Okay) + check ok.resp_id == 123 + + test "FetchNote response has matching resp_id": + let relay = testRelay() + var alice = relay.authenticatedConn() + + # Publish a note first + relay.handleCommand(alice, RelayCommand( + kind: PublishNote, + pub_topic: "test", + pub_data: "data", + )) + discard alice.pop(Okay) + + # Fetch the note with resp_id + relay.handleCommand(alice, RelayCommand( + kind: FetchNote, + resp_id: 456, + fetch_topic: "test", + )) + let note = alice.pop(Note) + check note.resp_id == 456 + + test "Error response has matching resp_id": + let relay = testRelay() + var alice = relay.authenticatedConn() + + relay.handleCommand(alice, RelayCommand( + kind: PublishNote, + resp_id: 789, + pub_topic: "a".repeat(RELAY_MAX_TOPIC_SIZE + 1), + pub_data: "data", + )) + let err = alice.pop(Error) + check err.resp_id == 789 + + test "Data message has resp_id 0 (no command trigger)": + let relay = testRelay() + var alice = relay.authenticatedConn() + var bob = relay.authenticatedConn() + + relay.handleCommand(alice, RelayCommand( + kind: SendData, + resp_id: 111, + send_dst: bob.pk, + send_val: "hello", + )) + + # Bob receives the Data message - it should have resp_id 0 + # because it wasn't triggered by Bob's command + let data = bob.pop(Data) + check data.resp_id == 0 + + test "StoreChunk command response has matching resp_id": + let relay = testRelay() + var alice = relay.authenticatedConn() + + relay.handleCommand(alice, RelayCommand( + kind: StoreChunk, + resp_id: 222, + chunk_dst: @[], + chunk_key: "key", + chunk_val: "val", + )) + # StoreChunk doesn't send a response by default, no message to check + check alice.msgCount == 0 + + test "GetChunks response has matching resp_id": + let relay = testRelay() + var alice = relay.authenticatedConn() + + # Store a chunk first + relay.handleCommand(alice, RelayCommand( + kind: StoreChunk, + chunk_dst: @[], + chunk_key: "key", + chunk_val: "val", + )) + + # Get the chunk with resp_id + relay.handleCommand(alice, RelayCommand( + kind: GetChunks, + resp_id: 333, + chunk_src: alice.pk, + chunk_keys: @["key"], + )) + let chunk = alice.pop(Chunk) + check chunk.resp_id == 333 + + test "HasChunks response has matching resp_id": + let relay = testRelay() + var alice = relay.authenticatedConn() + + # Store a chunk first + relay.handleCommand(alice, RelayCommand( + kind: StoreChunk, + chunk_dst: @[], + chunk_key: "key", + chunk_val: "val", + )) + + # Check if chunk exists with resp_id + relay.handleCommand(alice, RelayCommand( + kind: HasChunks, + resp_id: 444, + has_src: alice.pk, + has_keys: @["key"], + )) + let status = alice.pop(ChunkStatus) + check status.resp_id == 444 + + test "Multiple commands with different resp_ids": + let relay = testRelay() + var alice = relay.authenticatedConn() + + # Send multiple commands with different resp_ids + relay.handleCommand(alice, RelayCommand( + kind: PublishNote, + resp_id: 100, + pub_topic: "topic1", + pub_data: "data1", + )) + relay.handleCommand(alice, RelayCommand( + kind: PublishNote, + resp_id: 200, + pub_topic: "topic2", + pub_data: "data2", + )) + + let ok1 = alice.pop(Okay) + check ok1.resp_id == 100 + let ok2 = alice.pop(Okay) + check ok2.resp_id == 200 + diff --git a/tests/tserde2.nim b/tests/tserde2.nim index 122362e..6d30580 100644 --- a/tests/tserde2.nim +++ b/tests/tserde2.nim @@ -17,13 +17,13 @@ test "CommandKind": test "RelayMessage": for kind in low(MessageKind)..high(MessageKind): let example = case kind - of Who: RelayMessage(kind: Who, who_challenge: generateChallenge()) - of Okay: RelayMessage(kind: Okay, ok_cmd: SendData) - of Error: RelayMessage(kind: Error, err_cmd: SendData, err_code: TooLarge, err_message: "foo") - of Note: RelayMessage(kind: Note, note_topic: "something", note_data: "data") - of Data: RelayMessage(kind: Data, data_src: "hey".PublicKey, data_val: "foo") - of Chunk: RelayMessage(kind: Chunk, chunk_src: "hey".PublicKey, chunk_key: "key", chunk_val: some("theval")) - of ChunkStatus: RelayMessage(kind: ChunkStatus, status_src: "a".PublicKey, present: @["foo"], absent: @["bar"]) + of Who: RelayMessage(kind: Who, resp_id: 0, who_challenge: generateChallenge()) + of Okay: RelayMessage(kind: Okay, resp_id: 42, ok_cmd: SendData) + of Error: RelayMessage(kind: Error, resp_id: 123, err_cmd: SendData, err_code: TooLarge, err_message: "foo") + of Note: RelayMessage(kind: Note, resp_id: 456, note_topic: "something", note_data: "data") + of Data: RelayMessage(kind: Data, resp_id: 0, data_src: "hey".PublicKey, data_val: "foo") + of Chunk: RelayMessage(kind: Chunk, resp_id: 789, chunk_src: "hey".PublicKey, chunk_key: "key", chunk_val: some("theval")) + of ChunkStatus: RelayMessage(kind: ChunkStatus, resp_id: 999, status_src: "a".PublicKey, present: @["foo"], absent: @["bar"]) let serialized = example.serialize() info $example info "serialized: " & serialized.nice @@ -34,6 +34,7 @@ test "RelayCommand": let example = case kind of Iam: RelayCommand( kind: Iam, + resp_id: 1, iam_pubkey: "hey".PublicKey, iam_answer: ( nonce: 1, @@ -41,18 +42,20 @@ test "RelayCommand": signature: "hey", ), ) - of PublishNote: RelayCommand(kind: PublishNote, pub_topic: "topic", pub_data: "data") - of FetchNote: RelayCommand(kind: FetchNote, fetch_topic: "topic") - of SendData: RelayCommand(kind: SendData, send_dst: "one".PublicKey, send_val: "data") + of PublishNote: RelayCommand(kind: PublishNote, resp_id: 100, pub_topic: "topic", pub_data: "data") + of FetchNote: RelayCommand(kind: FetchNote, resp_id: 200, fetch_topic: "topic") + of SendData: RelayCommand(kind: SendData, resp_id: 300, send_dst: "one".PublicKey, send_val: "data") of StoreChunk: RelayCommand( kind: StoreChunk, + resp_id: 400, chunk_dst: @["one".PublicKey], chunk_key: "theky", chunk_val: "someval" ) - of GetChunks: RelayCommand(kind: GetChunks, chunk_src: "hey".PublicKey, chunk_keys: @["foo", "bar"]) + of GetChunks: RelayCommand(kind: GetChunks, resp_id: 500, chunk_src: "hey".PublicKey, chunk_keys: @["foo", "bar"]) of HasChunks: RelayCommand( kind: HasChunks, + resp_id: 600, has_src: "hey".PublicKey, has_keys: @["foo", "Bar"], ) @@ -63,6 +66,7 @@ test "RelayCommand": test "Chunk with none": let chunk = RelayMessage(kind: Chunk, + resp_id: 888, chunk_src: "foo".PublicKey, chunk_key: "key", chunk_val: none[string](), @@ -77,3 +81,20 @@ test "ErrorCodes": let serialized = err.serialize() checkpoint "serialized.nice: " & nice($serialized) check ErrorCode.deserialize(serialized) == err + +test "resp_id serialization": + # Test that resp_id values are preserved during serialization + let msg1 = RelayMessage(kind: Okay, resp_id: 12345, ok_cmd: SendData) + check RelayMessage.deserialize(msg1.serialize()).resp_id == 12345 + + let msg2 = RelayMessage(kind: Error, resp_id: 99999, err_cmd: Iam, err_code: Generic, err_message: "test") + check RelayMessage.deserialize(msg2.serialize()).resp_id == 99999 + + let msg3 = RelayMessage(kind: Who, resp_id: 0, who_challenge: generateChallenge()) + check RelayMessage.deserialize(msg3.serialize()).resp_id == 0 + + let cmd1 = RelayCommand(kind: PublishNote, resp_id: 54321, pub_topic: "topic", pub_data: "data") + check RelayCommand.deserialize(cmd1.serialize()).resp_id == 54321 + + let cmd2 = RelayCommand(kind: SendData, resp_id: 0, send_dst: "dst".PublicKey, send_val: "val") + check RelayCommand.deserialize(cmd2.serialize()).resp_id == 0