Skip to content

feat(watch): implement has_certificate pattern matching#994

Merged
scarmuega merged 1 commit into
txpipe:mainfrom
acedward:feat/watch-has-certificate
May 12, 2026
Merged

feat(watch): implement has_certificate pattern matching#994
scarmuega merged 1 commit into
txpipe:mainfrom
acedward:feat/watch-has-certificate

Conversation

@acedward
Copy link
Copy Markdown
Contributor

@acedward acedward commented May 7, 2026

Summary

Implements has_certificate filtering in the watch module's TxPattern, as defined in the UTxO RPC spec. This enables clients to subscribe to transactions containing specific certificate types via the WatchTx gRPC stream.

Both v1alpha and v1beta watch services are updated.

Certificate pattern types

Pattern What it matches Fields
StakeRegistration Registration certs stake_credential
StakeDeregistration Deregistration certs stake_credential
StakeDelegation Delegation certs stake_credential + pool_keyhash
PoolRegistration Pool reg certs operator + pool_keyhash
PoolRetirement Pool retirement certs pool_keyhash + epoch
AnyStakeCredential Any cert involving a credential raw hash bytes
AnyPoolKeyhash Any cert involving a pool raw hash bytes
AnyDrep Any cert involving a DRep raw hash bytes

All pattern fields use AND logic. Empty/None fields match everything (vacuous truth), consistent with existing watch predicates (has_address, moves_asset, etc.).

Example: watch delegations to a specific pool

grpcurl -plaintext \
  -d '{
    "predicate": {
      "match": {
        "cardano": {
          "has_certificate": {
            "stake_delegation": {
              "pool_keyhash": "<base64-encoded-28-byte-pool-hash>"
            }
          }
        }
      }
    }
  }' \
  localhost:50051 \
  utxorpc.v1beta.watch.WatchService.WatchTx

Test plan

  • 10 unit tests using real mainnet delegation TX CBOR (9f8de766...ec465), decoded through the full pallas mapper pipeline
  • Tests cover: specific pool match, specific wallet match, combined match, wrong pool rejection, stake registration, wildcard matchers (AnyPoolKeyhash, AnyStakeCredential), empty pattern, and full TxPattern integration
  • cargo test -p dolos -- watch::tests — all pass
  • cargo check clean
  • Manual test with a synced dolos node using grpcurl against WatchTx

Summary by CodeRabbit

  • New Features
    • Added certificate-based filtering to transaction pattern matching, allowing users to filter transactions by certificate type, stake credentials, pool information, and delegation details.

Review Change Stack

Add certificate-based filtering to the watch module, enabling clients
to monitor transactions containing specific certificate types (stake
registration, delegation, pool registration, DRep, etc).

Includes tests using real mainnet delegation tx CBOR data.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

📝 Walkthrough

Walkthrough

This pull request adds certificate-based filtering to Cardano transaction pattern matching in both v1alpha and v1beta gRPC API versions. Helper functions compare stake credentials, pool keyhashes, and DRep hashes against certificate data. A pattern matcher dispatches by certificate type and handles wildcard cases. The new has_certificate predicate is integrated into the main transaction matching logic. v1beta includes comprehensive unit tests validating the behavior against real mainnet transactions.

Changes

Certificate-Based Transaction Pattern Matching

Layer / File(s) Summary
v1alpha: Credential and Certificate Involvement Helpers
src/serve/grpc/v1alpha/watch.rs
Introduces helper functions to compare stake credential hashes, pool keyhashes, and DRep hashes across stake registration/deregistration/delegation, pool registration/retirement, and vote/reg-delegation certificate variants.
v1alpha: Certificate Pattern Matching Dispatcher
src/serve/grpc/v1alpha/watch.rs
Implements matches_certificate_pattern to evaluate a CertificatePattern against transaction certificates, dispatching by certificate type and handling match-all and "any-*" pattern cases.
v1alpha: TxPattern Integration
src/serve/grpc/v1alpha/watch.rs
Updates matches_cardano_pattern to compute has_certificate_match and require it in the overall conjunction alongside existing address/consume/mint/move/produce predicates.
v1beta: Credential and Certificate Involvement Helpers
src/serve/grpc/v1beta/watch.rs
Introduces helper functions to compare credentials and implements matches_certificate_pattern to dispatch by certificate type against transaction certificates.
v1beta: TxPattern Integration
src/serve/grpc/v1beta/watch.rs
Updates matches_cardano_pattern to evaluate has_certificate and require it alongside existing predicates.
v1beta: Certificate Pattern Matching Tests
src/serve/grpc/v1beta/watch.rs
Unit tests decode a mainnet transaction and validate certificate-pattern matching for stake credentials, pool operations, DRep certificates, wildcards, and end-to-end integration with TxPattern.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 A certificate in every block, we now inspect with care,
Comparing credentials and keyhashes everywhere,
From stake to DRep, the patterns we match,
Two API versions—each with a perfect catch,
With tests on mainnet, our filtering's fair!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: implementing certificate-based filtering via the has_certificate pattern in the watch module for both v1alpha and v1beta services.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@acedward acedward marked this pull request as ready for review May 11, 2026 20:28
@acedward acedward requested a review from scarmuega as a code owner May 11, 2026 20:28
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/serve/grpc/v1beta/watch.rs (1)

665-691: ⚡ Quick win

Consider one negative-path test for TxPattern.has_certificate.

The current tests all assert matches_cardano_pattern(...) == true once has_certificate is set. A symmetric assertion with a deliberately mismatching CertificatePattern (e.g. StakeDelegation { pool_keyhash: vec![0xaa; 28], .. } against this tx) would lock in the "rejecting" path through matches_cardano_pattern, not just matches_certificate_pattern. It would also surface the PoolRegistration field-collapse behavior flagged in v1alpha if a similar pool-reg fixture is ever added.

♻️ Suggested additional test
#[test]
fn cardano_pattern_rejects_when_certificate_does_not_match() {
    let tx = decoded_tx();
    let tx_pattern = u5c::cardano::TxPattern {
        has_certificate: Some(u5c::cardano::CertificatePattern {
            certificate_type: Some(
                u5c::cardano::certificate_pattern::CertificateType::StakeDelegation(
                    u5c::cardano::StakeDelegationPattern {
                        stake_credential: None,
                        pool_keyhash: vec![0xaa; 28].into(),
                    },
                ),
            ),
        }),
        ..Default::default()
    };
    assert!(!matches_cardano_pattern(&tx_pattern, &tx));
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/serve/grpc/v1beta/watch.rs` around lines 665 - 691, Add a negative-path
unit test that constructs a TxPattern with has_certificate =
Some(CertificatePattern { certificate_type =
Some(StakeDelegation(StakeDelegationPattern { stake_credential: None,
pool_keyhash: vec![0xaa; 28].into() })) }) and asserts that
matches_cardano_pattern(&tx_pattern, &decoded_tx()) is false; place it alongside
the existing tests (e.g. name it
cardano_pattern_rejects_when_certificate_does_not_match) so it exercises
matches_cardano_pattern (not just matches_certificate_pattern) and verifies a
mismatching pool_keyhash is rejected.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/serve/grpc/v1alpha/watch.rs`:
- Around line 254-263: In the CertificateType::PoolRegistration match arm
(matching Cert::PoolRegistration), the pool_keyhash comparison is incorrectly
checking pat.pool_keyhash against reg.operator; update the pool_match logic so
pat.pool_keyhash is compared to reg.pool_keyhash (i.e., set pool_match =
pat.pool_keyhash.is_empty() || pat.pool_keyhash == reg.pool_keyhash) while
keeping the existing operator_match check and the final operator_match &&
pool_match return.

---

Nitpick comments:
In `@src/serve/grpc/v1beta/watch.rs`:
- Around line 665-691: Add a negative-path unit test that constructs a TxPattern
with has_certificate = Some(CertificatePattern { certificate_type =
Some(StakeDelegation(StakeDelegationPattern { stake_credential: None,
pool_keyhash: vec![0xaa; 28].into() })) }) and asserts that
matches_cardano_pattern(&tx_pattern, &decoded_tx()) is false; place it alongside
the existing tests (e.g. name it
cardano_pattern_rejects_when_certificate_does_not_match) so it exercises
matches_cardano_pattern (not just matches_certificate_pattern) and verifies a
mismatching pool_keyhash is rejected.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4d9886be-da4d-4550-be23-4de599fb6747

📥 Commits

Reviewing files that changed from the base of the PR and between d1d2f35 and 39b060d.

📒 Files selected for processing (2)
  • src/serve/grpc/v1alpha/watch.rs
  • src/serve/grpc/v1beta/watch.rs

Comment thread src/serve/grpc/v1alpha/watch.rs
@scarmuega scarmuega merged commit 8aa2cf7 into txpipe:main May 12, 2026
13 of 16 checks passed
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.

2 participants