Skip to content

feat: add UtxoProcessor event listeners#20

Merged
smartgoo merged 14 commits intokaspanet:mainfrom
elldeeone:utxo-event-listeners
Feb 28, 2026
Merged

feat: add UtxoProcessor event listeners#20
smartgoo merged 14 commits intokaspanet:mainfrom
elldeeone:utxo-event-listeners

Conversation

@elldeeone
Copy link
Contributor

@elldeeone elldeeone commented Feb 7, 2026

Summary:

  • Add UtxoProcessor event listeners: add_event_listener, remove_event_listener, remove_all_event_listeners.
  • Wire async dispatch from processor.multiplexer().channel() to Python callbacks; start dispatcher before UtxoProcessor.start(), stop after UtxoProcessor.stop().
  • Stable payload shape: {"type": "<kebab>", "data": <obj|None>}; tx-record events flattened to match WASM Events::to_js_value().

Tests:

  • ./check

Notes:

  • Targets: same event strings as UtxoProcessorEventType (wallet/core/src/wasm/notify.rs @ rusty-kaspa@1a2f98a), plus "*" and "all" alias.
  • Callback convention: callback(*args, event, **kwargs); callback errors logged; dispatch continues.
  • Example: examples/transactions/utxo_context_listener.py
  • Stubs: pyo3-stub-gen can’t express the overload cleanly (it emits callback: Optional[...] = None, which implies invalid calls when targets are provided). We post-process kaspa.pyi to inject @typing.overload entries so type-checkers/IDEs reflect the real call patterns; typing-only, no runtime behaviour change.
  • Changelog: Added listeners + Fixed stub overload note.

WASM parity: UtxoProcessor addEventListener/removeEventListener API surface (dispatch TBD)

Tests: cargo fmt --all; cargo clippy -- -D warnings; ./build-dev; pytest tests/unit -v
WASM parity: wallet/core/src/wasm/utxo/processor.rs (multiplexer dispatch)

Tests: cargo fmt --all

Tests: cargo clippy -- -D warnings

Tests: ./build-dev

Tests: source env/bin/activate && pytest tests/unit -v

Tests: source env/bin/activate && pytest tests/integration -v
WASM parity: wallet/core/src/wasm/utxo/processor.rs (Events::to_js_value flattening; start double-call behavior)

Tests: cargo fmt --all

Tests: cargo clippy -- -D warnings

Tests: ./build-dev

Tests: source env/bin/activate && pytest tests/unit -v

Tests: source env/bin/activate && pytest tests/integration -v
WASM parity: wallet/core/src/wasm/utxo/processor.rs (event listeners)

Tests: cargo fmt --all

Tests: cargo clippy -- -D warnings

Tests: source env/bin/activate && pytest tests/unit -v
WASM parity: wasm/wallet/core/src/wasm/utxo/processor.rs@1a2f98a
WASM parity: wasm/wallet/core/src/wasm/utxo/processor.rs@1a2f98a
WASM parity: wasm/wallet/core/src/wasm/utxo/processor.rs@1a2f98a
WASM parity: wasm/wallet/core/src/wasm/utxo/processor.rs@1a2f98a
WASM parity: wasm/wallet/core/src/wasm/utxo/processor.rs@1a2f98a (listener overloads)

Tests: cargo fmt --all; cargo clippy -- -D warnings; source env/bin/activate && pytest tests/unit -v
WASM parity: docs only

Tests: cargo fmt --all; cargo clippy -- -D warnings; source env/bin/activate && pytest tests/unit -v
Copy link
Collaborator

@smartgoo smartgoo left a comment

Choose a reason for hiding this comment

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

@elldeeone Again, thank you for your continued work here! This callback functionality is fun to work on, much deeper than simple standard wrappers.

Here are comments from first pass through. One thing to change and two where I'm curious for your thoughts. May result in changes depending on what you think.

}
}

fn parse_event_targets(value: Bound<'_, PyAny>) -> PyResult<Vec<EventKind>> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

@elldeeone The following probably could have been tagged to a different line of code but this fn made me think about it.

What do you think about the following?

  • Introduce a Python-exposed enum that wraps EventKind (e.g. UtxoProcessorEventKind or something like that).
  • Implement PyO3's FromPyObject trait for the enum so that it can be extracted from str or enum variant on the Rust side.
  • For add/remove listener methods - the event parameter can then be passed as str, UtxoProcessorEventKind variant, list[str | UtxoProcessorEventKind], (and maybe others if I'm missing anything).

There is something similar in place for RpcClient event types:

  • PyNotificationEvent provides enum with all event types to Python developers.
  • It can be extracted from pyobject as str or variant (see FromPyObject impl here).
  • RpcClient add_event_listener() accepts event parameter as PyNotificationEvent (link). This allows user to pass event as str or enum variant. Different here is that a list of events is not supported

If we added a wrapper enum for UtxoProcessor, it could probably use one of the wrap enum macros from macros.rs. While on the RpcClient side it required a different implementation because of some RK native enum nesting (iirc).

The benefit of adding a wrapper enum is that it gives Python developers a static list of supported event types.

I don't feel too strongly about this at the moment but while work is actively being done here perhaps it's worth doing for parity with python RpcClient event passing. Curious what you think

Copy link
Contributor Author

@elldeeone elldeeone Feb 26, 2026

Choose a reason for hiding this comment

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

@smartgoo Thanks for the suggestion and for laying it out so clearly. I agree with your direction here, and your comparison to RpcClient helped shape my final approach.

I’ve implemented a Python-exposed UtxoProcessorEvent enum, FromPyObject extraction from both str and enum variant, and listener target parsing for str | UtxoProcessorEvent | Sequence[str | UtxoProcessorEvent] in both add_event_listener and remove_event_listener.

I also updated stubs/docs and tests. I ended up taking the explicit enum/impl route here instead of the macro, mainly to keep the extraction behaviour and event surface explicit. Do you think this looks ok?

Copy link
Collaborator

@smartgoo smartgoo Feb 26, 2026

Choose a reason for hiding this comment

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

@elldeeone I think the primary benefit of a Python wrapper enum over the RK native enum (via one of the macros or otherwise) is - the compiler ensures all enum variants are present in the Python enum.

That said I'm ok with this approach of defining explicitly. Not like it rk native enum will change often. Just curious why this approach instead of one the macros? If there is missing functionality, an issue, etc.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@elldeeone BTW just the one question in comment above and we should be able to merge this after. Everything else looks good!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@smartgoo Yeah true, I agree the macro path gives compile-time coverage if RK enum variants change.

I went explicit in this PR mainly to avoid blowing out scope while we were still settling listener parsing/validation behaviour. In hindsight, I suppose it's not that much though. If you’d prefer, I’m happy to switch this enum to wrap_unit_enum_for_py! here before merge just lmk.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@elldeeone All good, we can move it to macro sometime soon. Merging shortly!

Extract shared internal PyCallback helper for RpcClient and UtxoProcessor event listener dispatch/removal.

WASM parity: no API behavior change; keep UtxoProcessor/RpcClient addEventListener semantics aligned with wallet/core/src/wasm/utxo/processor.rs@1a2f98a
Expose UtxoProcessorEvent to Python and accept str | enum | sequence[str|enum] in UtxoProcessor listener target parsing.

WASM parity: align Python UtxoProcessor listener target surface with wallet/core/src/wasm/notify.rs and wallet/core/src/wasm/utxo/processor.rs@1a2f98a
Prevent empty-string listener targets from falling through iterable parsing and silently no-oping. Add regression tests for add/remove listener empty target handling.

WASM parity: preserve event-target validation behavior while extending Python target typing for UtxoProcessor listeners
@smartgoo smartgoo merged commit 2b29914 into kaspanet:main Feb 28, 2026
3 checks passed
@smartgoo
Copy link
Collaborator

@elldeeone Merged, thank you!

@elldeeone
Copy link
Contributor Author

@smartgoo thanks for the review!

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