Skip to content

refactor(filter): rename chainsync to cardano#575

Merged
wolf31o2 merged 1 commit intomainfrom
refactor/filter-cardano
Jan 7, 2026
Merged

refactor(filter): rename chainsync to cardano#575
wolf31o2 merged 1 commit intomainfrom
refactor/filter-cardano

Conversation

@wolf31o2
Copy link
Copy Markdown
Member

@wolf31o2 wolf31o2 commented Jan 7, 2026

Summary by cubic

Renamed the chainsync filter to cardano and optimized filtering with O(1) lookups and better hex/bech32 pool ID handling. This makes event filtering faster and clearer across addresses, policies, assets, and pools.

  • Refactors

    • Replaced filter/chainsync with filter/cardano and updated plugin registration/imports.
    • Added filterSet and option helpers that precompute maps for addresses, policies, assets, and pools (including hex↔bech32).
    • Split filtering into small match functions; updated and renamed tests.
  • Migration

    • Plugin name changes from "chainsync" to "cardano"—update any references.
    • CLI flags stay the same (address, asset, policy, pool) and now support comma-separated values; pool IDs work in both hex and bech32.

Written for commit d1f58fe. Summary will update on new commits.

Summary by CodeRabbit

Release Notes

  • New Features

    • New Cardano blockchain filter plugin enabling selective event filtering by payment addresses, stake addresses, asset fingerprints, policy IDs, and pool identifiers
    • Supports comma-separated multi-value inputs for flexible configuration
  • Tests

    • Updated test suite with comprehensive test coverage for the Cardano filter functionality

✏️ Tip: You can customize this high-level summary in your review settings.

Signed-off-by: Chris Gianelloni <wolf31o2@blinklabs.io>
@wolf31o2 wolf31o2 requested a review from a team as a code owner January 7, 2026 22:35
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 7, 2026

📝 Walkthrough

Walkthrough

This change replaces the ChainSync filter plugin with a new Cardano filter plugin. The Cardano filter is implemented with event processing and multi-layered filtering capabilities for block and transaction events, supporting address-based, policy ID, asset fingerprint, and pool ID filtering. The implementation includes lifecycle management (Start/Stop), channel-based I/O, and graceful shutdown handling. The ChainSync plugin and its option builders are completely removed, and the plugin registration is updated to load the new Cardano filter instead.

Possibly related PRs

  • PR #572: Introduces the lifecycle and plugin API pattern (ErrorChan, InputChan, OutputChan, Start/Stop methods) that the new Cardano filter implements.
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 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: systematically renaming the chainsync filter package and all associated types to cardano throughout the codebase.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 9 files

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

Caution

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

⚠️ Outside diff range comments (1)
filter/cardano/cardano_test.go (1)

221-223: Fix "Basic address match" test case to use a real address or certificate.

The mockAddress function always returns an empty common.Address{} regardless of its input parameter. At line 238-243, the "Basic address match" test expects mockAddress("addr_test1qqjwq357") to produce an output matching the filter "addr_test1qqjwq357", but since no certificate is provided, the address string comparison at line 162 of the filter logic will fail (the empty address's String() method won't return "addr_test1qqjwq357").

Either provide a certificate for this test case (like the subsequent tests do) or implement mockAddress to return an address whose String() method returns the expected value.

🤖 Fix all issues with AI agents
In @filter/cardano/cardano.go:
- Around line 284-301: Stop() currently closes inputChan which can panic if
external goroutines send to InputChan() concurrently; remove closing of
c.inputChan and instead set c.inputChan = nil after acquiring stopOnce (close
only doneChan/outputChan/errorChan as before), ensuring the accessor InputChan()
will return nil post-stop; update Stop() to nil out the channel fields
(c.inputChan, c.outputChan, c.errorChan, c.doneChan) after closing the ones you
do close so TestCardano_Stop still observes nil values and avoid closing
inputChan to prevent sender panics.
🧹 Nitpick comments (4)
filter/cardano/cardano_test.go (1)

155-168: Consider starting the filter before stopping in this test.

The test calls Stop() without first calling Start(). While the current implementation handles this gracefully (channels are nil, stopOnce prevents issues), this doesn't test the expected lifecycle. Consider adding a Start() call first to verify proper shutdown behavior.

♻️ Suggested improvement
 func TestCardano_Stop(t *testing.T) {
 	c := New()
+	err := c.Start()
+	if err != nil {
+		t.Fatalf("expected no error on start, got %v", err)
+	}
 	err := c.Stop()
 	if err != nil {
 		t.Fatalf("expected no error, got %v", err)
 	}
filter/cardano/option.go (1)

45-59: Consider logging bech32 decoding failures.

When bech32 decoding fails (lines 49-52), the error is silently ignored. While this may be intentional (gracefully handling invalid input), logging a warning could help users diagnose configuration issues with malformed stake addresses.

♻️ Optional: Add warning for decode failures
 			// Pre-decode the bech32 to get credential hash
 			if _, data, err := bech32.Decode(addr); err == nil {
 				if decoded, err := bech32.ConvertBits(data, 5, 8, false); err == nil {
 					c.filterSet.addresses.stakeCredentialHashes[addr] = decoded
 				}
+			} else if c.logger != nil {
+				c.logger.Warn("failed to decode stake address", "address", addr, "error", err)
 			}
filter/cardano/plugin.go (1)

82-113: Consider trimming whitespace from comma-separated values.

When users provide comma-separated values with spaces (e.g., "addr1, addr2"), the spaces will be included in the parsed values, potentially causing silent filter failures. Consider trimming whitespace.

♻️ Helper function to split and trim
func splitAndTrim(s string) []string {
	parts := strings.Split(s, ",")
	result := make([]string, 0, len(parts))
	for _, p := range parts {
		if trimmed := strings.TrimSpace(p); trimmed != "" {
			result = append(result, trimmed)
		}
	}
	return result
}

Then use splitAndTrim(cmdlineOptions.address) instead of strings.Split(cmdlineOptions.address, ",").

filter/cardano/cardano.go (1)

152-186: Minor: Avoid repeated slice allocation in match functions.

append(te.Outputs, te.ResolvedInputs...) allocates a new slice on each call. Since matchAddressFilter, matchPolicyFilter, and matchAssetFilter all do this, consider iterating both slices separately to avoid allocations in the hot path.

♻️ Alternative: iterate both slices
 func (c *Cardano) matchAddressFilter(te event.TransactionEvent) bool {
-	// Include resolved inputs as outputs for matching
-	allOutputs := append(te.Outputs, te.ResolvedInputs...)
-
-	// Check outputs against payment and stake addresses
-	for _, output := range allOutputs {
+	// Check outputs and resolved inputs against payment and stake addresses
+	for _, outputs := range [][]ledger.TransactionOutput{te.Outputs, te.ResolvedInputs} {
+		for _, output := range outputs {
 		addrStr := output.Address().String()
 		// ... rest of logic
+		}
+	}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2400375 and d1f58fe.

📒 Files selected for processing (9)
  • filter/cardano/cardano.go
  • filter/cardano/cardano_test.go
  • filter/cardano/filter_types.go
  • filter/cardano/option.go
  • filter/cardano/plugin.go
  • filter/cardano/plugin_test.go
  • filter/chainsync/chainsync.go
  • filter/chainsync/option.go
  • filter/filter.go
💤 Files with no reviewable changes (2)
  • filter/chainsync/chainsync.go
  • filter/chainsync/option.go
🧰 Additional context used
🧬 Code graph analysis (5)
filter/cardano/plugin_test.go (1)
plugin/plugin.go (1)
  • Plugin (23-29)
filter/cardano/option.go (2)
filter/cardano/cardano.go (1)
  • Cardano (28-36)
plugin/log.go (1)
  • Logger (4-13)
filter/cardano/cardano.go (4)
plugin/log.go (1)
  • Logger (4-13)
filter/cardano/option.go (1)
  • CardanoOptionFunc (25-25)
event/block.go (1)
  • BlockEvent (28-35)
event/tx.go (1)
  • TransactionEvent (30-44)
filter/cardano/cardano_test.go (3)
filter/cardano/cardano.go (1)
  • New (39-45)
filter/cardano/option.go (1)
  • WithPoolIds (63-100)
event/block.go (1)
  • BlockEvent (28-35)
filter/cardano/plugin.go (3)
plugin/option.go (2)
  • PluginOption (35-43)
  • PluginOptionTypeString (29-29)
filter/cardano/option.go (2)
  • CardanoOptionFunc (25-25)
  • WithLogger (28-32)
internal/logging/logging.go (1)
  • GetLogger (60-65)
⏰ 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). (2)
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: Analyze (go)
🔇 Additional comments (11)
filter/filter.go (1)

18-21: LGTM!

Clean import swap to register the new Cardano filter plugin instead of ChainSync. The side-effect import pattern for auto-registration is preserved.

filter/cardano/plugin_test.go (2)

27-48: LGTM!

Plugin registration test correctly updated to look for the "cardano" plugin name. The test logic is sound.


87-121: LGTM!

The event processing test correctly verifies that events pass through the filter when no filters are configured. The timeout handling is appropriate.

filter/cardano/filter_types.go (1)

17-52: LGTM!

Well-designed internal data structures for O(1) lookups. Using map[string]struct{} for set semantics is idiomatic Go. The pre-computed credential hashes and dual hex/bech32 pool ID representations enable efficient matching without runtime conversions.

filter/cardano/cardano_test.go (1)

426-458: LGTM!

Pool ID filter test correctly updated to use hex format, aligning with the Cardano filter's O(1) lookup implementation. The test verifies that block events are filtered based on IssuerVkey matching the configured pool ID.

filter/cardano/option.go (2)

62-100: LGTM!

Pool ID option correctly handles both hex and bech32 formats, pre-computing both representations for O(1) lookups. The bidirectional mapping enables efficient matching regardless of the format used in events.


102-130: LGTM!

Simple and efficient set-based storage for policy IDs and asset fingerprints. The lazy initialization pattern (if c.filterSet.X == nil) is consistently applied.

filter/cardano/plugin.go (1)

31-74: LGTM!

Plugin registration correctly updated with new name "cardano", updated description, and enhanced option descriptions indicating comma-separated multi-value support. The change maintains backward compatibility with the same option flags.

filter/cardano/cardano.go (3)

119-150: Verify AND filter logic is intentional.

The current implementation uses AND logic: a transaction must match all configured filters to pass through. For example, if both address and policy filters are set, the transaction must match both.

If OR semantics are intended (match any filter), the logic would need adjustment. Please confirm this is the expected behavior.


28-56: LGTM!

Clean struct design with proper channel types and lifecycle fields. The sync.Once for stop prevents double-close panics. Buffered channels (size 10) provide reasonable backpressure handling.


94-117: LGTM!

Block event filtering correctly checks the issuer vkey against all pre-computed pool ID representations with O(1) lookups.

Comment on lines +284 to +301
// Stop the cardano filter
func (c *Cardano) Stop() error {
c.stopOnce.Do(func() {
if c.doneChan != nil {
close(c.doneChan)
}
if c.inputChan != nil {
close(c.inputChan)
}
if c.outputChan != nil {
close(c.outputChan)
}
if c.errorChan != nil {
close(c.errorChan)
}
})
return nil
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Closing inputChan may cause sender panic.

If an external goroutine sends to InputChan() while or after Stop() closes the channel, it will panic. Consider either:

  1. Not closing inputChan in Stop() (let senders manage lifecycle)
  2. Using a mutex or atomic flag to prevent sends after stop
  3. Documenting that callers must stop sending before calling Stop()
🔧 Alternative: nil out channels after close to cause panic on accessor instead

A safer pattern is to set channels to nil after closing, so that subsequent calls to InputChan() return nil (which will also panic on send, but is more debuggable):

 func (c *Cardano) Stop() error {
 	c.stopOnce.Do(func() {
 		if c.doneChan != nil {
 			close(c.doneChan)
+			c.doneChan = nil
 		}
 		if c.inputChan != nil {
 			close(c.inputChan)
+			c.inputChan = nil
 		}
 		if c.outputChan != nil {
 			close(c.outputChan)
+			c.outputChan = nil
 		}
 		if c.errorChan != nil {
 			close(c.errorChan)
+			c.errorChan = nil
 		}
 	})
 	return nil
 }

Note: The test TestCardano_Stop already expects channels to be nil after stop, but the current implementation doesn't set them to nil.

📝 Committable suggestion

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

Suggested change
// Stop the cardano filter
func (c *Cardano) Stop() error {
c.stopOnce.Do(func() {
if c.doneChan != nil {
close(c.doneChan)
}
if c.inputChan != nil {
close(c.inputChan)
}
if c.outputChan != nil {
close(c.outputChan)
}
if c.errorChan != nil {
close(c.errorChan)
}
})
return nil
}
// Stop the cardano filter
func (c *Cardano) Stop() error {
c.stopOnce.Do(func() {
if c.doneChan != nil {
close(c.doneChan)
c.doneChan = nil
}
if c.inputChan != nil {
close(c.inputChan)
c.inputChan = nil
}
if c.outputChan != nil {
close(c.outputChan)
c.outputChan = nil
}
if c.errorChan != nil {
close(c.errorChan)
c.errorChan = nil
}
})
return nil
}
🤖 Prompt for AI Agents
In @filter/cardano/cardano.go around lines 284 - 301, Stop() currently closes
inputChan which can panic if external goroutines send to InputChan()
concurrently; remove closing of c.inputChan and instead set c.inputChan = nil
after acquiring stopOnce (close only doneChan/outputChan/errorChan as before),
ensuring the accessor InputChan() will return nil post-stop; update Stop() to
nil out the channel fields (c.inputChan, c.outputChan, c.errorChan, c.doneChan)
after closing the ones you do close so TestCardano_Stop still observes nil
values and avoid closing inputChan to prevent sender panics.

@wolf31o2 wolf31o2 merged commit b27d127 into main Jan 7, 2026
10 of 11 checks passed
@wolf31o2 wolf31o2 deleted the refactor/filter-cardano branch January 7, 2026 22:50
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