Skip to content

[v0.0.18 / Shellnet 4.0.30] Failed dead-gateway probe debits one buyer tick and leaves finalized_owed with probe_accepted=false #121

Description

@AndreaMangraviti

Summary

On live Shellnet 4.0.30 with dexdo v0.0.18, a buyer matched a valid ask and received the handover, but the first inference request failed with HTTP 502 because the seller gateway could not open the upstream connection.

The buyer subsequently submitted streamStop successfully. All remaining escrow was released and buyer_locked returned to zero, but the buyer PrivateNote balance decreased by exactly one tick (10 SHELL), while the residual TokenContract reports:

  • probe_accepted=false
  • finalized_owed=10 SHELL
  • buyer_locked=0
  • deposit=0
  • prepaid=0
  • frozen=0

This appears to mean that the failed probe tick was finalized as owed, rather than being burned to no one, despite no inference being delivered.

This report is related to the accounting questions previously discussed in #42, but it is a new by-fact reproduction on the current network and current CLI generation.

Environment

  • OS: Windows
  • Network: Shellnet
  • dexdo CLI: v0.0.18
  • SuperRoot: 4.0.30
  • RootPN: 4.0.30
  • RootOracle: 4.0.30
  • dexdo doctor: PASS
  • Policy: OK
  • Shellnet endpoint: reachable
  • Local clock skew: 1 second
  • PrivateNote code hash:
    4712999eb88c096fef770755b21d3b6b3fde724967424a928507a5499767e812
  • Expected PrivateNote code hash:
    4712999eb88c096fef770755b21d3b6b3fde724967424a928507a5499767e812

Buyer PrivateNote

PrivateNote:

0:c16cebdfbc451bbddc0d140700fea98f5171e41ed7f0147bfd13e594a40cce7d

The PrivateNote was active, current-generation, and had no reported order lock before the purchase.

Authoritative ECC[2] spendable balance captured immediately before the buyer invocation:

81 SHELL

Authoritative ECC[2] spendable balance captured after streamStop and cleanup:

71 SHELL

Observed net debit:

10 SHELL

The net debit is exactly one tick.

Market and selected ask

frame_model:

qwen--qwen3--32b

order_book:

0:9946bdc532829014289bfad162c3afcfcf43470be2be4739c6d855bb43f11f64

order_id:

160

TokenContract:

0:45a7cc371ccdb44875d7775507b5e53c3a55f07fa7a08794b24c40e2cbd32f40

Requested ticks:

2

Price per tick:

10 SHELL
raw: 10000000000

Quote accounting:

  • total_without_fee: 20 SHELL
  • platform_fee: 0.5 SHELL
  • total_with_fee: 20.5 SHELL

Raw quote accounting:

  • total_without_fee: 20000000000
  • platform_fee: 500000000
  • total_with_fee: 20500000000

The quote was complete and selected the explicit TokenContract successfully.

Buyer invocation

The buyer was started with the equivalent of:

dexdo buyer
--note-key
--note-addr 0:c16cebdfbc451bbddc0d140700fea98f5171e41ed7f0147bfd13e594a40cce7d
--frame-model qwen--qwen3--32b
--token-contract 0:45a7cc371ccdb44875d7775507b5e53c3a55f07fa7a08794b24c40e2cbd32f40
--allow-unverified-model
--models <LOCAL_MODELS_JSON>
--continuity-mode on-demand
--ticks 2
--max-price-per-tick 10000000000
--local-listen 127.0.0.1:18081
--contracts contracts/deployed.shellnet.json
--json

No secret key, seed phrase, API key, or Telegram credential is included in this report.

Observed buyer lifecycle

The buyer emitted the following lifecycle sequence:

  1. starting
  2. endpoint_binding
  3. endpoint_ready
  4. quote_selected
  5. buy_submitted
  6. matched
  7. handover_waiting
  8. handover_received
  9. inference request attempted
  10. inference failed with HTTP 502
  11. post-inference streamStop submitted

Relevant timing:

  • endpoint_ready: approximately 1.9 seconds
  • quote_selected: approximately 24.5 seconds
  • buy_submitted: approximately 49.1 seconds
  • matched: approximately 52.0 seconds
  • handover_received: approximately 62.8 seconds
  • inference request completed with failure: approximately 192.8 seconds
  • streamStop submitted: approximately 195.7 seconds

Inference failure

The local OpenAI-compatible endpoint returned:

HTTP 502

Response:

{
"error": {
"message": "upstream open failed after retry: transport error",
"type": "invalid_request_error"
}
}

The CLI also logged that the upstream open failed and was retried once according to:

dead_gateway=retry_then_reclaim

The retry did not produce a usable inference response.

No inference content was delivered to the buyer.

Post-inference close

The buyer-side close completed with:

{
"schema": "dexdo.close.v1",
"network": "shellnet",
"handle": "deal-0-45a7cc371ccdb44875d7775507b5e53c3a55f07fa7a08794b24c40e2cbd32f40-buyer",
"role": "buyer",
"token_contract": "0:45a7cc371ccdb44875d7775507b5e53c3a55f07fa7a08794b24c40e2cbd32f40",
"action": "streamStop",
"submitted": true,
"terminal": false,
"reason": null,
"state_before": "probe",
"state_after": "stopped",
"tx": null
}

Current on-chain status

Command:

dexdo status 0:45a7cc371ccdb44875d7775507b5e53c3a55f07fa7a08794b24c40e2cbd32f40
--contracts contracts/deployed.shellnet.json
--json

Current output:

{
"schema": "dexdo.status.v1",
"network": "shellnet",
"token_contract": "0:45a7cc371ccdb44875d7775507b5e53c3a55f07fa7a08794b24c40e2cbd32f40",
"frame_model": null,
"state": "funded-but-never-opened",
"active": true,
"funded": true,
"opened": false,
"disputed": false,
"probe_accepted": false,
"accounting": {
"finalized_owed": "10000000000",
"buyer_locked": "0",
"deposit": "0",
"prepaid": "0",
"frozen": "0",
"last_advance_unix": 1785357545,
"funded_time_unix": 1785357532
},
"next": {
"action": "unknown_role",
"retryable_after_unix": null,
"command": "close"
}
}

Accounting reconciliation

Buyer balance before:

81 SHELL

Maximum quote/escrow:

20.5 SHELL

Buyer balance after stop and cleanup:

71 SHELL

Net buyer debit:

10 SHELL

Escrow returned or released:

10.5 SHELL

The 10 SHELL debit corresponds exactly to one probe tick.

At the same time, the TokenContract reports:

finalized_owed=10 SHELL

This is the point that needs clarification. A probe burn and a finalized seller entitlement are materially different accounting outcomes.

Expected behavior

Based on the documented probe-and-burn protection:

  • the buyer may risk at most one probe tick;
  • when a seller fails model/content/gateway verification, the seller should earn nothing;
  • the seller commission/stake and buyer probe should be burned according to the applicable failure path;
  • all remaining buyer escrow should be refunded;
  • the final state and accounting fields should make it clear whether the probe was burned, refunded, or finalized to the seller.

If a dead gateway after handover is intentionally classified differently, the CLI should expose that classification and the resulting accounting consequence explicitly.

Actual behavior

  • The seller gateway failed to deliver inference.
  • The buyer received HTTP 502 after the configured retry.
  • streamStop was submitted during the probe phase.
  • probe_accepted remains false.
  • The buyer lost exactly one probe tick.
  • finalized_owed equals that same probe tick.
  • The residual TokenContract remains active in funded-but-never-opened.
  • No buyer funds remain locked.

Why this may be a regression

Issue #42 discussed the distinction between:

  • a buyer probe tick burned from internal escrow; and
  • a note-level debit or seller entitlement.

The current reproduction is on dexdo v0.0.18 and Shellnet 4.0.30 and includes an exact ECC[2] before/after delta.

Unlike the previously described bounded-burn behavior, this TokenContract exposes finalized_owed equal to the buyer's lost probe tick after a dead-gateway failure.

Impact

If finalized_owed is withdrawable by the seller, a seller with a dead or unusable gateway may still earn one tick per failed buyer attempt.

That would weaken the intended anti-abuse property that an invalid seller earns nothing and loses its stake.

Even if finalized_owed does not represent a seller-withdrawable entitlement in this state, the current status schema is ambiguous enough to prevent reliable accounting and automated risk classification.

The residual state is also internally confusing:

  • the buyer lifecycle reached handover_received;
  • the close was submitted from state_before=probe;
  • the current status reports funded-but-never-opened;
  • finalized_owed is non-zero;
  • opened=false and probe_accepted=false.

Questions

  1. Is finalized_owed=10 SHELL expected when the first inference request fails with a dead-gateway HTTP 502 and probe_accepted=false?
  2. Does finalized_owed in this state represent an amount the seller can actually withdraw?
  3. Was the buyer's 10 SHELL probe tick burned, transferred to the seller, or retained by the TokenContract?
  4. Should dead_gateway=retry_then_reclaim eventually produce a ProbeBurn/BurnBoth outcome rather than finalized_owed?
  5. Why does the final status report funded-but-never-opened after matched and handover_received were emitted?
  6. Is streamStop the correct buyer action for this failure, or should the CLI have submitted a dispute or another failure-specific transition?
  7. Should the CLI emit an authoritative terminal settlement receipt showing:
    • probe debit;
    • seller entitlement;
    • platform fee charged/refunded;
    • buyer refund;
    • burned amount;
    • residual TokenContract balance?
  8. Is this a regression from the bounded-burn behavior discussed in settlement says BurnBoth(ProbeBurn{buyer:N}) but the buyer note is refunded ~in full on probe-phase close — is the burn label or the refund correct? #42?
  9. Can the residual TokenContract be safely destroyed or cleaned up without changing the already-finalized buyer accounting?

Requested outcome

Please confirm the intended accounting semantics for this failure path and whether finalized_owed is correct.

If this is unintended, the buyer failure policy should ensure that a dead gateway cannot leave a seller entitlement after no inference was delivered.

If it is intended, dexdo status and the buyer terminal output should clearly distinguish:

  • funds still locked;
  • funds refunded;
  • probe tick burned;
  • probe tick finalized to seller;
  • platform fee retained or refunded;
  • residual seller/protocol cleanup state.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions