Skip to content

Conversation

@philanton
Copy link
Contributor

@philanton philanton commented Nov 13, 2025

Summary by CodeRabbit

  • New Features

    • Added a "resizing" channel status, per-asset resizing flag, and channel participant tracking.
    • Channel flows now resolve and use the correct participant signer for open/close/resize operations.
  • Bug Fixes

    • Guards to prevent concurrent/duplicate resize operations and ensure consistent signer usage across custody calls.
  • Breaking/SDK Changes

    • Channel creation payload split into unsigned initial state and server signature.
  • Tests

    • Unit and integration tests updated; new backward-compatibility integration test added.

@philanton philanton requested a review from a team as a code owner November 13, 2025 13:06
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 13, 2025

Walkthrough

Adds a new ChannelStatus value "resizing", propagates channel participant and resizing state into Cerebro configs, introduces participant-aware signer selection and a resize-in-progress guard in channel ops, updates SDK signer resolution and tests, and adds integration tests for session-key vs wallet flows.

Changes

Cohort / File(s) Change Summary
RPC API Constant
clearnode/pkg/rpc/api.go
Added ChannelStatusResizing ChannelStatus = "resizing".
Cerebro config & operator
examples/cerebro/config.go, examples/cerebro/operator.go
Added ChannelResizing bool and ChannelParticipant string to ChainAssetConfig; operator fetches channels without the "open" filter, treats resizing as valid, captures participant and resizing flag when building config.
Cerebro channel ops
examples/cerebro/channel_ops.go
Guard to prevent concurrent resize if ChannelResizing true; compute participant-aware signer (wallet vs session) and pass participant signer into Open/Close/Resize custody calls.
SDK: state signer resolution & docs
sdk/src/client/state.ts, sdk/src/client/prepare.ts
Added participant-aware signer resolution helpers and replaced direct stateSigner usage with resolved signer; fixed JSDoc to reference stateSigner.signState; adjusted imports.
SDK tests / types
sdk/test/unit/client/index.test.ts, sdk/test/unit/client/state.test.ts
Updated tests/types: CreateChannelParams renamed initialStateunsignedInitialState and added serverSignature; adjusted mocks and expectations for signer-resolution and channel data; added walletClient/chainId/getPackedState mocks.
Integration helper
integration/common/nitroliteClient.ts
Constructor now accepts optional StateSigner (defaults to identity.stateWalletSigner); super call uses provided/default signer.
Integration tests
integration/tests/backward_compatibility/onchain_ops_with_sk.test.ts
New integration test covering on-chain flows with Session Key and Wallet participants: create/deposit/resize/challenge/close/withdraw sequences and assertions.
Auth comments
clearnode/auth.go
Comment clarifications: Challenge.SessionKeyExpiresAt and Policy.ExpiresAt documented as Unix timestamps (seconds).
Module manifest
go.mod
Present in diff (no behavioral change).

Sequence Diagram(s)

sequenceDiagram
    participant Operator as Operator
    participant RPC as Clearnode RPC (GetChannels)
    participant Config as ChainAssetConfig
    Operator->>RPC: Request channels (no "open" filter)
    RPC-->>Operator: Return channels list
    Operator->>Operator: For each channel\n- ignore if status ∉ {"open","resizing"}\n- if status == "resizing": set ChannelResizing=true, record ChannelParticipant\n- if match: capture ChannelID/RawAmount
    Operator->>Config: Build ChainAssetConfig with ChannelResizing & ChannelParticipant
    note right of Config `#DDFFDD`: New fields added to config
Loading
sequenceDiagram
    participant Ops as ChannelOps (Open/Close/Resize)
    participant Resolver as SignerResolver
    participant Wallet as WalletClient
    participant Session as SessionKeySigner
    Ops->>Resolver: Resolve participant signer (participant == wallet ? WalletStateSigner : session signer)
    Resolver-->>Ops: Return participantSigner
    Ops->>Ops: If config.ChannelResizing == true -> early exit (guard)
    alt proceed
        Ops->>CustodyAPI: Call Open/Close/Resize with Wallet + participantSigner
        CustodyAPI-->>Ops: Return result
    end
    note right of Ops `#FFF2CC`: participant-aware signing applied to all custody calls
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Focus review on signer-resolution logic and its propagation in SDK (sdk/src/client/state.ts).
  • Verify participant signer usage and resize guard in examples/cerebro/channel_ops.go.
  • Check operator channel filtering and config population in examples/cerebro/operator.go.
  • Quick check of new RPC constant in clearnode/pkg/rpc/api.go and integration test stability.

Files meriting extra attention:

  • sdk/src/client/state.ts
  • examples/cerebro/channel_ops.go
  • integration/tests/backward_compatibility/onchain_ops_with_sk.test.ts

Possibly related PRs

Suggested labels

ready

Suggested reviewers

  • nksazonov
  • alessio

Poem

🐇 I found a channel, small and sizing,

I chose the paw that fits the lining.
I guard the hop, avoid the race,
sign with the right and keep the pace.
A tiny tag, a gentler prance.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main change: implementing correct state signer selection for channel operations based on participant identity.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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 fix/cerebro-resizing

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0d7bb0b and 701f5b6.

📒 Files selected for processing (2)
  • examples/cerebro/channel_ops.go (4 hunks)
  • examples/cerebro/operator.go (2 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: dimast-x
Repo: erc7824/nitrolite PR: 411
File: clearnode/custody.go:425-427
Timestamp: 2025-11-04T10:39:28.297Z
Learning: In clearnode/custody.go's handleResized function, when a resize event is received for a channel with status ChannelStatusOpen (not ChannelStatusResizing), the code intentionally logs an error but continues processing the resize operation normally. This is the expected behavior.
Learnt from: dimast-x
Repo: erc7824/nitrolite PR: 411
File: clearnode/custody.go:449-465
Timestamp: 2025-11-05T10:19:06.155Z
Learning: In clearnode/custody.go's handleResized function during positive resize operations (deposits), the ledger flow is: on-chain funds first credit the channel account, are immediately debited from the channel account, then credit the unified wallet account. The channel account acts as a pass-through for deposits. The unified wallet account is where actual deposited funds are held. This is recorded as a deposit transaction from channelAccountID to walletAccountID.
📚 Learning: 2025-11-05T10:19:06.155Z
Learnt from: dimast-x
Repo: erc7824/nitrolite PR: 411
File: clearnode/custody.go:449-465
Timestamp: 2025-11-05T10:19:06.155Z
Learning: In clearnode/custody.go's handleResized function during positive resize operations (deposits), the ledger flow is: on-chain funds first credit the channel account, are immediately debited from the channel account, then credit the unified wallet account. The channel account acts as a pass-through for deposits. The unified wallet account is where actual deposited funds are held. This is recorded as a deposit transaction from channelAccountID to walletAccountID.

Applied to files:

  • examples/cerebro/channel_ops.go
  • examples/cerebro/operator.go
📚 Learning: 2025-11-04T19:14:48.960Z
Learnt from: dimast-x
Repo: erc7824/nitrolite PR: 411
File: clearnode/custody.go:542-553
Timestamp: 2025-11-04T19:14:48.960Z
Learning: In clearnode/custody.go's handleClosed function, when unlocking escrow funds on channel close, the code intentionally passes the channelEscrowAccountBalance (which may be negative) directly to ledger.Record for the wallet account. When the escrow balance is negative (e.g., resize confirmation received for a channel not in resizing state), this causes a debit to the unified wallet account, which is the expected behavior to equalize accounting. Temporary negative unified balances in this scenario are acceptable.

Applied to files:

  • examples/cerebro/channel_ops.go
  • examples/cerebro/operator.go
📚 Learning: 2025-11-04T10:39:28.297Z
Learnt from: dimast-x
Repo: erc7824/nitrolite PR: 411
File: clearnode/custody.go:425-427
Timestamp: 2025-11-04T10:39:28.297Z
Learning: In clearnode/custody.go's handleResized function, when a resize event is received for a channel with status ChannelStatusOpen (not ChannelStatusResizing), the code intentionally logs an error but continues processing the resize operation normally. This is the expected behavior.

Applied to files:

  • examples/cerebro/channel_ops.go
  • examples/cerebro/operator.go
🧬 Code graph analysis (2)
examples/cerebro/channel_ops.go (3)
clearnode/pkg/sign/sign.go (3)
  • Signer (11-15)
  • PublicKey (23-26)
  • Address (29-34)
clearnode/pkg/rpc/api.go (1)
  • Channel (666-693)
examples/cerebro/custody/custody_binding.go (1)
  • Channel (40-45)
examples/cerebro/operator.go (2)
clearnode/pkg/sign/sign.go (2)
  • PublicKey (23-26)
  • Address (29-34)
examples/cerebro/config.go (1)
  • ChainAssetConfig (75-84)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build and Publish (Clearnode)
  • GitHub Check: Test (Integration) / Test Integration
  • GitHub Check: Analyze (go)
🔇 Additional comments (6)
examples/cerebro/channel_ops.go (3)

154-159: LGTM!

The participant signer selection logic correctly uses the stored asset.ChannelParticipant and applies consistent signer resolution for the close operation.


214-217: LGTM!

The guard against concurrent resizing prevents race conditions and provides clear feedback to the user.


319-324: LGTM!

The participant signer selection logic is consistent with the close operation and correctly applies participant-aware signer resolution for resize operations.

examples/cerebro/operator.go (3)

256-256: LGTM!

Changing the filter from "open" to "" (no filter) allows the code to fetch all channels and apply client-side filtering for both "open" and "resizing" statuses.


270-283: LGTM - Past issue resolved!

The channelResizing flag logic has been fixed. The assignment on line 283 now occurs AFTER the channel match is verified on line 279, preventing incorrect state tracking when one asset is resizing while others are not.


288-296: LGTM!

The ChainAssetConfig construction correctly includes the new ChannelParticipant and ChannelResizing fields, enabling participant-aware signer selection and resize guards in channel operations.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @philanton, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a new ChannelStatusResizing state to the system, enabling better tracking and management of channels undergoing resizing. The cerebro example application has been updated to integrate this new status, preventing redundant resize attempts and ensuring that channel states are accurately reflected and handled throughout its operations.

Highlights

  • New Channel Status: Introduced a new ChannelStatusResizing enum value in the RPC API to explicitly represent channels that are currently undergoing a resizing operation.
  • Cerebro Channel State Management: Updated the cerebro example application to recognize and manage channels that are in a 'resizing' state, enhancing its ability to track channel lifecycle.
  • Prevent Duplicate Resizing: Added a check within cerebro's handleResizeChannel function to prevent initiating a new resize operation on a channel that is already marked as 'resizing'.
  • Comprehensive Channel Fetching: Modified the GetChannels RPC call in cerebro to fetch channels regardless of their status (instead of only 'open' channels), allowing for proper identification and handling of 'resizing' channels during configuration reload.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@codecov
Copy link

codecov bot commented Nov 13, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a 'resizing' status for channels to prevent concurrent resize operations. The changes span across the RPC API definition, cerebro's configuration, and operational logic. While the intent is good, I've found a logical flaw in how the 'resizing' status is being determined for an asset's channel in examples/cerebro/operator.go. The current implementation incorrectly flags a channel as resizing if any channel is resizing, not just the specific one for the asset. I've provided a suggestion to fix this. The rest of the changes look good.

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: 2

🧹 Nitpick comments (1)
examples/cerebro/channel_ops.go (1)

72-75: Consider extracting participant signer selection logic.

The participant signer selection pattern appears in three locations with minor variations. While the current implementation is clear, consider extracting to a helper method for better maintainability.

For example:

func (o *Operator) selectParticipantSigner(participantAddress string) sign.Signer {
	if participantAddress == o.config.Wallet.PublicKey().Address().String() {
		return o.config.Wallet
	}
	return o.config.Signer
}

Then use: participantSigner := o.selectParticipantSigner(asset.ChannelParticipant)

Also applies to: 154-157, 319-322

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3df9a2b and 5a981bb.

📒 Files selected for processing (3)
  • examples/cerebro/channel_ops.go (4 hunks)
  • examples/cerebro/config.go (1 hunks)
  • examples/cerebro/operator.go (2 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: dimast-x
Repo: erc7824/nitrolite PR: 411
File: clearnode/custody.go:425-427
Timestamp: 2025-11-04T10:39:28.297Z
Learning: In clearnode/custody.go's handleResized function, when a resize event is received for a channel with status ChannelStatusOpen (not ChannelStatusResizing), the code intentionally logs an error but continues processing the resize operation normally. This is the expected behavior.
📚 Learning: 2025-11-05T10:19:06.155Z
Learnt from: dimast-x
Repo: erc7824/nitrolite PR: 411
File: clearnode/custody.go:449-465
Timestamp: 2025-11-05T10:19:06.155Z
Learning: In clearnode/custody.go's handleResized function during positive resize operations (deposits), the ledger flow is: on-chain funds first credit the channel account, are immediately debited from the channel account, then credit the unified wallet account. The channel account acts as a pass-through for deposits. The unified wallet account is where actual deposited funds are held. This is recorded as a deposit transaction from channelAccountID to walletAccountID.

Applied to files:

  • examples/cerebro/channel_ops.go
📚 Learning: 2025-11-04T19:14:48.960Z
Learnt from: dimast-x
Repo: erc7824/nitrolite PR: 411
File: clearnode/custody.go:542-553
Timestamp: 2025-11-04T19:14:48.960Z
Learning: In clearnode/custody.go's handleClosed function, when unlocking escrow funds on channel close, the code intentionally passes the channelEscrowAccountBalance (which may be negative) directly to ledger.Record for the wallet account. When the escrow balance is negative (e.g., resize confirmation received for a channel not in resizing state), this causes a debit to the unified wallet account, which is the expected behavior to equalize accounting. Temporary negative unified balances in this scenario are acceptable.

Applied to files:

  • examples/cerebro/channel_ops.go
📚 Learning: 2025-11-04T10:39:28.297Z
Learnt from: dimast-x
Repo: erc7824/nitrolite PR: 411
File: clearnode/custody.go:425-427
Timestamp: 2025-11-04T10:39:28.297Z
Learning: In clearnode/custody.go's handleResized function, when a resize event is received for a channel with status ChannelStatusOpen (not ChannelStatusResizing), the code intentionally logs an error but continues processing the resize operation normally. This is the expected behavior.

Applied to files:

  • examples/cerebro/channel_ops.go
🧬 Code graph analysis (2)
examples/cerebro/channel_ops.go (2)
clearnode/pkg/sign/sign.go (3)
  • Signer (11-15)
  • PublicKey (23-26)
  • Address (29-34)
examples/cerebro/custody/custody_binding.go (1)
  • Channel (40-45)
examples/cerebro/operator.go (2)
clearnode/pkg/sign/sign.go (2)
  • PublicKey (23-26)
  • Address (29-34)
examples/cerebro/config.go (1)
  • ChainAssetConfig (75-84)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build and Publish (Clearnode)
  • GitHub Check: Test (Integration) / Test Integration
  • GitHub Check: Analyze (go)
🔇 Additional comments (6)
examples/cerebro/config.go (1)

80-83: LGTM!

The new fields ChannelResizing and ChannelParticipant cleanly extend the per-asset channel state tracking. The additions integrate well with the operator config loading and channel operations logic.

examples/cerebro/channel_ops.go (3)

154-159: LGTM!

The participant signer selection correctly uses the stored ChannelParticipant from the asset config to determine whether to use the wallet or default signer for closing the channel.


214-217: LGTM - Guard prevents concurrent resize operations.

The check correctly prevents initiating a resize when one is already in progress for the asset.

Note: This guard's effectiveness depends on the ChannelResizing flag being correctly populated in operator.go. Please verify that the flag is set accurately when loading channel state.


319-324: LGTM!

The participant signer selection logic is consistent with the approach used in OpenChannel and CloseChannel.

examples/cerebro/operator.go (2)

256-256: LGTM - Fetching all channels now.

Removing the "open" filter to fetch all channels is necessary to track channels in the "resizing" state.


288-296: ChainAssetConfig construction looks correct.

The struct initialization properly includes the new ChannelParticipant and ChannelResizing fields. However, the correctness of ChannelResizing depends on fixing the flag logic in lines 274-278.

@alessio alessio self-requested a review November 14, 2025 08:04
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: 0

🧹 Nitpick comments (2)
clearnode/auth.go (1)

23-24: Clarify ExpiresAt comment to match the time.Time type

Challenge.SessionKeyExpiresAt is a Unix timestamp (uint64), but Policy.ExpiresAt is a time.Time while its comment still says “Unix timestamp (in seconds)”. That can confuse readers/clients expecting a raw numeric timestamp from JSON.

Consider rewording the ExpiresAt comment to something like “Expiration time (derived from a Unix timestamp)” or, if you truly want a numeric Unix value in the policy, switch the field type to uint64 for consistency.

Also applies to: 47-54

sdk/src/client/state.ts (1)

1-18: Participant-aware signer selection is consistent and backward-compatible

The new _fetchParticipantAndGetSigner / _checkParticipantAndGetSigner helpers and their integration into initial, challenge, resize, and final signing flows look coherent:

  • Initial state uses _checkParticipantAndGetSigner with channel.participants[0], matching the existing “first participant is the local user” assumption you already enforce with two participants.
  • Challenge/resize/final states pull participants from nitroliteService.getChannelData and gracefully fall back to stateSigner when the channel shape is unexpected (non-2 participants).
  • When the selected participant matches walletClient.account.address, you switch to WalletStateSigner, correctly routing signatures through the wallet; otherwise, you retain the existing stateSigner behavior, preserving pre-v0.5.0 compatibility.

The only thing to keep in mind is the explicit reliance on participants[0] as the user participant. If the participant ordering ever changes or multi-party channels are introduced, this will be the main place to revisit.

Also applies to: 63-66, 82-95, 106-128, 147-168, 187-208

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5a981bb and ac4ebce.

📒 Files selected for processing (5)
  • clearnode/auth.go (2 hunks)
  • sdk/src/client/prepare.ts (1 hunks)
  • sdk/src/client/state.ts (6 hunks)
  • sdk/test/unit/client/index.test.ts (5 hunks)
  • sdk/test/unit/client/state.test.ts (3 hunks)
✅ Files skipped from review due to trivial changes (1)
  • sdk/src/client/prepare.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: dimast-x
Repo: erc7824/nitrolite PR: 411
File: clearnode/custody.go:425-427
Timestamp: 2025-11-04T10:39:28.297Z
Learning: In clearnode/custody.go's handleResized function, when a resize event is received for a channel with status ChannelStatusOpen (not ChannelStatusResizing), the code intentionally logs an error but continues processing the resize operation normally. This is the expected behavior.
📚 Learning: 2025-11-13T10:32:14.531Z
Learnt from: nksazonov
Repo: erc7824/nitrolite PR: 428
File: clearnode/rpc_router_auth.go:233-244
Timestamp: 2025-11-13T10:32:14.531Z
Learning: In clearnode/rpc_router_auth.go, when CheckSessionKeyExists returns ErrSessionKeyExistsAndExpired during authentication, the error should be returned to the client and block re-authentication. This is intentional behavior - expired session keys should not allow automatic re-authentication and replacement in the auth flow.

Applied to files:

  • clearnode/auth.go
🧬 Code graph analysis (1)
sdk/src/client/state.ts (3)
sdk/src/utils/channel.ts (1)
  • getChannelId (11-25)
sdk/src/client/prepare.ts (1)
  • PreparerDependencies (39-48)
sdk/src/client/types.ts (1)
  • ChannelId (8-8)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build and Publish (Clearnode)
  • GitHub Check: Test (Integration) / Test Integration
  • GitHub Check: Analyze (go)
🔇 Additional comments (6)
sdk/test/unit/client/state.test.ts (3)

11-19: Utils mock shape looks compatible with updated state logic

Adding getPackedState to the ../../../src/utils mock keeps the module shape broad enough for current and future uses in state.ts without impacting these tests (which only hit getChannelId here). No issues spotted.


35-49: Deps wiring for initial-state tests matches new signer resolution

Providing walletClient (with account and signMessage) and chainId in deps aligns with the updated PreparerDependencies and ensures _checkParticipantAndGetSigner can safely inspect the wallet vs. session signer. The tests still exercise the stateSigner path as intended since the first participant differs from the wallet account.


154-183: Final-state deps correctly exercise _fetchParticipantAndGetSigner

The added walletClient, nitroliteService.getChannelData mock, and chainId let _prepareAndSignFinalState resolve the signer via _fetchParticipantAndGetSigner without runtime surprises. The mock channel data ensures the “non-wallet participant” branch uses stateSigner, matching the test expectations.

sdk/test/unit/client/index.test.ts (3)

78-83: Overriding sharedDeps services keeps client and state helpers aligned

Updating client.sharedDeps.nitroliteService and client.sharedDeps.erc20Service to the same mocks used for the client’s private services ensures all flows that go through PreparerDependencies (e.g., state helpers) operate on the test doubles, avoiding accidental real calls. This is an important fix for the new sharedDeps-based design.


127-153: CreateChannelParams and initial-state expectations updated correctly

Switching to unsignedInitialState plus a separate serverSignature in CreateChannelParams, and having _prepareAndSignInitialState return the fully signed initialState, matches the updated API. The test correctly:

  • Constructs initialState by augmenting unsignedInitialState with sigs.
  • Mocks _prepareAndSignInitialState to return { initialState, channelId }.
  • Verifies that createChannel passes the signed initialState into nitroliteService.createChannel and returns it in the result.

Looks solid.

Also applies to: 156-173


240-252: challengeChannel tests now cover participant-aware signing

The new getChannelData mocks in the challengeChannel tests provide a realistic channel structure (participants, status, wallets, etc.), allowing _fetchParticipantAndGetSigner to pick the wallet-based signer path. As a result, asserting that mockNitroService.challenge receives mockSignature as the last argument effectively verifies that the wallet signer is being used end-to-end, while the failure test still checks error propagation with the same setup.

These changes line up well with the new signer-resolution logic.

Also applies to: 309-321

@philanton philanton changed the title YNU-422: handle resizing channel status in cerebro YNU-422: do channel operations with the correct state signer Nov 18, 2025
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: 0

🧹 Nitpick comments (4)
integration/tests/backward_compatibility/onchain_ops_with_sk.test.ts (4)

36-51: Global setup/teardown looks solid; consider initial Clearnode reset for full isolation

The shared BlockchainUtils, DatabaseUtils, Identity, TestWebSocket, and auth setup are consistent and appropriate for this suite, and the final ws.close()/databaseUtils.close() cleanup is good.

If you want this file to be completely self-contained regardless of what other integration tests do, you might also call await databaseUtils.resetClearnodeState() once in beforeAll to guarantee a clean DB/session-key cache before any scenarios run, not just after each inner describe.


60-125: Manual construction of legacy session‑key channel is correct; simplify initial state handling

The way you manually construct a Channel with participants: [sessionKey, clearnode], derive channelId, and sign unsignedInitialState with both the session key and clearnodeSigner accurately simulates the legacy “session key as participant” layout.

One thing that could be simplified: depositAndCreateChannel conceptually expects something akin to an unsigned/partially signed initial state plus a separate serverSignature. Passing an initialState that already has both userSignature and clearnodeSignature as unsignedInitialState is slightly confusing and may become brittle if depositAndCreateChannel ever starts relying on sigs being empty or server-only. You could avoid this by:

  • Keeping unsignedInitialState with sigs: [] (or at most the server sig, depending on the intended contract).
  • Letting depositAndCreateChannel and the configured stateSigner handle user signing, using only the serverSignature parameter.

This would more closely mirror the RPC path used in the wallet-participant flow and reduce the chance of subtle signature handling changes breaking this test in the future.


132-267: Session‑key participant lifecycle flow is coherent but tightly couples tests via shared mutable state

The four it blocks here nicely exercise resize → challenge → checkpoint → close → withdraw over the same channel, and the assertions on lastValidState.version, ChannelStatus.VOID, and balances align with the intended lifecycle.

The trade‑off is that currentState and currentVersion are mutated across tests, so the suite relies on them running in order and all succeeding. For integration tests this is often acceptable, but it does make failures harder to isolate and can hinder parallelization.

If you ever want more robust isolation, consider either:

  • Combining these into a single “full lifecycle” test, or
  • Cloning the fundamental setup into independent flows per it (at the cost of additional runtime).

As a tiny nit, the hard‑coded toRaw(BigInt(500)) in the withdrawal mirrors the earlier resizeAmount of 500 USDC; reusing a shared constant would avoid the magic number duplication.


322-458: Wallet‑participant lifecycle mirrors the session‑key flow; minor nits only

The resize, challenge, checkpoint, close, and withdraw tests here mirror the earlier session‑key scenario and provide good coverage that operations still work end‑to‑end when the channel participant is the wallet:

  • Resize uses Clearnode RPC and asserts lastValidState.version and balance.
  • Challenge/checkpoint craft states signed by the wallet and clearnodeSigner, then submit via the client.
  • Close uses closeChannel with a final state and checks for ChannelStatus.VOID.
  • Withdraw asserts the account balance returns to zero.

Two small optional nits:

  • As above, the flow depends on currentState/currentVersion being mutated across its; acceptable for integration, but worth being aware of for debugging and future parallelization.
  • There is a bit of duplication between this block and the session‑key block (version bumping, manual state construction). If the flows start to diverge more, a tiny helper for “build signed OPERATE/FINALIZE state from currentState/currentVersion” might reduce boilerplate and keep the tests easier to evolve.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ac4ebce and 0d7bb0b.

📒 Files selected for processing (2)
  • integration/common/nitroliteClient.ts (2 hunks)
  • integration/tests/backward_compatibility/onchain_ops_with_sk.test.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • integration/common/nitroliteClient.ts
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: dimast-x
Repo: erc7824/nitrolite PR: 411
File: clearnode/custody.go:425-427
Timestamp: 2025-11-04T10:39:28.297Z
Learning: In clearnode/custody.go's handleResized function, when a resize event is received for a channel with status ChannelStatusOpen (not ChannelStatusResizing), the code intentionally logs an error but continues processing the resize operation normally. This is the expected behavior.
🧬 Code graph analysis (1)
integration/tests/backward_compatibility/onchain_ops_with_sk.test.ts (9)
integration/common/ws.ts (2)
  • TestWebSocket (9-139)
  • getCreateChannelPredicate (193-197)
integration/common/identity.ts (1)
  • Identity (7-33)
integration/common/blockchainUtils.ts (1)
  • BlockchainUtils (22-222)
integration/common/databaseUtils.ts (1)
  • DatabaseUtils (7-135)
integration/common/setup.ts (2)
  • CONFIG (4-44)
  • chain (46-46)
integration/common/auth.ts (1)
  • createAuthSessionWithClearnode (11-45)
integration/common/nitroliteClient.ts (1)
  • TestNitroliteClient (28-193)
sdk/src/utils/channel.ts (3)
  • getChannelId (11-25)
  • convertRPCToClientState (69-81)
  • convertRPCToClientChannel (60-67)
integration/common/testHelpers.ts (1)
  • toRaw (21-23)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build and Publish (Clearnode)
  • GitHub Check: Test (Integration) / Test Integration
  • GitHub Check: Analyze (go)
🔇 Additional comments (1)
integration/tests/backward_compatibility/onchain_ops_with_sk.test.ts (1)

276-315: Wallet‑participant scenario correctly exercises backward compatibility with a session‑key signer

This setup correctly reflects the backward‑compat use case:

  • The channel is created via Clearnode RPC using identity.messageWalletSigner, so the participant is the wallet address.
  • TestNitroliteClient is instantiated with identity.stateSKSigner, matching how newer clients will operate.
  • depositAndCreateChannel uses the RPC-derived convertRPCToClientState and convertRPCToClientChannel, and the post‑creation assertion confirms participants[0] === walletAddress and ChannelStatus.ACTIVE.

This is a good targeted scenario for validating the new signer‑resolution logic against legacy wallet‑participant channels.

@philanton philanton force-pushed the fix/cerebro-resizing branch from 654e2d1 to 701f5b6 Compare November 18, 2025 11:38
@nksazonov nksazonov merged commit f863561 into main Nov 18, 2025
11 checks passed
@nksazonov nksazonov deleted the fix/cerebro-resizing branch November 18, 2025 14:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants