Midnight network state tracking separates local proof work from network finality
Midnight network transactions move through local execution, zero-knowledge proof generation, wallet balancing, network submission and finalization. Track the phase before changing anything: proof-server queue fields diagnose local proving, a node response identifies submission rejection and a transaction identifier and block height confirm inclusion. Closure needs one more check when a contract call should change public state: query the indexed contract state and compare the expected ledger field.
State evidence comes before another attempt
Preserve the visible state at each layer before repeating a call. Record the selected network identifier, proof-server health, queue activity, circuit name, wallet balancing outcome and any node response. If the call returned a transaction identifier, keep its block height as well. These observations locate the interruption without treating every delay as a rejected transaction.
A successful early phase says nothing about later phases. Local execution may finish while proving remains queued. A proof may return before wallet balancing discovers insufficient DUST. Submission may reach the node and fail transaction-pool validation. Even an included transaction needs a public-state check when the intended outcome is a contract change.
Five stages trace the transaction path
The pipeline becomes easier to diagnose when each stage has a precise meaning:
- An unproven transaction contains the output of local circuit execution.
- A proven transaction carries the zero-knowledge proof required for validation.
- A balanced transaction includes the wallet inputs needed to cover its DUST fee.
- A submitted transaction has crossed from the local providers to the node.
- A finalized transaction has inclusion evidence and a block height.
These stages are ordered. Proof generation cannot confirm submission, and a submission request cannot confirm finalization. A user interface may combine several phases into one progress label, so logs and returned fields provide the clearer account.
Local execution creates the first inspectable boundary
A Compact circuit runs on the local device and produces an unproven transaction. Its public transcript describes the on-chain values read or written and the rules applied. Private witness data supplies secret inputs needed by the circuit. This local result is a candidate state transition, not evidence the network has accepted anything.
The network receives the public transcript and a zero-knowledge proof, but not the private witness. The local proof server does receive the witness because it must prove the circuit constraints. It does not hold wallet signing keys, sign transactions or spend funds.
Proof generation has observable queue state
The proof service exposes separate health, version and readiness information. A healthy process reports an operational status. Readiness adds the number of jobs processing, jobs pending and available job capacity. A running container alone does not establish that a proof request is advancing.
If pending work rises while processing stays flat, proving jobs are queuing. Check readiness and worker availability before treating the delay as a stall. No transaction has reached wallet balancing or network submission at that stage.
The prover checks its proof before returning it. A mismatch between the circuit and its proving materials produces an error instead of a silently defective transaction. Recompile the contract when the proving key, verifier key or ZKIR artifacts no longer match the circuit build.
The broadcast boundary changes the recovery choice
The smallest safe adjustment depends on whether submission began. Before broadcast, recovery remains local. After broadcast, repeating an operation without checking its status can create a second request for an action the network may already have accepted.
Recovery before submission
A connection refusal identifies an unreachable proof service. A queue which does not advance points to its workers or readiness. A circuit-material mismatch calls for a fresh compile. Correct the narrowest cause, recreate the call from the current inputs and let the normal pipeline produce a new proof.
Failure before the proof returns cannot produce finalized ledger evidence because balancing and submission occur later. Keep the original error and readiness snapshot. They distinguish a service outage from an invalid circuit build and prevent an unrelated wallet change from obscuring the cause.
Recovery after submission
Once the transaction reaches the node, first determine whether it was rejected, accepted or left uncertain by a lost response. A returned transaction identifier provides a direct lookup key. Without one, inspect the full RPC response and the current public state before issuing another operation.
Transaction-pool rejection
A 1010 Invalid Transaction response means the node's transaction pool rejected the extrinsic. Its outer code does not identify the underlying fault. If the response data contains Custom error: N, that inner value identifies a Midnight ledger variant; without it, the rejection occurred in upstream Substrate validation. Use the available error detail to choose the response: rebuild stale or malformed transactions, but address node-side infrastructure errors before retrying.
Accepted call without the expected view
A transaction identifier and block height establish where the call landed. They do not interpret the contract's fields. Query the contract through the public data provider and decode its ledger state. Compare the field owned by the circuit, not a cached interface label or an unrelated wallet balance.
Version and network compatibility limit valid adjustments
The compiler output, runtime packages, proof server, node, indexer and wallet integration must belong to a supported combination. The configured network identifier must also match the node and indexer endpoints. Changing a timeout cannot repair incompatible artifacts or an incorrect network target. Confirm those relationships before modifying the transaction itself.
Status fields separate delay from failure
Each checkpoint has a distinct success signal and failure meaning. The table keeps those signals together without turning them into one synthetic status.
| Observed field or stage | Expected evidence and failure meaning |
|---|---|
| Network identifier | An explicit target matches the connected services; InvalidNetworkId indicates disagreement. |
| Proof health | The service reports an operational status; no response prevents proving from starting. |
| Proof queue | Processing, pending and capacity fields show load; pending growth with no processing means jobs are queued but not advancing. |
| Circuit materials | The proving key, verifier key and ZKIR come from the matching compile; disagreement stops proof creation. |
| Wallet balancing | Available DUST covers the calculated fee; insufficient DUST stops balancing before submission. |
| Node response | An accepted call yields transaction data; a 1010 response may carry an inner rejection code. |
| Closure evidence | The transaction identifier and block height show inclusion; decoded indexed state confirms the intended public effect. |
No single row replaces the others. A ready proof server does not establish wallet readiness, while a transaction identifier does not explain a contract field. Read the last successful row and the first missing or failing row together.
Node rejection requires the complete response
The 1010 code is a Substrate-level envelope for a transaction-pool rejection. When Midnight ledger logic supplies an inner variant, the response data carries it as an unsigned byte value. Client wrappers may keep the short message while dropping that data, so retain the complete serialized error before beginning a retry.
Variant numbers can change between node releases. Interpret the inner value against the release running on the target network instead of relying on an old numeric list. The named category then guides the adjustment: current-state conflicts require rebuilding, malformed proof data requires a construction fix and infrastructure faults concern the node rather than the submitted action.
Finalization combines inclusion with application state
A Midnight.js contract call builds, proves, balances and submits its transaction before resolving with public transaction data. The returned txId identifies the transaction, while blockHeight identifies the block containing it. Together they provide durable inclusion evidence for that completed call.
Application closure is narrower. If the operation was meant to update a contract field, read the contract through the indexer and decode the returned data with the generated ledger interface. The expected field change connects the transaction record to the application outcome instead of assuming every included call produced the intended view.
Public closure and private state answer different questions
The indexer exposes public contract state, block history and transaction-related data. It does not provide the private witness used during local execution. Private contract state and locally generated signing material stay with the private-state provider, while the proof lets the ledger validate constraints without receiving those secrets.
The proof server still sees witness data during proof creation. Run it on the local device or on a controlled machine reached through an encrypted channel. A successful public-state query confirms the visible ledger outcome, but it does not turn private inputs into public records.
A bounded recovery loop preserves reversibility
Start with the earliest phase lacking its normal evidence. Preserve later settings until the failed layer is understood. This keeps a local proving problem from becoming an unnecessary wallet, network or contract change.
Assume the configured environment is compatible only after its network identifier and component versions agree. Run the circuit, watch proof readiness and continue only when the proof returns. A stalled queue or material mismatch invalidates that assumption before any broadcast occurs.
If wallet balancing fails, resolve DUST readiness and rebuild through the normal pipeline. If the node rejects submission, decode the full response before changing inputs. When submission may have succeeded despite a lost client response, search for ledger evidence before repeating the operation.
Finish by joining inclusion and effect. For a contract call, one concrete closure record contains the txId, blockHeight and decoded public ledger field returned through the indexer.
FAQ
What happens if the DUST validity window expires before broadcast?
If a transaction's DUST spend falls outside its permitted validity window before broadcast, the node will reject it when submitted. Rebuild and rebalance the transaction through the normal pipeline instead of submitting the expired form again. The fresh operation should use current wallet and ledger state, then produce new submission evidence. An expired intent is a pre-finalization failure, so it cannot serve as proof of an on-chain state change.
Why can wallet balancing fail after proof generation succeeds?
Wallet balancing is a separate stage which follows zero-knowledge proof generation. A valid proof establishes that the circuit constraints were satisfied, but it does not establish that enough DUST is available for the fee. The wallet provider performs that later check. If balancing reports insufficient funds, retain the successful proof-stage result, correct DUST readiness and rebuild the transaction before attempting submission.
Is a missing transaction identifier proof that nothing was submitted?
A missing transaction identifier alone does not prove the node never received the transaction. An explicit proof or balancing failure occurs before submission, but a connection loss can also hide a response after broadcast. Check the full error, node-facing records and current public state before repeating the action. This distinction prevents a recovery attempt from duplicating an operation whose acknowledgement was lost.
What does a block height prove beyond the transaction identifier?
A block height places the identified transaction in a specific finalized block. The transaction identifier distinguishes the call, while the height provides its chain location and supports block-level inspection. Neither field interprets the resulting contract data. When closure depends on a state change, query the contract through the indexer and decode the relevant ledger field as a separate verification step.
Can a proof be reused after changing transaction inputs?
A proof cannot be treated as valid evidence for different transaction inputs. It attests to the circuit execution and transcript produced from the original public and private values. Changing those values requires local execution and proof generation to run again. The new result must then pass wallet balancing and submission in order, because the earlier proof does not authorize a modified state transition.