Skip to content

Stream market#1

Open
lastmeta wants to merge 42 commits intomainfrom
stream-market
Open

Stream market#1
lastmeta wants to merge 42 commits intomainfrom
stream-market

Conversation

@lastmeta
Copy link
Copy Markdown
Member

No description provided.

lastmeta and others added 25 commits March 24, 2026 01:04
Change _compileClaimOnP2SHMultiSigMiddle default sighashFlag from
SIGHASH_ALL to SIGHASH_ANYONECANPAY|SIGHASH_ALL so the receiver can
add their own EVR input for fees when claiming channel payments.

Also fix kwarg bug: {sighashFlag: sighashFlag} used the int value as
dict key instead of the string 'sighashFlag'.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SIGHASH_SINGLE locks only the output at the signed input's index (sender's
change), while ANYONECANPAY allows additional inputs. Together they let the
receiver add both fee inputs and fee change outputs when claiming.

Updated _compileClaimOnP2SHMultiSigMiddle default, _createPartialOriginator-
Simple, _createPartialCompleterSimple, and related docstrings.
_createPartialOriginatorSimple now splits the first output into multiple
pieces if len(txins) > len(txouts), guaranteeing every input index has a
corresponding output (required by SIGHASH_SINGLE to avoid degenerate hash).

Also update ravencoin wallet to use SIGHASH_SINGLE|ANYONECANPAY (matching
evrmore) and import SIGHASH_SINGLE.
_createPartialOriginatorSimple and _createPartialCompleterSimple are for
Mundo transactions which only need ANYONECANPAY (add inputs, not outputs).
SIGHASH_SINGLE is only for P2SH channel signing (_compileClaimOnP2SH-
MultiSigMiddle) which was already set correctly.
New kind 34604 (KIND_CHANNEL_COMMITMENT) — parameterized replaceable event
keyed by p2sh_address, tagged with receiver pubkey for push delivery.

models.py:
- ChannelCommitment dataclass (mirrors Mantra's Commitment object)
- InboundCommitment wrapper
- KIND_CHANNEL_COMMITMENT = 34604 constant

client.py:
- publish_commitment() — buyer posts partial tx (replaces POST /channel/commitment)
- get_commitment() — fetch latest for a channel (replaces GET /channel/commitment/:addr)
- remove_commitment() — publish tombstone after claim (replaces DELETE /channel/commitment/:addr)
- commitments() — async iterator, push delivery to seller (replaces polling GET)
- _handle_commitment_event() — internal handler, filters to own pubkey, drops tombstones
- KIND_CHANNEL_COMMITMENT added to subscription filter and event router
- _commitment_queue and commitments_sent/received stats added
- ChannelOpen dataclass + InboundChannelOpen wrapper in models.py
- KIND_CHANNEL_OPEN = 34605 (parameterized replaceable, d=p2sh_address)
- SatoriNostr.publish_channel_open() — sender announces channel to receiver
- SatoriNostr.channel_opens() — async iterator for incoming announcements
- _handle_channel_open_event() routes kind 34605 to _channel_open_queue
- 34605 added to subscription filter so receivers get push delivery
- Add stream_name field to ChannelCommitment (in JSON body + as Nostr tag for relay filtering)
- publish_commitment: emit ["stream", stream_name] tag when present
- record_subscription: preserve last_paid_seq and subscribed_at on re-registration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add kind 34607 constant and CompetitionAnnouncement dataclass with
full serialization, d_tag(), and close() methods. 8 tests, all green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add three competition methods to SatoriNostr client:
- announce_competition: publishes KIND_34607 with d-tag and s/p tags
- close_competition: re-publishes with active=False
- discover_competitions: fetches, deduplicates by d-tag (latest wins),
  filters active_only client-side, supports stream_name tag filter

8 tests, all green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add PredictionSubmission and InboundPrediction dataclasses (kind 34608)
- Add submit_prediction() encrypted DM to host, incoming_predictions() async iterator
- Fix discover_competitions() dedup to use content timestamp (not Nostr created_at)
  so close() (which bumps timestamp+1) always wins even within the same second
- Add 26 tests covering prediction models and client round-trip

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The host needs the predictor's EVRmore wallet pubkey to open a payment
channel and pay them. Previously it wasn't transmitted at all, forcing
the neuron to search subscriptions for the predictor's nostr pubkey as
if they were a stream producer — which never works because predictors
aren't producers. Now it rides along inside the encrypted DM itself
(still private to the host) and the host reads it back off the
competition_predictions row when scoring fires.
publish_commitment now requires receiver_nostr_pubkey (32-byte x-only
Nostr pubkey) instead of using commitment.receiver_pubkey (33-byte EVR
wallet pubkey) for the Nostr p-tag. The old value was rejected by relays
as an invalid fixed-size tag.

_handle_commitment_event no longer filters by comparing wallet pubkey to
Nostr pubkey (which always failed). Commitments are queued unconditionally;
the consumer filters by p2sh_address DB lookup, matching the channel_open
pattern.
Was 0, causing roundSatsDownToDivisibility to zero out any amount under
100M sats. With div=0, payment channel partial txs omitted the receiver
output entirely — Alice got 0 SATORI despite being owed the cumulative.
jordan added 2 commits April 13, 2026 23:36
Add AccessRequest/InboundAccessRequest models, KIND_ACCESS_REQUEST (34609),
approval_required flag on DatastreamMetadata. Client gains request_access(),
incoming_access_requests(), and _handle_access_request_event() for NIP-04
encrypted access request DMs between subscribers and producers.
incoming_predictions() and incoming_access_requests() used `return` on
asyncio.TimeoutError instead of `continue`, causing the async iterator
to exit after 1 second of inactivity. This meant prediction listeners
died immediately after startup — hosts never received prediction DMs
in real-time. All other async iterators (observations, payments,
channels, settlements, tombstones) correctly use `continue`.
krizhnaa and others added 14 commits April 17, 2026 09:36
New subscribers to paid streams had last_paid_seq=None, so the
provider never sent them an observation, and without receiving an
observation the subscriber never triggered payment — deadlock.

Fix: when free_sample is True (default), send one free encrypted
observation to new subscribers. After delivery, set last_paid_seq
to the current seq so they must pay for subsequent observations.
The subscriber's automatic payment on receipt then bootstraps the
normal paid cycle.

Adds free_sample field to DatastreamMetadata (default True).
Providers can set it to False to require upfront payment instead.
Pin the agreed price at commitment time so receiver uses the same price
the sender committed to, preventing mismatch on mid-cycle price edits.
Provider removes subscriber from in-memory _subscribers dict when
receiving an unsubscribe announcement, stopping encrypted observations.
Drop non-tombstone commitment events whose p tag doesn't match our
pubkey. Eliminates 3s retry + DB lookup for stale third-party
commitments on reconnect.
No longer needed — receiver uses its own live price, sender refreshes
price via relay discovery during reconcile.
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