Skip to content

Conversation

@agaffney
Copy link
Contributor

@agaffney agaffney commented Oct 23, 2025

Summary by CodeRabbit

  • New Features

    • Adds LeiosFetch protocol support for fetching ledger items (blocks, block transactions, votes, and block ranges) with client and server endpoints and request/response flows.
    • Exposes user-facing APIs to request and stream ranged block data.
    • Integrates the protocol into connection startup/shutdown so it runs with other node-to-node protocols.
  • Bug Fixes / Improvements

    • Adjusted protocol identifier and state numbering for the notification protocol to correct runtime behavior.
@agaffney agaffney requested a review from a team as a code owner October 23, 2025 18:51
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 23, 2025

Walkthrough

Adds a new LeiosFetch node-to-node protocol (client, server, CBOR messages, config/callbacks) and wires it into Connection lifecycle; also adjusts an existing LeiosNotify protocol identifier and its Done state numeric ID.

Changes

Cohort / File(s) Summary
Connection integration
connection.go
Adds leiosFetch *leiosfetch.LeiosFetch and leiosFetchConfig *leiosfetch.Config fields and LeiosFetch() accessor; initializes, starts/stops, and manages LeiosFetch alongside other mini-protocols during node-to-node setup.
LeiosFetch core & config
protocol/leiosfetch/leiosfetch.go
Introduces protocol metadata, state constants and state map, LeiosFetch struct, Config and callback types, CallbackContext, factory New and option helpers (With* / WithTimeout) and NewConfig.
LeiosFetch client implementation
protocol/leiosfetch/client.go
Adds exported Client type, constructor NewClient, lifecycle (Start/Stop), request methods (BlockRequest, BlockTxsRequest, VotesRequest, BlockRangeRequest), internal message handler and per-message result channel logic (including multi-message range assembly).
LeiosFetch server implementation
protocol/leiosfetch/server.go
Adds exported Server type and NewServer, protocol initialization, messageHandler dispatch, per-message handlers invoking configured callback functions, send-response logic and restart-on-Done behavior.
LeiosFetch CBOR messages
protocol/leiosfetch/messages.go
Defines CBOR-backed message types and constructors (MsgBlockRequest, MsgBlock, MsgBlockTxsRequest, MsgBlockTxs, MsgVotesRequest, MsgVotes, MsgBlockRangeRequest, MsgNextBlockAndTxsInRange, MsgLastBlockAndTxsInRange, MsgDone), NewMsgFromCbor factory, and message-type constants (0–9).
LeiosNotify update
protocol/leiosnotify/leiosnotify.go
Changes ProtocolId from 998 to 18 and adjusts StateDone initialization numeric ID from 4 to 3.

Sequence Diagram(s)

sequenceDiagram participant Client participant ClientProtocol as Client Protocol participant ServerProtocol as Server Protocol participant ServerHandler as Server Handler rect rgb(220, 235, 255) note over Client,ServerHandler: Single-item request/response (Block) Client->>ClientProtocol: Send MsgBlockRequest(slot, hash) ClientProtocol->>ServerProtocol: transport message ServerProtocol->>ServerHandler: messageHandler(MsgBlockRequest) ServerHandler->>ServerHandler: invoke BlockRequestFunc(ctx, slot, hash) ServerHandler-->>ServerProtocol: send MsgBlock ServerProtocol-->>ClientProtocol: deliver MsgBlock ClientProtocol->>Client: enqueue MsgBlock (client channel) end 
Loading
sequenceDiagram participant Client participant ClientProtocol as Client Protocol participant ServerProtocol as Server Protocol participant ServerHandler as Server Handler rect rgb(235, 255, 230) note over Client,ServerHandler: Multi-message BlockRange flow Client->>ClientProtocol: Send MsgBlockRangeRequest(start,end) ClientProtocol->>ServerProtocol: transport message ServerProtocol->>ServerHandler: messageHandler(MsgBlockRangeRequest) ServerHandler->>ServerHandler: invoke BlockRangeRequestFunc(ctx, start, end) loop multiple blocks ServerHandler-->>ServerProtocol: send MsgNextBlockAndTxsInRange ServerProtocol-->>ClientProtocol: deliver MsgNextBlockAndTxsInRange ClientProtocol->>Client: enqueue (range channel) end ServerHandler-->>ServerProtocol: send MsgLastBlockAndTxsInRange ServerProtocol-->>ClientProtocol: deliver MsgLastBlockAndTxsInRange (completion) ClientProtocol->>Client: finalize BlockRangeRequest result end 
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "feat: initial LeoisFetch protocol" directly aligns with the primary changes in this changeset. The main objective is to introduce the initial implementation of the LeiosFetch protocol, adding client, server, and protocol infrastructure files under protocol/leiosfetch/, along with integration into connection.go. The title clearly and concisely communicates this primary change. While there is a minor spelling inconsistency in the title (written as "LeoisFetch" whereas the implementation uses "LeiosFetch"), this does not obscure the intent or make the title misleading—readers would clearly understand the PR is about introducing this protocol feature.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/protocol-leios-fetch

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
protocol/leiosfetch/client.go (1)

25-35: Document single-flight expectations or add serialization

Multiple concurrent requests of the same kind will race on shared result channels (e.g., two BlockRequests). Either document that the client is single‑flight per request type or serialize internally (mutex/queue).

protocol/leiosfetch/server.go (1)

194-206: Restarting protocol inside message handler may race

Calling Stop/initProtocol/Start synchronously from within the handler can deadlock or fight with the receiver loop. Prefer deferring restart in a goroutine.

- // Restart protocol - s.Stop() - s.initProtocol() - s.Start() + // Restart protocol asynchronously to avoid handler/loop reentrancy + go func() { + _ = s.Stop() + s.initProtocol() + s.Start() + }()
protocol/leiosfetch/leiosfetch.go (1)

115-121: Minor: clarify Timeout scope

Timeout appears to be applied only on the client (via state map overrides). Add a short comment to avoid confusion for server users.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d9fff7d and 6ba18b7.

📒 Files selected for processing (6)
  • connection.go (6 hunks)
  • protocol/leiosfetch/client.go (1 hunks)
  • protocol/leiosfetch/leiosfetch.go (1 hunks)
  • protocol/leiosfetch/messages.go (1 hunks)
  • protocol/leiosfetch/server.go (1 hunks)
  • protocol/leiosnotify/leiosnotify.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (6)
connection.go (3)
protocol/leiosfetch/leiosfetch.go (3)
  • LeiosFetch (110-113)
  • Config (115-121)
  • New (138-144)
protocol/leiosfetch/client.go (1)
  • Client (25-35)
protocol/leiosfetch/server.go (1)
  • Server (24-29)
protocol/leiosfetch/server.go (4)
protocol/protocol.go (6)
  • Protocol (39-54)
  • ProtocolOptions (93-102)
  • ProtocolConfig (57-71)
  • ProtocolRoleServer (89-89)
  • MessageHandlerFunc (110-110)
  • MessageFromCborFunc (113-113)
protocol/leiosfetch/leiosfetch.go (10)
  • Config (115-121)
  • CallbackContext (124-128)
  • ProtocolName (26-26)
  • ProtocolId (27-27)
  • StateMap (39-108)
  • New (138-144)
  • BlockRequestFunc (132-132)
  • BlockTxsRequestFunc (133-133)
  • VotesRequestFunc (134-134)
  • BlockRangeRequestFunc (135-135)
protocol/leiosfetch/messages.go (10)
  • NewMsgFromCbor (41-73)
  • MessageTypeBlockRequest (29-29)
  • MessageTypeBlockTxsRequest (31-31)
  • MessageTypeVotesRequest (33-33)
  • MessageTypeBlockRangeRequest (35-35)
  • MessageTypeDone (38-38)
  • MsgBlockRequest (75-79)
  • MsgBlockTxsRequest (107-112)
  • MsgVotesRequest (141-144)
  • MsgBlockRangeRequest (177-181)
protocol/message.go (1)
  • Message (18-22)
protocol/leiosfetch/client.go (5)
protocol/protocol.go (6)
  • Protocol (39-54)
  • ProtocolOptions (93-102)
  • ProtocolConfig (57-71)
  • ProtocolRoleClient (88-88)
  • MessageHandlerFunc (110-110)
  • MessageFromCborFunc (113-113)
protocol/leiosfetch/leiosfetch.go (12)
  • Config (115-121)
  • CallbackContext (124-128)
  • NewConfig (148-157)
  • StateMap (39-108)
  • StateBlock (32-32)
  • StateBlockTxs (33-33)
  • StateVotes (34-34)
  • StateBlockRange (35-35)
  • ProtocolName (26-26)
  • ProtocolId (27-27)
  • StateIdle (31-31)
  • New (138-144)
protocol/leiosfetch/messages.go (13)
  • NewMsgFromCbor (41-73)
  • NewMsgDone (232-239)
  • NewMsgBlockRequest (81-90)
  • NewMsgBlockTxsRequest (114-124)
  • MsgVotesRequestVoteId (146-150)
  • NewMsgVotesRequest (152-160)
  • NewMsgBlockRangeRequest (183-192)
  • MsgLastBlockAndTxsInRange (211-215)
  • MessageTypeBlock (30-30)
  • MessageTypeBlockTxs (32-32)
  • MessageTypeVotes (34-34)
  • MessageTypeNextBlockAndTxsInRange (37-37)
  • MessageTypeLastBlockAndTxsInRange (36-36)
protocol/error.go (1)
  • ErrProtocolShuttingDown (19-19)
protocol/common/types.go (1)
  • Point (23-28)
protocol/leiosnotify/leiosnotify.go (1)
protocol/state.go (1)
  • NewState (38-43)
protocol/leiosfetch/messages.go (5)
protocol/message.go (2)
  • Message (18-22)
  • MessageBase (25-30)
cbor/decode.go (1)
  • Decode (29-44)
protocol/leiosfetch/leiosfetch.go (1)
  • ProtocolName (26-26)
cbor/cbor.go (2)
  • RawMessage (36-36)
  • StructAsArray (45-48)
protocol/common/types.go (1)
  • Point (23-28)
protocol/leiosfetch/leiosfetch.go (7)
protocol/state.go (6)
  • NewState (38-43)
  • StateMapEntry (62-66)
  • AgencyClient (27-27)
  • StateTransition (51-55)
  • AgencyServer (28-28)
  • AgencyNone (26-26)
protocol/leiosfetch/messages.go (11)
  • MessageTypeBlockRequest (29-29)
  • MessageTypeBlockTxsRequest (31-31)
  • MessageTypeVotesRequest (33-33)
  • MessageTypeBlockRangeRequest (35-35)
  • MessageTypeDone (38-38)
  • MessageTypeBlock (30-30)
  • MessageTypeBlockTxs (32-32)
  • MessageTypeVotes (34-34)
  • MessageTypeNextBlockAndTxsInRange (37-37)
  • MessageTypeLastBlockAndTxsInRange (36-36)
  • MsgVotesRequestVoteId (146-150)
protocol/leiosfetch/client.go (2)
  • Client (25-35)
  • NewClient (37-87)
protocol/leiosfetch/server.go (2)
  • Server (24-29)
  • NewServer (31-43)
connection.go (2)
  • ConnectionId (56-56)
  • New (133-135)
protocol/common/types.go (1)
  • Point (23-28)
protocol/protocol.go (1)
  • ProtocolOptions (93-102)
🔇 Additional comments (8)
protocol/leiosnotify/leiosnotify.go (2)

32-32: Sequential state IDs improve consistency.

The state ID change makes the state sequence sequential (1, 2, 3 instead of 1, 2, 4), which improves maintainability. However, this is a breaking change if state IDs are persisted or referenced by external systems.

If this protocol was previously deployed, please verify that no persistent state references the old state ID 4.


26-26: Protocol ID and state ID assignments are correct and intentional.

The changes from ProtocolId 998→18 and StateDone 4→3 represent the official assignment of protocol and state identifiers to new Leios protocols. The previous code explicitly marked 998 as a dummy value awaiting replacement. Both changes are consistent with the codebase design:

  • Protocol IDs 18 and 19 follow the existing numbering scheme for protocol assignments
  • State IDs are now sequential (1, 2, 3) matching the pattern in LeiosFetch
  • These are new protocols being formalized, not modifications to existing ones
  • No backward compatibility concerns exist—these protocols are not yet deployed to production nodes

No migration plan is needed, and all protocol ID references have been properly updated throughout the codebase.

connection.go (3)

89-103: Wiring fields look good

Adding leiosFetch and leiosFetchConfig matches existing mini‑protocol patterns. No concerns.


215-218: Public accessor LGTM

LeiosFetch() mirrors other accessors and is idiomatic.


416-444: ProtocolId 19 is unique and properly defined; verify peer compatibility before unconditional startup.

Verification confirms:

  • ProtocolId 19 is unique (no other protocol reuses it)
  • LeiosFetch is created and started unconditionally without nil checks or feature flags, unlike keepAlive (sendKeepAlives boolean) and peerSharing (nil check)
  • LeiosNotify follows the same unconditional pattern

The risk is that peers may not support the leios-fetch protocol. If all target peers support it, this is acceptable; otherwise, consider adding a feature-gate flag or version negotiation similar to keepAlive. Only you can verify peer compatibility at runtime or through protocol documentation.

protocol/leiosfetch/client.go (1)

89-107: Shutdown cleanup timing

Closing result channels on <-DoneChan is fine assuming Protocol guarantees no further handler invocations afterward. Please confirm Protocol.Stop() semantics prevent sends after Done to avoid “send on closed channel” panics.

protocol/leiosfetch/messages.go (1)

27-39: Message type IDs marked “dummy”

These constants are labeled temporary. Please reconcile with the spec and update before releasing to avoid wire incompatibility.

protocol/leiosfetch/leiosfetch.go (1)

25-29: ProtocolId 19 collision check passed

No collision detected. ID 19 is unique; existing protocols use IDs: 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 18.

Comment on lines +41 to +73
func NewMsgFromCbor(msgType uint, data []byte) (protocol.Message, error) {
var ret protocol.Message
switch msgType {
case MessageTypeBlockRequest:
ret = &MsgBlockRequest{}
case MessageTypeBlock:
ret = &MsgBlock{}
case MessageTypeBlockTxsRequest:
ret = &MsgBlockTxsRequest{}
case MessageTypeBlockTxs:
ret = &MsgBlockTxs{}
case MessageTypeVotesRequest:
ret = &MsgVotesRequest{}
case MessageTypeVotes:
ret = &MsgVotes{}
case MessageTypeBlockRangeRequest:
ret = &MsgBlockRangeRequest{}
case MessageTypeLastBlockAndTxsInRange:
ret = &MsgLastBlockAndTxsInRange{}
case MessageTypeNextBlockAndTxsInRange:
ret = &MsgNextBlockAndTxsInRange{}
case MessageTypeDone:
ret = &MsgDone{}
}
if _, err := cbor.Decode(data, ret); err != nil {
return nil, fmt.Errorf("%s: decode error: %w", ProtocolName, err)
}
if ret != nil {
// Store the raw message CBOR
ret.SetCbor(data)
}
return ret, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Guard unknown message types before decoding

If msgType isn’t recognized, ret is nil and cbor.Decode(nil, …) risks a panic or undefined behavior. Return a clear error early.

 func NewMsgFromCbor(msgType uint, data []byte) (protocol.Message, error) { var ret protocol.Message switch msgType { // cases... } + if ret == nil { + return nil, fmt.Errorf("%s: unknown message type: %d", ProtocolName, msgType) + } if _, err := cbor.Decode(data, ret); err != nil { return nil, fmt.Errorf("%s: decode error: %w", ProtocolName, err) } if ret != nil { // Store the raw message CBOR ret.SetCbor(data) } return ret, nil }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func NewMsgFromCbor(msgType uint, data []byte) (protocol.Message, error) {
var ret protocol.Message
switch msgType {
case MessageTypeBlockRequest:
ret = &MsgBlockRequest{}
case MessageTypeBlock:
ret = &MsgBlock{}
case MessageTypeBlockTxsRequest:
ret = &MsgBlockTxsRequest{}
case MessageTypeBlockTxs:
ret = &MsgBlockTxs{}
case MessageTypeVotesRequest:
ret = &MsgVotesRequest{}
case MessageTypeVotes:
ret = &MsgVotes{}
case MessageTypeBlockRangeRequest:
ret = &MsgBlockRangeRequest{}
case MessageTypeLastBlockAndTxsInRange:
ret = &MsgLastBlockAndTxsInRange{}
case MessageTypeNextBlockAndTxsInRange:
ret = &MsgNextBlockAndTxsInRange{}
case MessageTypeDone:
ret = &MsgDone{}
}
if _, err := cbor.Decode(data, ret); err != nil {
return nil, fmt.Errorf("%s: decode error: %w", ProtocolName, err)
}
if ret != nil {
// Store the raw message CBOR
ret.SetCbor(data)
}
return ret, nil
}
func NewMsgFromCbor(msgType uint, data []byte) (protocol.Message, error) {
var ret protocol.Message
switch msgType {
case MessageTypeBlockRequest:
ret = &MsgBlockRequest{}
case MessageTypeBlock:
ret = &MsgBlock{}
case MessageTypeBlockTxsRequest:
ret = &MsgBlockTxsRequest{}
case MessageTypeBlockTxs:
ret = &MsgBlockTxs{}
case MessageTypeVotesRequest:
ret = &MsgVotesRequest{}
case MessageTypeVotes:
ret = &MsgVotes{}
case MessageTypeBlockRangeRequest:
ret = &MsgBlockRangeRequest{}
case MessageTypeLastBlockAndTxsInRange:
ret = &MsgLastBlockAndTxsInRange{}
case MessageTypeNextBlockAndTxsInRange:
ret = &MsgNextBlockAndTxsInRange{}
case MessageTypeDone:
ret = &MsgDone{}
}
if ret == nil {
return nil, fmt.Errorf("%s: unknown message type: %d", ProtocolName, msgType)
}
if _, err := cbor.Decode(data, ret); err != nil {
return nil, fmt.Errorf("%s: decode error: %w", ProtocolName, err)
}
if ret != nil {
// Store the raw message CBOR
ret.SetCbor(data)
}
return ret, nil
}
🤖 Prompt for AI Agents
In protocol/leiosfetch/messages.go around lines 41 to 73, the function may call cbor.Decode with ret == nil when msgType is unrecognized; add a guard to return a clear error before decoding (e.g., after the switch, if ret == nil return fmt.Errorf("%s: unknown message type %d", ProtocolName, msgType)), so decoding is only attempted for known types and avoid calling ret.SetCbor when ret is nil. 
Signed-off-by: Aurora Gaffney <aurora@blinklabs.io>
@agaffney agaffney force-pushed the feat/protocol-leios-fetch branch from 6ba18b7 to 7153902 Compare October 23, 2025 19:00
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
protocol/leiosfetch/messages.go (1)

41-73: Guard unknown message types before decoding (prevents nil decode panic)
If msgType is unrecognized, ret is nil and cbor.Decode will panic/err. Return early.

Apply this diff:

 func NewMsgFromCbor(msgType uint, data []byte) (protocol.Message, error) { var ret protocol.Message switch msgType { // cases... } + if ret == nil { + return nil, fmt.Errorf("%s: unknown message type: %d", ProtocolName, msgType) + } if _, err := cbor.Decode(data, ret); err != nil { return nil, fmt.Errorf("%s: decode error: %w", ProtocolName, err) } if ret != nil { // Store the raw message CBOR ret.SetCbor(data) } return ret, nil }
🧹 Nitpick comments (5)
protocol/leiosfetch/leiosfetch.go (1)

138-144: Lifecycle: constructing both Client and Server—confirm only one starts per connection
New() builds both sides sharing the same muxer/ProtocolId. Verify the connection bootstrap never starts both simultaneously for a single stream to avoid contention. If there’s any risk, expose role‑specific constructors or guard Starts.

protocol/leiosfetch/messages.go (2)

27-39: Finalize wire message IDs before merge
Comment says these are “dummy” IDs. Please lock them to the LEIOS wire spec to avoid breaking interop, and remove the note.


97-105: Deep‑clone raw payloads to avoid aliasing after return
Several constructors copy only the slice header (slices.Clone) but not each []byte. Use bytes.Clone per element for cbor.RawMessage and nested []byte (VoteIssuerId).

 func NewMsgBlock(block cbor.RawMessage) *MsgBlock { m := &MsgBlock{ MessageBase: protocol.MessageBase{ MessageType: MessageTypeBlock, }, - BlockRaw: block, + BlockRaw: bytes.Clone(block), } return m } func NewMsgBlockTxs(txs []cbor.RawMessage) *MsgBlockTxs { - m := &MsgBlockTxs{ + // deep-clone each raw message + cloned := make([]cbor.RawMessage, len(txs)) + for i := range txs { + cloned[i] = bytes.Clone(txs[i]) + } + m := &MsgBlockTxs{ MessageBase: protocol.MessageBase{ MessageType: MessageTypeBlockTxs, }, - TxsRaw: slices.Clone(txs), + TxsRaw: cloned, } return m } func NewMsgVotes(votes []cbor.RawMessage) *MsgVotes { - m := &MsgVotes{ + cloned := make([]cbor.RawMessage, len(votes)) + for i := range votes { + cloned[i] = bytes.Clone(votes[i]) + } + m := &MsgVotes{ MessageBase: protocol.MessageBase{ MessageType: MessageTypeVotes, }, - VotesRaw: slices.Clone(votes), + VotesRaw: cloned, } return m } func NewMsgNextBlockAndTxsInRange(block cbor.RawMessage, txs []cbor.RawMessage) *MsgNextBlockAndTxsInRange { - m := &MsgNextBlockAndTxsInRange{ + cloned := make([]cbor.RawMessage, len(txs)) + for i := range txs { + cloned[i] = bytes.Clone(txs[i]) + } + m := &MsgNextBlockAndTxsInRange{ MessageBase: protocol.MessageBase{ MessageType: MessageTypeNextBlockAndTxsInRange, }, - BlockRaw: block, - TxsRaw: slices.Clone(txs), + BlockRaw: bytes.Clone(block), + TxsRaw: cloned, } return m } func NewMsgLastBlockAndTxsInRange(block cbor.RawMessage, txs []cbor.RawMessage) *MsgLastBlockAndTxsInRange { - m := &MsgLastBlockAndTxsInRange{ + cloned := make([]cbor.RawMessage, len(txs)) + for i := range txs { + cloned[i] = bytes.Clone(txs[i]) + } + m := &MsgLastBlockAndTxsInRange{ MessageBase: protocol.MessageBase{ MessageType: MessageTypeLastBlockAndTxsInRange, }, - BlockRaw: block, - TxsRaw: slices.Clone(txs), + BlockRaw: bytes.Clone(block), + TxsRaw: cloned, } return m }

Additionally, consider deep‑cloning VoteIssuerId elements:

 func NewMsgVotesRequest(voteIds []MsgVotesRequestVoteId) *MsgVotesRequest { - m := &MsgVotesRequest{ + // deep clone ids and nested byte slices + cloned := make([]MsgVotesRequestVoteId, len(voteIds)) + for i := range voteIds { + cloned[i].Slot = voteIds[i].Slot + cloned[i].VoteIssuerId = bytes.Clone(voteIds[i].VoteIssuerId) + } + m := &MsgVotesRequest{ MessageBase: protocol.MessageBase{ MessageType: MessageTypeVotesRequest, }, - VoteIds: slices.Clone(voteIds), + VoteIds: cloned, } return m }

Also applies to: 131-139, 167-175, 200-209, 217-226

protocol/leiosfetch/server.go (1)

202-206: Restart on Done: verify concurrent range senders
If BlockRangeRequest spawns async senders, they may still write while Stop/initProtocol/Start swaps s.Protocol. Confirm callbacks tolerate ErrProtocolShuttingDown and won’t leak goroutines. Consider a per‑request context to cancel on Done.

protocol/leiosfetch/client.go (1)

43-48: Consider small buffers on result channels to reduce producer blocking
Unbuffered chans can transiently block the protocol goroutine if a response lands before the caller starts waiting. Buffer size 1 (or configurable) for single‑shot responses, and a modest buffer (e.g., 32) for ranges improves resilience.

- blockResultChan: make(chan protocol.Message), - blockTxsResultChan: make(chan protocol.Message), - votesResultChan: make(chan protocol.Message), - blockRangeResultChan: make(chan protocol.Message), + blockResultChan: make(chan protocol.Message, 1), + blockTxsResultChan: make(chan protocol.Message, 1), + votesResultChan: make(chan protocol.Message, 1), + blockRangeResultChan: make(chan protocol.Message, 32),
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6ba18b7 and 7153902.

📒 Files selected for processing (6)
  • connection.go (6 hunks)
  • protocol/leiosfetch/client.go (1 hunks)
  • protocol/leiosfetch/leiosfetch.go (1 hunks)
  • protocol/leiosfetch/messages.go (1 hunks)
  • protocol/leiosfetch/server.go (1 hunks)
  • protocol/leiosnotify/leiosnotify.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • connection.go
  • protocol/leiosnotify/leiosnotify.go
🧰 Additional context used
🧬 Code graph analysis (4)
protocol/leiosfetch/server.go (4)
protocol/protocol.go (6)
  • Protocol (39-54)
  • ProtocolOptions (93-102)
  • ProtocolConfig (57-71)
  • ProtocolRoleServer (89-89)
  • MessageHandlerFunc (110-110)
  • MessageFromCborFunc (113-113)
protocol/leiosfetch/leiosfetch.go (11)
  • Config (115-121)
  • CallbackContext (124-128)
  • ProtocolName (26-26)
  • ProtocolId (27-27)
  • StateMap (39-108)
  • StateIdle (31-31)
  • New (138-144)
  • BlockRequestFunc (132-132)
  • BlockTxsRequestFunc (133-133)
  • VotesRequestFunc (134-134)
  • BlockRangeRequestFunc (135-135)
protocol/leiosfetch/messages.go (10)
  • NewMsgFromCbor (41-73)
  • MessageTypeBlockRequest (29-29)
  • MessageTypeBlockTxsRequest (31-31)
  • MessageTypeVotesRequest (33-33)
  • MessageTypeBlockRangeRequest (35-35)
  • MessageTypeDone (38-38)
  • MsgBlockRequest (75-79)
  • MsgBlockTxsRequest (107-112)
  • MsgVotesRequest (141-144)
  • MsgBlockRangeRequest (177-181)
protocol/message.go (1)
  • Message (18-22)
protocol/leiosfetch/client.go (5)
protocol/protocol.go (6)
  • Protocol (39-54)
  • ProtocolOptions (93-102)
  • ProtocolConfig (57-71)
  • ProtocolRoleClient (88-88)
  • MessageHandlerFunc (110-110)
  • MessageFromCborFunc (113-113)
protocol/leiosfetch/leiosfetch.go (12)
  • Config (115-121)
  • CallbackContext (124-128)
  • NewConfig (148-157)
  • StateMap (39-108)
  • StateBlock (32-32)
  • StateBlockTxs (33-33)
  • StateVotes (34-34)
  • StateBlockRange (35-35)
  • ProtocolName (26-26)
  • ProtocolId (27-27)
  • StateIdle (31-31)
  • New (138-144)
protocol/leiosfetch/messages.go (13)
  • NewMsgFromCbor (41-73)
  • NewMsgDone (232-239)
  • NewMsgBlockRequest (81-90)
  • NewMsgBlockTxsRequest (114-124)
  • MsgVotesRequestVoteId (146-150)
  • NewMsgVotesRequest (152-160)
  • NewMsgBlockRangeRequest (183-192)
  • MsgLastBlockAndTxsInRange (211-215)
  • MessageTypeBlock (30-30)
  • MessageTypeBlockTxs (32-32)
  • MessageTypeVotes (34-34)
  • MessageTypeNextBlockAndTxsInRange (37-37)
  • MessageTypeLastBlockAndTxsInRange (36-36)
protocol/error.go (1)
  • ErrProtocolShuttingDown (19-19)
protocol/common/types.go (1)
  • Point (23-28)
protocol/leiosfetch/leiosfetch.go (8)
protocol/state.go (6)
  • NewState (38-43)
  • StateMapEntry (62-66)
  • AgencyClient (27-27)
  • StateTransition (51-55)
  • AgencyServer (28-28)
  • AgencyNone (26-26)
protocol/leiosfetch/messages.go (11)
  • MessageTypeBlockRequest (29-29)
  • MessageTypeBlockTxsRequest (31-31)
  • MessageTypeVotesRequest (33-33)
  • MessageTypeBlockRangeRequest (35-35)
  • MessageTypeDone (38-38)
  • MessageTypeBlock (30-30)
  • MessageTypeBlockTxs (32-32)
  • MessageTypeVotes (34-34)
  • MessageTypeNextBlockAndTxsInRange (37-37)
  • MessageTypeLastBlockAndTxsInRange (36-36)
  • MsgVotesRequestVoteId (146-150)
protocol/leiosfetch/client.go (2)
  • Client (25-35)
  • NewClient (37-87)
protocol/leiosfetch/server.go (2)
  • Server (24-29)
  • NewServer (31-43)
connection.go (2)
  • ConnectionId (56-56)
  • New (133-135)
protocol/message.go (1)
  • Message (18-22)
protocol/common/types.go (1)
  • Point (23-28)
protocol/protocol.go (1)
  • ProtocolOptions (93-102)
protocol/leiosfetch/messages.go (5)
protocol/message.go (2)
  • Message (18-22)
  • MessageBase (25-30)
cbor/decode.go (1)
  • Decode (29-44)
protocol/leiosfetch/leiosfetch.go (1)
  • ProtocolName (26-26)
cbor/cbor.go (2)
  • RawMessage (36-36)
  • StructAsArray (45-48)
protocol/common/types.go (1)
  • Point (23-28)
🔇 Additional comments (3)
protocol/leiosfetch/leiosfetch.go (1)

25-28: Confirm ProtocolId uniqueness and stability across mux
Ensure 19 doesn’t collide with existing/adjacent mini‑protocol IDs and is final for wire compatibility. If undecided, gate behind a const in a central registry or document the allocation.

protocol/leiosfetch/server.go (1)

170-176: LGTM: log string matches handler
“block range request” accurately reflects BlockRangeRequest.

protocol/leiosfetch/client.go (1)

166-182: LGTM: BlockRangeRequest now consumes from blockRangeResultChan
This resolves the deadlock reported earlier.

@agaffney agaffney merged commit 36845d7 into main Oct 24, 2025
10 checks passed
@agaffney agaffney deleted the feat/protocol-leios-fetch branch October 24, 2025 11:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants