Skip to content

Conversation

@ws4charlie
Copy link
Contributor

@ws4charlie ws4charlie commented Nov 12, 2025

Description

Remaining work:

  • unit tests

This PR implements a sequential batch TSS keysign scheduler for EVM chain, improving outbound speed by 4~5X.

  1. Decouple CCTX process goroutine scheduleCCTX and TSS keysign scheduler goroutine scheduleKeysign.

  2. Use an artificial (deterministic) height instead of real ZetaChain height to create TSS keysign request.
    This improves outbound performance by 2X.

  3. Schedule TSS keysign for batched digests instead of only one single digest. Reduced total keysign requests number from multiple to only one (per chain).

  4. Schedule TSS keysign by nonce (batched) sequentially without waiting intervals, replacing the existing interval based logic zeta_height % interval == cctx_nonce % interval.
    This improves outbound performance by 2~3X


The eth withdraw stress test result before:
image

The result after:
image

Closes #4436

How Has This Been Tested?

  • Tested CCTX in localnet
  • Tested in development environment
  • Go unit tests
  • Go integration tests
  • Tested via GitHub Actions

Note

Cursor Bugbot is generating a summary for commit cdf2b23. Configure here.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 12, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

This pull request introduces a batched TSS keysign system to improve EVM chain outbound performance. It refactors signing workflows to use per-nonce digest caching, batch multiple keysigns into a single TSS operation, and eliminates height-based scheduling in favor of a nonce-driven approach with stale-block detection.

Changes

Cohort / File(s) Summary
Cantor Pairing Utilities
pkg/math/pairing.go, pkg/math/pairing_test.go
Introduces Cantor pairing functions (CantorPair, CantorUnpair) and MaxPairValue constant for mapping uint32 pairs to uint64 values, with comprehensive round-trip testing.
Base Signer Batch Infrastructure
zetaclient/chains/base/signer.go, zetaclient/chains/base/signer_batch_info.go, zetaclient/chains/base/signer_batch_sign.go
Adds per-nonce TSS tracking (tssKeysignInfoMap, nextTSSNonce), introduces TSSKeysignInfo and TSSKeysignBatch structures, implements batch accumulation logic, readiness checks, signing workflows, and nonce-to-batch mapping utilities. Changes mu from Mutex to RWMutex for concurrent access.
EVM Signer Refactoring
zetaclient/chains/evm/signer/outbound_data.go, zetaclient/chains/evm/signer/outbound_data_test.go, zetaclient/chains/evm/signer/sign.go, zetaclient/chains/evm/signer/sign_test.go, zetaclient/chains/evm/signer/signer.go, zetaclient/chains/evm/signer/signer_admin.go, zetaclient/chains/evm/signer/signer_admin_test.go, zetaclient/chains/evm/signer/signer_test.go, zetaclient/chains/evm/signer/v2_sign.go
Removes height parameter from NewOutboundData and Sign operations. Replaces TSS-based signing with GetSignatureOrAddDigest flow, introducing ErrWaitForSignature for async keysign awaiting. Adds NextTSSNonce method. Updates test infrastructure with digest-based mocking and signature preloading for all signing paths.
EVM Chain Scheduler Refactoring
zetaclient/chains/evm/evm.go
Introduces scheduleKeysign method with batch preparation, readiness checks, and sequential batch signing. Refactors scheduleCCTX to use NextTSSNonce instead of tracker-based nonce heuristics, adds stale-block-event skipping logic, simplifies conflict checking. Removes getTrackerSet helper and tracker-based gating.
Client and Repository Interface Extensions
zetaclient/chains/tssrepo/client.go, zetaclient/chains/zrepo/client.go, zetaclient/chains/zrepo/zrepo.go, zetaclient/dry/dry.go, zetaclient/testutils/mocks/tss.go, zetaclient/testutils/mocks/zetacore.go, zetaclient/mode/chaos/generated.go, zetaclient/tss/service.go
Adds IsSignatureCached method to TSSClient interface and implementations (TSSService, mocks, chaos). Adds GetBlockHeight method to ZetacoreReaderClient interface and implementations (ZetaRepo, mocks, chaos). Updates mock parameter naming for clarity.
Metrics and Monitoring
zetaclient/metrics/metrics.go
Introduces NextTSSNonce gauge metric (per-chain) for observability of TSS account nonce state.
Test Performance and Configuration
cmd/zetae2e/local/performance.go, zetaclient/mode/chaos/generate/sample.json
Moves timer start in withdraw performance test to after deposit step, measuring only keysign execution time. Adds GetBlockHeight configuration entry to chaos generator.
Documentation
changelog.md
Documents new batch keysign feature for EVM performance improvement.
Observer Comments
zetaclient/chains/evm/observer/outbound.go
Adds explanatory comment about batch keysign usage and deprecation of continueKeysign flag.

Sequence Diagram(s)

sequenceDiagram
    participant Scheduler as EVM Scheduler
    participant Signer as Base Signer
    participant Batch as Batch Manager
    participant TSS as TSS Service
    participant Cache as Signature Cache
    
    Scheduler->>Signer: PrepareForKeysign(zetaHeight, nextNonce)
    Signer->>Signer: Check stale blocks
    Signer->>Signer: Clean stale keysign info
    Signer-->>Scheduler: Ready: bool
    
    alt Batch ready to sign
        Scheduler->>Signer: GetKeysignBatch(batchNumber)
        Signer->>Batch: Collect digests in nonce range
        Batch-->>Signer: TSSKeysignBatch
        Signer->>Signer: SignBatch(batch)
        Signer->>Signer: Compute keysignHeight via Cantor pairing
        Signer->>TSS: SignBatch(digests, height)
        TSS->>Cache: Store signatures
        Signer->>Signer: AddBatchSignatures(batch, sigs)
    else Waiting for signatures
        Signer->>Cache: GetSignatureOrAddDigest(nonce, digest)
        Cache-->>Signer: (sig [65]byte, found bool)
        alt Found in cache
            Signer-->>Scheduler: Success
        else Not found
            Signer-->>Scheduler: ErrWaitForSignature
        end
    end
Loading
sequenceDiagram
    participant Client as EVM Client
    participant Outbound as OutboundData
    participant TSS as Signer (TSS)
    participant Batch as Batch Signing
    
    rect rgb(200, 200, 255)
    note over Client,TSS: Old Flow: Height-based scheduling
    Client->>Outbound: NewOutboundData(ctx, cctx, height, logger)
    Outbound-->>Client: OutboundData with height field
    Client->>TSS: Sign(ctx, data, ..., height)
    TSS->>TSS: Per-nonce TSS keysign
    end
    
    rect rgb(200, 255, 200)
    note over Client,Batch: New Flow: Batch and digest-based
    Client->>Outbound: NewOutboundData(ctx, cctx, logger)
    Outbound-->>Client: OutboundData (height from ObservedExternalHeight)
    Client->>TSS: GetSignatureOrAddDigest(nonce, digest)
    alt Signature cached
        TSS-->>Client: (sig, true)
    else Waiting on batch
        TSS-->>Client: (empty, false)
        Client->>Batch: PrepareForKeysign()
        Batch->>Batch: Accumulate nonces
        Batch->>TSS: SignBatch(digests[])
        TSS->>TSS: Single TSS keysign for batch
        Batch->>Batch: Cache all signatures
    end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas requiring extra attention:

  • Concurrency safeguards in batch accumulation: The new RWMutex usage in Signer and per-nonce map (tssKeysignInfoMap) require careful review to ensure lock ordering and prevent deadlocks during concurrent batch operations.
  • Cantor pairing correctness: The bidirectional mapping (NonceToBatchNumber, BatchNumberToRange) and KeysignHeight computation using Cantor pairing must be validated for round-trip correctness and absence of collisions.
  • Scheduler control flow changes: The replacement of tracker-based nonce heuristics with NextTSSNonce and introduction of stale-block-event detection fundamentally alters CCTX processing order and timing. Verify this does not break existing ordering guarantees or introduce race conditions.
  • Asynchronous signature awaiting: The new ErrWaitForSignature signal and GetSignatureOrAddDigest flow introduce eventual-consistency semantics. Verify retry logic, backpressure handling, and that outbounds are not silently dropped or duplicated.
  • Test infrastructure alignment: Digest-based mocking across multiple signing paths (sign_test.go, signer_admin_test.go, signer_test.go) must be consistent to avoid false negatives masking real signing failures.
  • Interface compliance: New interface methods (IsSignatureCached, GetBlockHeight) added to TSSClient and ZetacoreReaderClient require verification that all implementations (service, mocks, chaos, dry) are correctly updated.

Possibly related PRs

Suggested reviewers

  • skosito
  • lumtis
  • brewmaster012
  • kingpinXD

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.41% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main architectural change: implementing a sequential batch TSS keysign scheduler for EVM chains, which is the primary objective of the changeset.
Description check ✅ Passed The description is comprehensive and covers the four key architectural changes, performance improvements, test results, and linked issue. However, testing coverage is incomplete: only localnet testing is checked while Go unit/integration tests and GitHub Actions are unchecked.
Linked Issues check ✅ Passed The changeset directly addresses issue #4436 by implementing batched keysign scheduling and deterministic height computation to reduce excessive TSS requests and improve throughput consistency.
Out of Scope Changes check ✅ Passed All changes remain within scope: EVM batch keysign implementation, supporting utilities (Cantor pairing, batch info structures), interface extensions (IsSignatureCached, GetBlockHeight), and related test updates. The changelog entry is documentation.

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.

@github-actions
Copy link

github-actions bot commented Nov 12, 2025

!!!WARNING!!!
nosec detected in the following files: pkg/math/pairing.go, zetaclient/chains/base/signer_batch_info.go, zetaclient/chains/base/signer_batch_info_test.go, zetaclient/chains/base/signer_batch_sign.go, zetaclient/chains/base/signer_batch_sign_test.go, zetaclient/tss/service.go

Be very careful about using #nosec in code. It can be a quick way to suppress security warnings and move forward with development, it should be employed with caution. Suppressing warnings with #nosec can hide potentially serious vulnerabilities. Only use #nosec when you're absolutely certain that the security issue is either a false positive or has been mitigated in another way.

Only suppress a single rule (or a specific set of rules) within a section of code, while continuing to scan for other problems. To do this, you can list the rule(s) to be suppressed within the #nosec annotation, e.g: /* #nosec G401 */ or //#nosec G201 G202 G203
Broad #nosec annotations should be avoided, as they can hide other vulnerabilities. The CI will block you from merging this PR until you remove #nosec annotations that do not target specific rules.

Pay extra attention to the way #nosec is being used in the files listed above.

@github-actions github-actions bot added the nosec label Nov 12, 2025
@ws4charlie ws4charlie changed the title feat(WIP): initial PoC of batch keysign for eth chain feat(WIP): initial PoC of batch keysign for EVM chain Nov 12, 2025
@ws4charlie ws4charlie changed the title feat(WIP): initial PoC of batch keysign for EVM chain feat: batch and sequential keysign for EVM chain Nov 21, 2025
@ws4charlie ws4charlie added zetaclient Issues related to ZetaClient CONSENSUS_BREAKING_ACK Acknowledge a consensus breaking change chain:evm performance labels Nov 21, 2025
@codecov
Copy link

codecov bot commented Nov 22, 2025

Codecov Report

❌ Patch coverage is 70.96774% with 108 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.94%. Comparing base (737e30d) to head (df601e2).

Files with missing lines Patch % Lines
zetaclient/chains/evm/evm.go 28.30% 33 Missing and 5 partials ⚠️
zetaclient/chains/evm/signer/signer.go 24.00% 17 Missing and 2 partials ⚠️
zetaclient/chains/base/signer_batch_sign.go 92.39% 8 Missing and 5 partials ⚠️
zetaclient/chains/base/signer.go 10.00% 9 Missing ⚠️
zetaclient/chains/evm/signer/v2_signer.go 0.00% 8 Missing ⚠️
zetaclient/mode/chaos/generated.go 0.00% 8 Missing ⚠️
zetaclient/chains/evm/signer/v2_sign.go 0.00% 4 Missing ⚠️
zetaclient/chains/base/signer_batch_info.go 93.61% 1 Missing and 2 partials ⚠️
zetaclient/chains/evm/signer/sign.go 75.00% 2 Missing ⚠️
zetaclient/chains/zrepo/zrepo.go 0.00% 2 Missing ⚠️
... and 1 more
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #4427      +/-   ##
===========================================
+ Coverage    64.79%   64.94%   +0.15%     
===========================================
  Files          469      472       +3     
  Lines        28547    28781     +234     
===========================================
+ Hits         18497    18692     +195     
- Misses        9029     9061      +32     
- Partials      1021     1028       +7     
Files with missing lines Coverage Δ
pkg/math/pairing.go 100.00% <100.00%> (ø)
pkg/scheduler/context.go 22.72% <100.00%> (ø)
pkg/scheduler/tickers.go 81.96% <100.00%> (ø)
zetaclient/chains/evm/observer/outbound.go 63.28% <ø> (ø)
zetaclient/chains/evm/signer/outbound_data.go 66.01% <100.00%> (-0.33%) ⬇️
zetaclient/chains/evm/signer/signer_admin.go 83.69% <100.00%> (-1.31%) ⬇️
zetaclient/metrics/metrics.go 68.08% <ø> (ø)
zetaclient/tss/service.go 50.26% <100.00%> (+1.93%) ⬆️
zetaclient/chains/evm/signer/sign.go 65.04% <75.00%> (-0.41%) ⬇️
zetaclient/chains/zrepo/zrepo.go 42.01% <0.00%> (-0.51%) ⬇️
... and 9 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ws4charlie ws4charlie changed the title feat: batch and sequential keysign for EVM chain feat: a sequential batch TSS keysign scheduler for EVM chain Nov 22, 2025
@ws4charlie ws4charlie marked this pull request as ready for review November 22, 2025 03:21
@ws4charlie ws4charlie requested a review from a team as a code owner November 22, 2025 03:21
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
zetaclient/chains/evm/signer/signer_admin_test.go (1)

209-225: Fix negative test to actually use the modified nonce

In signMigrateERC20CustodyFundsCmd - should fail if keysign fails, txDataOther is created and its nonce incremented, but the call still passes txData. As written, the test will not exercise the cache‑miss path you intend and may spuriously pass depending on prior state.

Consider updating the call to use txDataOther:

-		// Call signWhitelistERC20Cmd
-		tx, err := evmSigner.signMigrateERC20CustodyFundsCmd(ctx, txData, params)
+		// Call signMigrateERC20CustodyFundsCmd with modified nonce (no cached signature)
+		tx, err := evmSigner.signMigrateERC20CustodyFundsCmd(ctx, &txDataOther, params)

This matches the pattern used in the other keysign fails subtests.

🧹 Nitpick comments (12)
zetaclient/chains/zrepo/zrepo.go (1)

68-70: Add godoc comment for public method.

The implementation is clean and follows the delegation pattern correctly. However, public methods should include godoc comments per Go conventions.

+// GetBlockHeight returns the current block height from the underlying zetacore client.
 func (repo *ZetaRepo) GetBlockHeight(ctx context.Context) (int64, error) {
 	return repo.client.GetBlockHeight(ctx)
 }
zetaclient/chains/zrepo/client.go (1)

34-34: Add godoc comment for interface method.

The method signature is clean and appropriate. However, exported interface methods should include godoc comments per Go conventions to describe their purpose and behavior.

+	// GetBlockHeight returns the current block height from zetacore.
 	GetBlockHeight(context.Context) (int64, error)
pkg/math/pairing_test.go (1)

9-35: Consider adding edge case tests.

The test coverage is good and includes boundary cases with MaxPairValue. However, consider adding tests for additional edge cases to ensure robustness:

 		{name: "test 4", x: 925478314, y: 91456237, z: 517077941108709313},
 		{name: "test 5", x: MaxPairValue, y: MaxPairValue, z: 9223372032559808512},
+		{name: "test 6 - zeros", x: 0, y: 0, z: 0},
+		{name: "test 7 - boundary x", x: MaxPairValue, y: 0, z: 4611686017353646080},
+		{name: "test 8 - boundary y", x: 0, y: MaxPairValue, z: 4611686018427387904},
 	}

These cases verify:

  • Minimum value handling (0, 0)
  • Asymmetric boundary behavior when one coordinate is MaxPairValue and the other is 0
zetaclient/dry/dry.go (1)

119-121: Consider returning false instead of panicking for this read-only query.

IsSignatureCached is a read-only method that queries signature cache state without triggering signing or mutating external state. In dry mode, it would be more ergonomic to return false (indicating no cached signature) rather than panicking, allowing dry-mode code paths to execute further without special handling.

Compare with other dry-mode clients where non-mutating getters (e.g., PubKey() at line 106) return valid values rather than panicking.

Apply this diff if you wish to make dry-mode code more permissive for read-only queries:

-func (*TSSClient) IsSignatureCached(int64, [][]byte) bool {
-	panic(MsgUnreachable)
-}
+func (*TSSClient) IsSignatureCached(int64, [][]byte) bool {
+	return false
+}
pkg/math/pairing.go (1)

22-46: Document input constraints or add defensive validation for CantorUnpair.

Lines 45-46 cast big.Int to uint32 with a #nosec directive, assuming the result is always in range. While this is safe when z was produced by CantorPair(x, y) with x, y ≤ MaxPairValue, there's no runtime validation to ensure the invariant holds.

Since the comment at line 21 indicates this function is "currently only used for unit tests," the risk is low. However, for production-grade code, consider either:

  1. Adding a defensive check before the cast:

    if x.Uint64() > math.MaxUint32 || y.Uint64() > math.MaxUint32 {
        panic("CantorUnpair: input out of valid range")
    }
  2. Strengthening the documentation to clearly state preconditions:

    // CantorUnpair inverts CantorPair. 
    // Precondition: z must be a value previously produced by CantorPair(x, y) 
    // where x, y ≤ MaxPairValue. Violating this precondition results in undefined behavior.
zetaclient/chains/evm/signer/signer_test.go (2)

141-260: Sign tests now accurately cover available vs. missing signature flows*

The refactored tests for TryProcessOutbound, BroadcastOutbound, and the various Sign* methods now:

  • Use NewOutboundData(ctx, cctx, logger) to build realistic txData.
  • Drive the happy path by pre-populating the signer’s cache with a matching digest/signature.
  • Exercise the ErrWaitForSignature path via a modified nonce that changes the digest.

This closely matches the new digest-based, per-nonce signing model and gives solid coverage of both success and “no signature yet” behaviors.


263-376: Digest helpers and mockSignature align well with batch TSS signing, but mirror production logic

The helper functions that reconstruct the digests and mockSignature nicely simulate the real batch TSS flow by:

  • Building transactions with the same parameters used in production (chain ID, target contract, receiver, amount, gas, nonce).
  • Hashing via the EVM client signer and feeding digests into the signer's cache and a synthetic TSSKeysignBatch.
  • Calling SignBatch and then AddBatchSignatures to populate signatures before tests invoke Sign*.

Two minor considerations:

  • Because these helpers duplicate the production digest construction, any future change in contract ABI or tx shape will require updates in both places; if this starts to drift, consider centralizing digest construction behind a shared helper used by both code and tests.
  • Ensure that the ABI method selectors ("withdraw", "onReceive", "onRevert") and parameter ordering exactly match the protocol-contracts definitions to avoid brittle tests tightly bound to a specific contract version.

Overall, the approach is sound and provides realistic test wiring into the batch-signing pipeline.

zetaclient/chains/base/signer.go (1)

36-45: Signer TSS nonce tracking is correct; consider lowering log verbosity

The new tssKeysignInfoMap and nextTSSNonce fields are properly initialized in NewSigner, and SetNextTSSNonce:

  • Uses the signer mutex, so updates are thread-safe.
  • Enforces a strictly increasing nextTSSNonce, which matches expected TSS nonce semantics.
  • Updates the NextTSSNonce gauge with a per-chain label, keeping metrics in sync.

Given that SetNextTSSNonce may be called frequently under load, logging every update at Info could become noisy in production. You may wish to reduce this to Debug (similar to MarkOutbound) or gate it behind a sampled/log-on-change pattern if logs become too chatty.

Also applies to: 63-74, 197-207

zetaclient/chains/evm/evm.go (1)

197-271: Sequential batch keysign loop is well structured; consider guarding interval misconfig

The scheduleKeysign logic ties together:

  • Stale block skipping via IsStaleBlockEvent.
  • A single nextTSSNonce as the deterministic starting point.
  • PrepareForKeysign to gate on OutboundScheduleInterval and pending nonces.
  • Sequential batch signing where:
    • Only unsigned batches are sent to SignBatch.
    • Advancement to the next batch occurs only when the current batch hits IsEnd().

One minor robustness improvement: PrepareForKeysign performs zetaHeight % scheduleInterval, assuming scheduleInterval > 0. If chain params are ever misconfigured to 0, this will panic. A small defensive check like:

if scheduleInterval <= 0 {
	return false, fmt.Errorf("invalid outbound schedule interval: %d", scheduleInterval)
}

would make failures explicit and easier to diagnose.

zetaclient/chains/evm/signer/signer.go (1)

176-211: Digest‑cache–driven Sign flow and ErrWaitForSignature handling are sound

Sign now:

  • Builds the tx as before.
  • Computes the digest via Signer().Hash(tx).
  • Uses GetSignatureOrAddDigest to either:
    • Return a cached signature, or
    • Register the digest and return ErrWaitForSignature.

TryProcessOutbound special‑cases ErrWaitForSignature by returning silently, allowing the scheduler to retry once SignBatch has populated signatures. This is a clean separation between digest registration, TSS batch signing, and broadcast, and avoids noisy logging on the expected “first attempt” miss.

It may be worth adding a very low‑level debug log on the ErrWaitForSignature path if you later need to trace digest registration behavior under load.

Also applies to: 338-352

zetaclient/chains/base/signer_batch_sign.go (2)

49-88: PrepareForKeysign logic is sound; guard against zero interval configuration

PrepareForKeysign correctly:

  • Triggers only on heights that are multiples of scheduleInterval.
  • Fetches pending nonces, prunes stale keysign info via removeKeysignInfo(nonceLow).
  • Short‑circuits when there are no pending CCTXs (nonceLow >= nonceHigh) or the TSS nonce is already at/above the pending high watermark.

One robustness improvement would be to explicitly handle scheduleInterval <= 0 before doing zetaHeight % scheduleInterval, returning an error instead of panicking if chain params are misconfigured.


90-120: Batch readiness and collection enforce the intended no‑gaps invariant

The combination of:

  • isBatchReadyToSign, which checks overlap between [NonceLow, NonceHigh) and the batch’s nonce range, and
  • collectKeysignBatch, which:
    • Sorts all known nonces,
    • Collects digests within the batch’s range,
    • Rejects empty, non‑sequential batches or those missing untilNonce,

ensures that batches are only signed when there is a contiguous span of digests up to the required nonce.

GetKeysignBatch’s use of retry.DoWithBackoff to wait briefly for missing digests is also a pragmatic trade‑off between responsiveness and avoiding partial batches.

You might consider differentiating between transient “waiting for digests” errors and unexpected internal errors to avoid retrying the latter, but this is not strictly necessary.

Also applies to: 188-217, 220-260

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4fcff5b and cdf2b23.

📒 Files selected for processing (28)
  • changelog.md (1 hunks)
  • cmd/zetae2e/local/performance.go (1 hunks)
  • pkg/math/pairing.go (1 hunks)
  • pkg/math/pairing_test.go (1 hunks)
  • zetaclient/chains/base/signer.go (6 hunks)
  • zetaclient/chains/base/signer_batch_info.go (1 hunks)
  • zetaclient/chains/base/signer_batch_sign.go (1 hunks)
  • zetaclient/chains/evm/evm.go (5 hunks)
  • zetaclient/chains/evm/observer/outbound.go (1 hunks)
  • zetaclient/chains/evm/signer/outbound_data.go (1 hunks)
  • zetaclient/chains/evm/signer/outbound_data_test.go (1 hunks)
  • zetaclient/chains/evm/signer/sign.go (1 hunks)
  • zetaclient/chains/evm/signer/sign_test.go (12 hunks)
  • zetaclient/chains/evm/signer/signer.go (7 hunks)
  • zetaclient/chains/evm/signer/signer_admin.go (0 hunks)
  • zetaclient/chains/evm/signer/signer_admin_test.go (15 hunks)
  • zetaclient/chains/evm/signer/signer_test.go (3 hunks)
  • zetaclient/chains/evm/signer/v2_sign.go (0 hunks)
  • zetaclient/chains/tssrepo/client.go (1 hunks)
  • zetaclient/chains/zrepo/client.go (1 hunks)
  • zetaclient/chains/zrepo/zrepo.go (1 hunks)
  • zetaclient/dry/dry.go (1 hunks)
  • zetaclient/metrics/metrics.go (1 hunks)
  • zetaclient/mode/chaos/generate/sample.json (1 hunks)
  • zetaclient/mode/chaos/generated.go (2 hunks)
  • zetaclient/testutils/mocks/tss.go (1 hunks)
  • zetaclient/testutils/mocks/zetacore.go (2 hunks)
  • zetaclient/tss/service.go (1 hunks)
💤 Files with no reviewable changes (2)
  • zetaclient/chains/evm/signer/v2_sign.go
  • zetaclient/chains/evm/signer/signer_admin.go
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

⚙️ CodeRabbit configuration file

Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.

Files:

  • zetaclient/chains/evm/signer/outbound_data.go
  • zetaclient/dry/dry.go
  • zetaclient/chains/tssrepo/client.go
  • cmd/zetae2e/local/performance.go
  • zetaclient/tss/service.go
  • zetaclient/mode/chaos/generated.go
  • zetaclient/chains/zrepo/zrepo.go
  • zetaclient/chains/evm/signer/signer_test.go
  • zetaclient/chains/zrepo/client.go
  • zetaclient/testutils/mocks/tss.go
  • zetaclient/chains/evm/signer/signer_admin_test.go
  • zetaclient/chains/evm/signer/sign_test.go
  • pkg/math/pairing.go
  • zetaclient/metrics/metrics.go
  • zetaclient/chains/evm/observer/outbound.go
  • zetaclient/testutils/mocks/zetacore.go
  • zetaclient/chains/evm/signer/outbound_data_test.go
  • zetaclient/chains/base/signer.go
  • zetaclient/chains/base/signer_batch_info.go
  • pkg/math/pairing_test.go
  • zetaclient/chains/evm/evm.go
  • zetaclient/chains/evm/signer/signer.go
  • zetaclient/chains/evm/signer/sign.go
  • zetaclient/chains/base/signer_batch_sign.go
🧠 Learnings (33)
📓 Common learnings
Learnt from: gartnera
Repo: zeta-chain/node PR: 3632
File: zetaclient/chains/solana/signer/signer.go:304-304
Timestamp: 2025-03-04T22:39:58.395Z
Learning: The Solana signer implementation in zetaclient/chains/solana/signer/signer.go has limited test coverage, particularly for the transaction broadcasting logic with fallback scenarios. Adding this coverage has been acknowledged as a potential future improvement outside the scope of immediate fixes.
📚 Learning: 2025-09-18T14:13:53.345Z
Learnt from: lumtis
Repo: zeta-chain/node PR: 4200
File: changelog.md:16-16
Timestamp: 2025-09-18T14:13:53.345Z
Learning: The LastBlockHeight state variable in the ZetaChain codebase was never used since the beginning of the project, making its removal a cleanup/refactor rather than a functional breaking change.

Applied to files:

  • zetaclient/mode/chaos/generate/sample.json
  • zetaclient/chains/evm/signer/outbound_data.go
  • zetaclient/chains/zrepo/zrepo.go
  • zetaclient/chains/zrepo/client.go
  • zetaclient/testutils/mocks/zetacore.go
  • zetaclient/chains/evm/signer/sign.go
📚 Learning: 2025-11-09T17:07:25.025Z
Learnt from: kingpinXD
Repo: zeta-chain/node PR: 4419
File: server/testnet.go:288-292
Timestamp: 2025-11-09T17:07:25.025Z
Learning: In the ZetaChain node testnet command (server/testnet.go), when setting StartHeight for validator signing info to `app.LastBlockHeight() - 1`, it's safe to assume LastBlockHeight() >= 1 because the testnet command is designed to fork from existing node data. For fresh initialization with no blocks, users would use the regular `zetacored start` command instead.

Applied to files:

  • zetaclient/mode/chaos/generate/sample.json
  • zetaclient/chains/evm/signer/outbound_data.go
  • zetaclient/mode/chaos/generated.go
  • zetaclient/chains/zrepo/zrepo.go
  • zetaclient/chains/zrepo/client.go
  • zetaclient/testutils/mocks/zetacore.go
  • zetaclient/chains/evm/signer/signer.go
  • zetaclient/chains/evm/signer/sign.go
📚 Learning: 2025-01-14T03:39:29.764Z
Learnt from: ws4charlie
Repo: zeta-chain/node PR: 3306
File: zetaclient/chains/bitcoin/signer/sign.go:225-233
Timestamp: 2025-01-14T03:39:29.764Z
Learning: The signature hash calculation in Bitcoin TSS transactions using `txscript.NewCannedPrevOutputFetcher([]byte{}, 0)` with correct input amounts has been proven to work reliably in production, despite theoretical concerns about empty previous output fetcher.

Applied to files:

  • zetaclient/dry/dry.go
  • zetaclient/chains/evm/signer/signer_test.go
  • zetaclient/chains/evm/signer/signer_admin_test.go
  • zetaclient/chains/evm/signer/sign_test.go
  • zetaclient/chains/evm/signer/signer.go
📚 Learning: 2025-05-30T16:31:30.275Z
Learnt from: skosito
Repo: zeta-chain/node PR: 3939
File: go.mod:52-52
Timestamp: 2025-05-30T16:31:30.275Z
Learning: The ethermint dependency updates in the zeta-chain/node repository are typically moves between feature branches and main branch of the same fork, not breaking API changes. CI status should be verified before assuming compilation issues.

Applied to files:

  • changelog.md
📚 Learning: 2025-02-07T19:33:35.631Z
Learnt from: kingpinXD
Repo: zeta-chain/node PR: 3503
File: e2e/runner/require.go:0-0
Timestamp: 2025-02-07T19:33:35.631Z
Learning: In e2e tests for the zeta-chain/node repository, minimize computation and avoid redundant checks that are already covered by unit tests to ensure faster execution. For example, ballot sorting is verified in unit tests, so e2e tests can safely assume the order.

Applied to files:

  • changelog.md
📚 Learning: 2024-10-31T16:23:17.250Z
Learnt from: gartnera
Repo: zeta-chain/node PR: 3071
File: e2e/e2etests/test_stress_eth_withdraw.go:41-42
Timestamp: 2024-10-31T16:23:17.250Z
Learning: In `e2e/e2etests/test_stress_eth_withdraw.go`, since some goroutines may fail, we should use a mutex to append durations of successful withdrawals for accurate latency reporting.

Applied to files:

  • cmd/zetae2e/local/performance.go
📚 Learning: 2024-10-31T16:21:47.362Z
Learnt from: gartnera
Repo: zeta-chain/node PR: 3071
File: e2e/e2etests/test_stress_eth_withdraw.go:0-0
Timestamp: 2024-10-31T16:21:47.362Z
Learning: In `e2e/e2etests/test_stress_eth_withdraw.go`, when handling the error returned by `stats.Describe` in the `TestStressEtherWithdraw` function, log the error instead of failing the test.

Applied to files:

  • cmd/zetae2e/local/performance.go
📚 Learning: 2024-11-01T17:07:59.584Z
Learnt from: gartnera
Repo: zeta-chain/node PR: 3079
File: e2e/e2etests/test_pause_erc20_custody.go:65-68
Timestamp: 2024-11-01T17:07:59.584Z
Learning: In the file `e2e/e2etests/test_pause_erc20_custody.go`, prefer verbosity and avoid extracting common event handling logic into helper functions to enhance readability and ease future refactoring.

Applied to files:

  • cmd/zetae2e/local/performance.go
  • zetaclient/chains/evm/signer/signer_test.go
  • zetaclient/chains/evm/signer/signer_admin_test.go
  • zetaclient/chains/evm/signer/sign_test.go
📚 Learning: 2025-08-06T01:54:04.100Z
Learnt from: kingpinXD
Repo: zeta-chain/node PR: 4064
File: cmd/zetae2e/local/solana.go:0-0
Timestamp: 2025-08-06T01:54:04.100Z
Learning: The `CheckSolanaTSSBalance()` method in e2e/runner has been refactored to not return an error. Instead, it uses internal assertions (require statements) to fail the test immediately if balance checks fail. This is part of a broader refactoring pattern in the E2E runner where balance check methods were changed from error-returning to assertion-based approaches.

Applied to files:

  • cmd/zetae2e/local/performance.go
  • zetaclient/chains/evm/signer/signer_test.go
  • zetaclient/chains/evm/signer/sign_test.go
📚 Learning: 2025-07-28T18:08:13.883Z
Learnt from: ws4charlie
Repo: zeta-chain/node PR: 4053
File: e2e/e2etests/test_bitcoin_std_deposit.go:42-42
Timestamp: 2025-07-28T18:08:13.883Z
Learning: In Bitcoin deposit E2E tests for the ZetaChain project, `DepositBTCWithAmount` is preferred over `DepositBTCWithExactAmount` because depositing exact amounts to a ZEVM receiver is complex. The tests use rough amounts and then calculate the actual received amount from the raw Bitcoin transaction to verify balance changes, making the tests more robust and less flaky.

Applied to files:

  • cmd/zetae2e/local/performance.go
📚 Learning: 2024-07-04T23:46:38.428Z
Learnt from: ws4charlie
Repo: zeta-chain/node PR: 2411
File: zetaclient/orchestrator/orchestrator.go:192-217
Timestamp: 2024-07-04T23:46:38.428Z
Learning: The `GetUpdatedSigner` method in `zetaclient/orchestrator/orchestrator.go` is covered by unit tests in `zetaclient/orchestrator/chain_activate_test.go` and `zetaclient/orchestrator/orchestrator_test.go`.

Applied to files:

  • zetaclient/tss/service.go
  • zetaclient/mode/chaos/generated.go
  • zetaclient/chains/evm/signer/signer_test.go
  • zetaclient/chains/evm/signer/signer_admin_test.go
  • zetaclient/chains/evm/signer/sign_test.go
  • zetaclient/chains/evm/signer/outbound_data_test.go
  • zetaclient/chains/base/signer.go
  • zetaclient/chains/evm/signer/signer.go
  • zetaclient/chains/evm/signer/sign.go
  • zetaclient/chains/base/signer_batch_sign.go
📚 Learning: 2024-10-31T16:19:26.038Z
Learnt from: gartnera
Repo: zeta-chain/node PR: 3071
File: e2e/e2etests/test_stress_eth_withdraw.go:58-59
Timestamp: 2024-10-31T16:19:26.038Z
Learning: In the Go code within `e2e/utils/zetacore.go`, the function `WaitCctxMinedByInboundHash` does not return an error.

Applied to files:

  • zetaclient/mode/chaos/generated.go
📚 Learning: 2025-01-22T22:46:58.820Z
Learnt from: gartnera
Repo: zeta-chain/node PR: 3395
File: .github/workflows/reusable-sim.yml:29-30
Timestamp: 2025-01-22T22:46:58.820Z
Learning: The zeta-chain/node repository uses Go version >= 1.22. Do not suggest downgrading to earlier versions like 1.21.

Applied to files:

  • zetaclient/chains/zrepo/zrepo.go
📚 Learning: 2025-03-04T22:39:58.395Z
Learnt from: gartnera
Repo: zeta-chain/node PR: 3632
File: zetaclient/chains/solana/signer/signer.go:304-304
Timestamp: 2025-03-04T22:39:58.395Z
Learning: The Solana signer implementation in zetaclient/chains/solana/signer/signer.go has limited test coverage, particularly for the transaction broadcasting logic with fallback scenarios. Adding this coverage has been acknowledged as a potential future improvement outside the scope of immediate fixes.

Applied to files:

  • zetaclient/chains/evm/signer/signer_test.go
  • zetaclient/chains/evm/signer/signer_admin_test.go
  • zetaclient/chains/evm/signer/sign_test.go
  • zetaclient/chains/evm/signer/outbound_data_test.go
  • zetaclient/chains/base/signer.go
  • zetaclient/chains/base/signer_batch_info.go
  • zetaclient/chains/evm/evm.go
  • zetaclient/chains/evm/signer/signer.go
  • zetaclient/chains/evm/signer/sign.go
  • zetaclient/chains/base/signer_batch_sign.go
📚 Learning: 2024-07-05T00:02:31.446Z
Learnt from: ws4charlie
Repo: zeta-chain/node PR: 2411
File: zetaclient/orchestrator/chain_activate.go:184-247
Timestamp: 2024-07-05T00:02:31.446Z
Learning: The `CreateSignerObserverBTC` function in `zetaclient/orchestrator/chain_activate.go` is covered by unit tests in `zetaclient/orchestrator/chain_activate_test.go`.

Applied to files:

  • zetaclient/chains/evm/signer/signer_test.go
  • zetaclient/chains/evm/signer/signer_admin_test.go
  • zetaclient/chains/evm/signer/sign_test.go
  • zetaclient/chains/evm/signer/outbound_data_test.go
  • zetaclient/chains/base/signer.go
  • zetaclient/chains/base/signer_batch_info.go
  • zetaclient/chains/evm/signer/signer.go
  • zetaclient/chains/evm/signer/sign.go
  • zetaclient/chains/base/signer_batch_sign.go
📚 Learning: 2024-07-05T00:02:36.493Z
Learnt from: ws4charlie
Repo: zeta-chain/node PR: 2411
File: zetaclient/orchestrator/chain_activate.go:116-181
Timestamp: 2024-07-05T00:02:36.493Z
Learning: The `CreateSignerObserverEVM` function in `zetaclient/orchestrator/chain_activate.go` is covered by unit tests in `zetaclient/orchestrator/chain_activate_test.go`.

Applied to files:

  • zetaclient/chains/evm/signer/signer_test.go
  • zetaclient/chains/evm/signer/signer_admin_test.go
  • zetaclient/chains/evm/signer/sign_test.go
  • zetaclient/chains/evm/observer/outbound.go
  • zetaclient/chains/evm/signer/outbound_data_test.go
  • zetaclient/chains/base/signer.go
  • zetaclient/chains/evm/evm.go
  • zetaclient/chains/evm/signer/signer.go
  • zetaclient/chains/evm/signer/sign.go
  • zetaclient/chains/base/signer_batch_sign.go
📚 Learning: 2024-10-08T15:34:48.217Z
Learnt from: ws4charlie
Repo: zeta-chain/node PR: 2907
File: zetaclient/chains/bitcoin/observer/outbound_test.go:81-82
Timestamp: 2024-10-08T15:34:48.217Z
Learning: In the `mineTxNSetNonceMark` function within `bitcoin/observer/outbound_test.go`, it's acceptable to hardcode the chain ID, as the tests do not require varying chain IDs.

Applied to files:

  • zetaclient/chains/evm/signer/signer_test.go
  • zetaclient/chains/evm/signer/sign_test.go
  • zetaclient/chains/evm/observer/outbound.go
  • zetaclient/chains/evm/signer/outbound_data_test.go
  • zetaclient/chains/base/signer.go
📚 Learning: 2025-01-13T23:23:35.853Z
Learnt from: ws4charlie
Repo: zeta-chain/node PR: 3306
File: zetaclient/chains/bitcoin/signer/sign.go:155-211
Timestamp: 2025-01-13T23:23:35.853Z
Learning: Amount validation for Bitcoin outbound transactions, including checks for gas price, recipient address, and dust threshold, is performed in NewOutboundData before the transaction signing process begins.

Applied to files:

  • zetaclient/chains/evm/signer/signer_test.go
  • zetaclient/chains/evm/signer/signer_admin_test.go
  • zetaclient/chains/evm/signer/sign_test.go
  • zetaclient/chains/evm/signer/outbound_data_test.go
📚 Learning: 2025-01-13T23:23:35.853Z
Learnt from: ws4charlie
Repo: zeta-chain/node PR: 3306
File: zetaclient/chains/bitcoin/signer/sign.go:155-211
Timestamp: 2025-01-13T23:23:35.853Z
Learning: Bitcoin outbound transaction validation in NewOutboundData includes checks for:
1. Valid CCTX
2. Gas token only
3. Non-negative fee rate
4. Valid receiver address format and type
5. Dust amount threshold
6. Compliance requirements

Applied to files:

  • zetaclient/chains/evm/signer/signer_test.go
  • zetaclient/chains/evm/signer/sign_test.go
  • zetaclient/chains/evm/signer/outbound_data_test.go
  • zetaclient/chains/evm/signer/signer.go
📚 Learning: 2025-02-06T16:29:58.925Z
Learnt from: gartnera
Repo: zeta-chain/node PR: 3485
File: e2e/utils/sui/signer.go:24-35
Timestamp: 2025-02-06T16:29:58.925Z
Learning: The Sui blockchain requires signatures in [R || S || V] format, which is best generated using go-ethereum's crypto.Sign function that takes an ecdsa.PrivateKey.

Applied to files:

  • zetaclient/testutils/mocks/tss.go
  • zetaclient/chains/evm/signer/signer_admin_test.go
  • zetaclient/chains/evm/signer/sign_test.go
  • zetaclient/chains/evm/signer/signer.go
📚 Learning: 2025-09-15T13:42:17.594Z
Learnt from: lumtis
Repo: zeta-chain/node PR: 4199
File: zetaclient/chains/evm/signer/signer_admin.go:25-26
Timestamp: 2025-09-15T13:42:17.594Z
Learning: In PR 4199, the CLI references to CmdMigrateTssFunds in x/crosschain/client/cli/ files are intentionally not updated as they are out of scope for this specific refactor focused on removing ERC20 custody messages.

Applied to files:

  • zetaclient/chains/evm/signer/signer_admin_test.go
  • zetaclient/chains/evm/signer/sign.go
📚 Learning: 2024-11-06T21:11:34.420Z
Learnt from: gartnera
Repo: zeta-chain/node PR: 3105
File: cmd/zetae2e/local/bitcoin.go:8-8
Timestamp: 2024-11-06T21:11:34.420Z
Learning: In the `e2e` codebase, it's acceptable to use `github.com/stretchr/testify/require` outside of test files.

Applied to files:

  • zetaclient/chains/evm/signer/signer_admin_test.go
📚 Learning: 2024-10-10T18:54:33.554Z
Learnt from: ws4charlie
Repo: zeta-chain/node PR: 2987
File: testutil/sample/memo.go:0-0
Timestamp: 2024-10-10T18:54:33.554Z
Learning: In the `testutil/sample/memo.go` file, unchecked type assertions in the `ABIPack` function are acceptable because it is designed for unit tests and simplicity.

Applied to files:

  • zetaclient/chains/evm/signer/signer_admin_test.go
  • pkg/math/pairing_test.go
📚 Learning: 2024-11-06T21:10:14.301Z
Learnt from: gartnera
Repo: zeta-chain/node PR: 3105
File: e2e/runner/setup_bitcoin.go:51-69
Timestamp: 2024-11-06T21:10:14.301Z
Learning: In test code (`e2e/runner/setup_bitcoin.go`), adding security measures for private key handling in the `GetBtcAddress` method is not required.

Applied to files:

  • zetaclient/chains/evm/signer/signer_admin_test.go
📚 Learning: 2024-07-04T23:47:56.072Z
Learnt from: ws4charlie
Repo: zeta-chain/node PR: 2411
File: zetaclient/orchestrator/orchestrator.go:222-237
Timestamp: 2024-07-04T23:47:56.072Z
Learning: The `GetUpdatedChainObserver` method in the `Orchestrator` class is covered by unit tests in `zetaclient/orchestrator/orchestrator_test.go` and `zetaclient/orchestrator/chain_activate_test.go`.

Applied to files:

  • zetaclient/chains/evm/signer/sign_test.go
📚 Learning: 2025-01-21T04:39:26.779Z
Learnt from: ws4charlie
Repo: zeta-chain/node PR: 3381
File: zetaclient/chains/bitcoin/signer/sign.go:71-74
Timestamp: 2025-01-21T04:39:26.779Z
Learning: In Bitcoin ZRC20 token context, `txData.txSize` represents the gas limit and is used to determine if a transaction is a ZRC20 'withdraw' operation that should be charged less fee by comparing it with `common.BtcOutboundBytesWithdrawer`.

Applied to files:

  • zetaclient/chains/evm/signer/sign_test.go
  • zetaclient/chains/evm/signer/sign.go
📚 Learning: 2025-01-14T03:37:18.430Z
Learnt from: ws4charlie
Repo: zeta-chain/node PR: 3306
File: pkg/math/integer_test.go:0-0
Timestamp: 2025-01-14T03:37:18.430Z
Learning: Use big.Int for handling large number calculations and edge cases to avoid integer overflow issues.

Applied to files:

  • pkg/math/pairing.go
📚 Learning: 2025-02-04T06:03:54.382Z
Learnt from: ws4charlie
Repo: zeta-chain/node PR: 3461
File: x/observer/migrations/v10/migrate.go:29-34
Timestamp: 2025-02-04T06:03:54.382Z
Learning: In the observer module's ChainParams struct, the Confirmation field is a value type (not a pointer), so nil checks are not needed.

Applied to files:

  • zetaclient/chains/evm/observer/outbound.go
📚 Learning: 2025-02-04T06:12:41.760Z
Learnt from: ws4charlie
Repo: zeta-chain/node PR: 3461
File: x/observer/types/chain_params.go:59-62
Timestamp: 2025-02-04T06:12:41.760Z
Learning: The legacy `ConfirmationCount` field in the ChainParams struct is planned to be deprecated after a full upgrade to the new confirmation count system that uses SafeInboundCount, FastInboundCount, SafeOutboundCount, and FastOutboundCount fields.

Applied to files:

  • zetaclient/chains/evm/observer/outbound.go
📚 Learning: 2024-08-01T18:08:13.681Z
Learnt from: kingpinXD
Repo: zeta-chain/node PR: 2615
File: x/crosschain/keeper/msg_server_vote_outbound_tx_test.go:472-472
Timestamp: 2024-08-01T18:08:13.681Z
Learning: The `SaveFailedOutbound` function in `x/crosschain/keeper/msg_server_vote_outbound_tx.go` requires a string argument for the error message.

Applied to files:

  • zetaclient/chains/evm/observer/outbound.go
📚 Learning: 2024-10-30T17:56:16.341Z
Learnt from: gartnera
Repo: zeta-chain/node PR: 3070
File: cmd/zetae2e/init.go:0-0
Timestamp: 2024-10-30T17:56:16.341Z
Learning: In code reviews for Go files like `cmd/zetae2e/init.go` in the ZetaChain project, avoid suggesting unrelated refactoring. Focus comments on changes relevant to the PR objectives.

Applied to files:

  • zetaclient/chains/evm/evm.go
  • zetaclient/chains/evm/signer/sign.go
📚 Learning: 2024-10-10T19:29:59.300Z
Learnt from: ws4charlie
Repo: zeta-chain/node PR: 2987
File: pkg/memo/fields_v0_test.go:270-320
Timestamp: 2024-10-10T19:29:59.300Z
Learning: In the `FieldsV0` struct of the `memo` package, `RevertAddress` may be left empty when `CallOnRevert` is set to true.

Applied to files:

  • zetaclient/chains/evm/signer/sign.go
🧬 Code graph analysis (14)
zetaclient/chains/evm/signer/outbound_data.go (1)
testutil/sample/crosschain.go (1)
  • InboundParams (176-191)
zetaclient/dry/dry.go (1)
zetaclient/chains/tssrepo/client.go (1)
  • TSSClient (13-31)
zetaclient/tss/service.go (2)
zetaclient/tss/crypto.go (1)
  • PubKey (28-31)
zetaclient/tss/config.go (1)
  • Version (20-20)
zetaclient/chains/evm/signer/signer_test.go (1)
zetaclient/chains/evm/signer/outbound_data.go (1)
  • NewOutboundData (53-155)
zetaclient/chains/evm/signer/signer_admin_test.go (3)
zetaclient/chains/evm/signer/outbound_data.go (2)
  • NewOutboundData (53-155)
  • OutboundData (22-49)
zetaclient/chains/evm/signer/signer.go (2)
  • Signer (62-76)
  • ErrWaitForSignature (47-47)
testutil/sample/crypto.go (2)
  • EthAddress (89-91)
  • Hash (189-191)
zetaclient/chains/evm/signer/sign_test.go (4)
zetaclient/chains/evm/signer/outbound_data.go (2)
  • NewOutboundData (53-155)
  • OutboundData (22-49)
zetaclient/chains/evm/signer/signer.go (2)
  • Signer (62-76)
  • ErrWaitForSignature (47-47)
zetaclient/chains/base/signer.go (1)
  • Signer (21-47)
zetaclient/chains/base/signer_batch_info.go (2)
  • NewTSSKeysignBatch (48-52)
  • NewTSSKeysignInfo (28-33)
zetaclient/testutils/mocks/zetacore.go (2)
zetaclient/maintenance/client.go (1)
  • ZetacoreClient (12-20)
zetaclient/orchestrator/contextupdater.go (1)
  • ZetacoreClient (18-32)
zetaclient/chains/evm/signer/outbound_data_test.go (1)
zetaclient/chains/evm/signer/outbound_data.go (1)
  • NewOutboundData (53-155)
zetaclient/chains/base/signer.go (2)
zetaclient/chains/base/signer_batch_info.go (1)
  • TSSKeysignInfo (19-25)
zetaclient/metrics/metrics.go (2)
  • NextTSSNonce (83-87)
  • Info (119-123)
zetaclient/chains/base/signer_batch_info.go (1)
pkg/math/pairing.go (2)
  • MaxPairValue (10-10)
  • CantorPair (15-18)
pkg/math/pairing_test.go (1)
pkg/math/pairing.go (3)
  • MaxPairValue (10-10)
  • CantorPair (15-18)
  • CantorUnpair (22-47)
zetaclient/chains/evm/evm.go (3)
pkg/scheduler/opts.go (1)
  • BlockTicker (47-49)
zetaclient/chains/base/signer.go (1)
  • OutboundIDFromCCTX (215-218)
zetaclient/chains/base/signer_batch_info.go (1)
  • NonceToBatchNumber (149-151)
zetaclient/chains/evm/signer/signer.go (3)
zetaclient/chains/base/signer.go (1)
  • Signer (21-47)
zetaclient/metrics/metrics.go (1)
  • NextTSSNonce (83-87)
zetaclient/chains/evm/signer/outbound_data.go (1)
  • NewOutboundData (53-155)
zetaclient/chains/base/signer_batch_sign.go (6)
zetaclient/chains/base/signer.go (1)
  • Signer (21-47)
zetaclient/chains/zrepo/zrepo.go (1)
  • ZetaRepo (44-50)
pkg/scheduler/context.go (1)
  • BlockFromContextWithDelay (27-41)
zetaclient/chains/base/signer_batch_info.go (5)
  • TSSKeysignBatch (36-45)
  • NonceToBatchNumber (149-151)
  • NewTSSKeysignInfo (28-33)
  • BatchNumberToRange (155-157)
  • NewTSSKeysignBatch (48-52)
pkg/retry/retry.go (2)
  • Retry (147-157)
  • DoWithBackoff (62-82)
zetaclient/logs/fields.go (1)
  • FieldNonce (23-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). (10)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: test-sim-import-export / sim
  • GitHub Check: test-sim-nondeterminism / sim
  • GitHub Check: test-sim-after-import / sim
  • GitHub Check: test-sim-fullappsimulation / sim
  • GitHub Check: lint
  • GitHub Check: gosec
  • GitHub Check: build-and-test
  • GitHub Check: build
  • GitHub Check: analyze (go)
🔇 Additional comments (24)
changelog.md (1)

7-8: LGTM!

The changelog entry accurately describes the performance improvement feature introduced in this PR.

cmd/zetae2e/local/performance.go (1)

167-172: LGTM! Correct timing measurement.

Moving the timer start to after the deposit operation ensures that the performance test measures only the withdraw test duration, not the setup overhead. This provides more accurate performance metrics.

zetaclient/testutils/mocks/zetacore.go (1)

171-196: LGTM!

The parameter renaming from ctx to _a0 is a standard mockery convention for generated mock code. No functional changes.

zetaclient/chains/evm/observer/outbound.go (1)

136-136: LGTM! Helpful clarification.

The comment appropriately documents that the continueKeysign flag is no longer used with the batch keysign implementation. This provides valuable context for future maintainers.

zetaclient/mode/chaos/generate/sample.json (1)

96-96: LGTM!

The GetBlockHeight entry is consistent with other chaos mode configuration entries and properly supports the new method added to the ZetacoreClient interface.

zetaclient/chains/tssrepo/client.go (1)

30-30: LGTM!

The interface addition is clean and follows Go conventions. The signature clearly expresses intent: query whether signatures for given digests on a specific chain are already cached.

zetaclient/chains/evm/signer/outbound_data.go (1)

143-143: LGTM! Height sourcing aligns with PR objectives.

Using cctx.InboundParams.ObservedExternalHeight for the outbound data height is semantically appropriate and decouples outbound processing from the real ZetaChain height, consistent with the PR's architectural goal of using deterministic height values.

zetaclient/mode/chaos/generated.go (1)

96-108: Generated code follows established patterns.

This is generated code (as noted in line 1). The new GetBlockHeight and IsSignatureCached wrappers follow the same patterns as other methods in the file, suggesting the generator is functioning correctly.

Also applies to: 1651-1659

zetaclient/chains/evm/signer/sign.go (1)

45-52: LGTM! Height parameter consistently removed across all signing functions.

All seven signing functions have been consistently updated to remove the height parameter from signer.Sign() calls. This mechanical refactoring aligns with the PR's objective to decouple signing from ZetaChain block height and move toward deterministic, nonce-based scheduling.

Also applies to: 91-98, 108-115, 125-132, 160-167, 193-200, 209-216

zetaclient/testutils/mocks/tss.go (1)

113-115: LGTM! Mock implementation returns safe default.

The mock returns false, indicating no cached signature, which is a sensible default for tests. Tests requiring cached signature behavior can use dependency injection or test setup to override this behavior if needed.

zetaclient/metrics/metrics.go (1)

82-87: NextTSSNonce metric definition looks consistent and low-risk

The new NextTSSNonce gauge follows the existing per-chain metric pattern (namespace, label set, help text) and should integrate cleanly with the signer logic using it.

zetaclient/chains/evm/signer/outbound_data_test.go (1)

20-22: Updated NewOutboundData usage aligns with new constructor signature

Switching newOutbound to NewOutboundData(ctx, cctx, logger) correctly matches the updated API and keeps the test semantics intact.

zetaclient/chains/evm/signer/signer_admin_test.go (4)

3-15: Tests correctly exercise digest-based signing flow

Using NewOutboundData with a test logger plus the new digest helpers keeps tests aligned with production construction of admin txs and the new ErrWaitForSignature / cache semantics. This is a solid pattern for the other admin command tests in this file as well.

Also applies to: 26-29


34-37: Consistent pre‑seeding of signatures for happy‑path tests

Precomputing the digest via the helper and then calling mockSignature before invoking the signer ensures tests remain deterministic while still going through the new cache‑first Sign path. This is a clean way to validate each admin command without coupling to internal signer fields.

Also applies to: 59-62, 79-82, 98-101, 130-133, 187-190, 246-249, 265-268, 323-326


152-161: Negative paths for missing signatures validate ErrWaitForSignature

Cloning txData, bumping the nonce, and then calling the command with txDataOther is an effective way to simulate a digest that has not yet been signed and assert ErrWaitForSignature is plumbed correctly. This nicely exercises the digest cache miss path without touching internal state.

Also applies to: 294-305, 339-348


351-436: Digest helpers closely mirror on‑chain encoding

The helpers correctly:

  • Use erc20custody.ERC20CustodyMetaData.GetAbi() and pack the expected function selectors (whitelist, withdraw, pause/unpause).
  • Rebuild txs with toChainID, to, appropriate amount (zero or txData.amount), gas, and nonce.
  • Derive the digest via signer.evmClient.Signer().Hash(tx).Bytes().

This should keep test digests in lock‑step with production behavior as contracts evolve.

zetaclient/chains/evm/evm.go (2)

58-67: Decoupled block subscriptions for CCTX and keysign

Creating separate WatchNewBlocks subscriptions for CCTX scheduling and TSS keysign (cctxBlockChan vs keysignBlockChan) and wiring them via scheduler.BlockTicker cleanly separates the two concerns. This should prevent keysign backpressure from delaying CCTX scheduling and vice versa, while keeping error handling straightforward.

Also applies to: 101-106


121-195: Nonce‑driven CCTX scheduling looks correct and simpler

The new scheduleCCTX flow is easy to reason about:

  • Stale events are filtered via signer.IsStaleBlockEvent, avoiding reprocessing old block notifications.
  • Chain params are refreshed each time, keeping OutboundScheduleLookahead and addresses current.
  • nextTSSNonce from the EVM account is used to:
    • Vote and advance already‑processed CCTXs (TssNonce < nextTSSNonce) via VoteOutboundIfConfirmed.
    • Only spawn TryProcessOutbound for nonces at or beyond the current TSS nonce.
  • The lookahead/outboundScheduleLookBack checks guard against pathological nonce gaps.

This is a solid improvement over height‑based gating.

zetaclient/chains/evm/signer/signer.go (1)

42-48: ErrWaitForSignature and NextTSSNonce integrate cleanly with base signer state

Defining ErrWaitForSignature and using NextTSSNonce to:

  • Query NonceAt on the TSS account, and
  • Feed SetNextTSSNonce for metrics/tracking

provides a clear contract between scheduling and the signer. The shared zeroValue constant is also appropriate for zero‑ETH outbounds as long as it remains treated as immutable throughout the codebase.

Also applies to: 163-174

zetaclient/chains/base/signer_batch_info.go (3)

11-45: Batch metadata and accessors are straightforward and coherent

TSSKeysignInfo and TSSKeysignBatch cleanly capture the minimal state needed for batching:

  • nonceLow/nonceHigh are updated only via AddKeysignInfo.
  • Accessors (BatchNumber, Digests, NonceLow, NonceHigh) keep call sites readable and encapsulate the mapping between nonces and batches.

This provides a solid foundation for the higher‑level batching logic.

Also applies to: 47-73


74-113: Sequentiality, end‑of‑batch detection, and nonce↔batch mapping look correct

  • IsSequential’s len(digests) == nonceHigh - nonceLow + 1 check is a simple and effective way to guard against gaps in the collected digests.
  • IsEnd using BatchNumberToRange correctly determines whether a batch has reached its upper nonce bound.
  • NonceToBatchNumber and BatchNumberToRange implement the intended 0–9, 10–19, ... mapping and align with the comments.

These helpers make the invariants around batching explicit and easy to audit.

Also applies to: 141-157


119-139: Cantor‑based keysign height computation is reasonable; bounds checks are appropriate

Using CantorPair(batchNumber+1, chainID) to derive a unique KeysignHeight per (batch, chain) pair is a neat choice, and the guards against batchNumber >= MaxPairValue and invalid chainID provide a clear failure mode rather than silent overflow.

This should keep TSS requests uniquely identifiable across chains.

zetaclient/chains/base/signer_batch_sign.go (2)

27-47: Stale block detection using live height vs event height is appropriate

IsStaleBlockEvent:

  • Extracts the block event from context.
  • Compares its height with zetaRepo.GetBlockHeight(ctx).

Skipping events where block.Height < zetaHeight is a sensible way to avoid processing backlogged events once the client has caught up, which is important now that keysign is tied to real‑time height.


151-181: No issues identified—code is correct as written.

Verification confirms:

  • Go 1.22.5 in go.mod satisfies the 1.21+ requirement for the min builtin (line 214)
  • RWMutex discipline is correct across all four functions: GetSignatureOrAddDigest and AddBatchSignatures appropriately use exclusive locks, while collectKeysignBatch correctly uses read locks, and removeKeysignInfo correctly uses exclusive locks for deletion

txEtherDeposit := r.DepositEtherDeployer()
r.WaitForMinedCCTX(txEtherDeposit)

r.Logger.Print("🏃 starting Ethereum withdraw performance tests")
Copy link
Contributor Author

@ws4charlie ws4charlie Nov 24, 2025

Choose a reason for hiding this comment

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

This is because the r.WaitForMinedCCTX may take minutes when there is a lot of pending deposits to process during stress test, which leads to inaccurate outbound stress testing time.

Copy link
Member

@lumtis lumtis left a comment

Choose a reason for hiding this comment

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

I don't see any issues in the change, it's typically what will nee to be tested manually on testnet with stress tests

I might think it would be good to track this one as well for a security review.

}

// PrepareForKeysign checks if it's time to sign and clean up stale keysign information.
func (s *Signer) PrepareForKeysign(
Copy link
Contributor

Choose a reason for hiding this comment

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

This can be broken down into two functions

  • Function that only reads and validates
  • Function that writes , which is the s.removeKeysignInfo(nonceLow) section .


// check if it's the time to perform keysign
interval := e.observer.ChainParams().OutboundScheduleInterval
shouldSign, err := e.signer.PrepareForKeysign(ctx, zetaRepo, nextTSSNonce, zetaHeight, interval)
Copy link
Contributor

Choose a reason for hiding this comment

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

We can add Pending Nonces as an argument to this function and KeySignBatch

slices.Sort(allNonces)

// collect digests within the batch's range
for _, nonce := range allNonces {
Copy link
Contributor

Choose a reason for hiding this comment

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

can we loop batch low to batch high , and pick the nonces from the map ?

}

// AddKeysignInfo adds one TSS keysign information to the batch.
func (b *TSSKeysignBatch) AddKeysignInfo(nonce uint64, info TSSKeysignInfo) {
Copy link
Contributor

@kingpinXD kingpinXD Nov 28, 2025

Choose a reason for hiding this comment

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

nit : this seems to be called by nonces in acesding order , maybe we can just set to high directly for every call.

But this is fine too , and a bit more robust , if we need to call it from other locations in the future

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking:cli chain:evm CONSENSUS_BREAKING_ACK Acknowledge a consensus breaking change nosec performance zetaclient Issues related to ZetaClient

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Eth withdraw stress performance instability (13 mins variance)

4 participants