You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Each trace record captures the VM state at instruction entry: `pos` is the instruction's byte offset in the binary (`null` for synthetic instructions), `instr` is the instruction and its operands, and `stack`/`locals` are the value stack and locals as `[type, value]` pairs. See [docs/interpreter.md](docs/interpreter.md) for the full trace format.
149
+
A trace can contain five kinds of records:
150
+
151
+
-`callContract`
152
+
- Wasm instruction records
153
+
-`hostCall`
154
+
-`contractData`
155
+
-`endWasm`
156
+
157
+
The example above only has three of these: `callContract`, instruction records, and `endWasm`. `foo()` doesn't touch storage or call any host functions, so no `contractData` or `hostCall` records show up.
158
+
159
+
Here's what each record type carries:
160
+
161
+
-`callContract`: logged for each contract call in the transaction, including contract-to-contract calls. Records the caller, the callee, the function name, the arguments, the call depth, and the callee's storage before the call runs.
162
+
- Instruction records: logged at each WebAssembly instruction's entry. `pos` is the instruction's byte offset in the binary (`null` for synthetic instructions), `instr` is the instruction and its operands, and `stack`/`locals` are the value stack and locals as `[type, value]` pairs.
163
+
-`hostCall`: logged when the contract calls a host function. `instr` gives `["hostCall", moduleId, functionId]`, identifying which host function ran. `locals` holds the function's arguments, indexed by position. Host calls don't use the stack, so `stack` is absent.
164
+
Here's a `hostCall` record for a call to `put_contract_data`, module id `l`, function id `_`:
-`contractData`: logged for storage updates. Gives the contract and the storage type (`instance`, `persistent`, or `temporary`). A `put` carries the key and value as its two args; a `del` carries only the key.
174
+
Here's a `contractData` record for a `put`, followed by a `del` on the same key:
Copy file name to clipboardExpand all lines: docs/node-semantics.md
+9-7Lines changed: 9 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -56,14 +56,14 @@ If `request.json` is absent, `insert-handleRequestFile` does not fire and K halt
56
56
57
57
## Dispatch and the read-only methods
58
58
59
-
`#dispatch` reads the `method` field and routes to a per-method rule. The read-only methods answer directly from constants and the bookkeeping files:
59
+
`#dispatch` reads the `method` field and routes to a per-method rule. The read-only methods answer directly from constants and the bookkeeping files. Response shapes follow real stellar-rpc's serialization: ledger sequences and `protocolVersion` are JSON numbers; close-time fields are int64-as-string (JSON strings holding a decimal integer).
60
60
61
-
-`getHealth` → `{ "status": "healthy" }`
62
-
-`getNetwork` → `{ "friendbotUrl": null, "passphrase": ..., "protocolVersion": ... }` (passphrase/version come from the request, keeping the semantics network-agnostic)
-`getTransaction` → reads the hash's `receipts/receipt_<hash>.json` file; returns the stored receipt merged with the current `latestLedger`/`latestLedgerCloseTime`, or `{ "status": "NOT_FOUND", ... }` when the file is absent
61
+
-`getHealth` → `{ "status": "healthy", "latestLedger": N, "latestLedgerCloseTime": <now>, "oldestLedger": 0, "oldestLedgerCloseTime": "0", "ledgerRetentionWindow": N+1 }` (everything since genesis is retained)
62
+
-`getNetwork` → `{ "passphrase": ..., "protocolVersion": ... }` (passphrase/version come from the request, keeping the semantics network-agnostic; `friendbotUrl` is omitted — no friendbot)
63
+
-`getLatestLedger` → reads `metadata.json` and returns `{ "id": #ledgerId(seq), "protocolVersion": ..., "sequence": <latest_ledger> }`; `#ledgerId` derives a deterministic per-ledger 64-hex id from the sequence (the semantics have no hash hooks, so it is a fixed odd-constant multiply mod 2^256, which is injective)
64
+
-`getTransaction` → reads the hash's `receipts/receipt_<hash>.json` file; returns the stored receipt merged with the ledger-range fields (`latestLedger`/`latestLedgerCloseTime`/`oldestLedger`/`oldestLedgerCloseTime`), or `{ "status": "NOT_FOUND", ... }` (with the same ledger-range fields) when the file is absent
65
65
66
-
`#respond(ID, RESULT)` is the shared terminal: it writes the JSON-RPC envelope to `response.json`, removes `request.json`, and sets the exit code to 0.
66
+
`#respond(ID, RESULT)` is the shared terminal: it writes the JSON-RPC envelope to `response.json`, removes `request.json`, and sets the exit code to 0.`#respondError(ID, CODE, MSG)` is its error counterpart, writing `{jsonrpc, id, error: {code, message}}` instead; the unknown-method fallback uses it to answer `-32601 Method not found` (a safety net — the Python server filters unknown methods before they reach K).
67
67
68
68
---
69
69
@@ -83,9 +83,11 @@ If `request.json` is absent, `insert-handleRequestFile` does not fire and K halt
83
83
84
84
1. writes `metadata.json` with `latest_ledger + 1`,
85
85
2. writes the receipt to `receipts/receipt_<hash>.json`:
3. responds with `{hash, status: "PENDING", latestLedger, latestLedgerCloseTime}`.
88
88
89
+
A `sendTransaction` whose hash already has a receipt never reaches `#runTx`: dispatch answers `{hash, status: "DUPLICATE", latestLedger, latestLedgerCloseTime}` directly, without executing anything or advancing the ledger. (For wasm uploads the Python server also suppresses the `<program>` injection on duplicates, since those steps would run before dispatch.)
90
+
89
91
The trace is not part of the receipt — the executing steps already appended it to `traces/trace_<hash>.jsonl`. Reaching `#finalizeTx` means the steps completed without getting stuck, so the status is `SUCCESS`. A failed transaction gets stuck before this point, `response.json` is never written, and the Python server records the `FAILED` receipt instead.
Copy file name to clipboardExpand all lines: docs/notes.md
+5-19Lines changed: 5 additions & 19 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -33,23 +33,9 @@ The tests do not yet cover `bytes` / `address` SCVal arguments or `SCVec` / `SCM
33
33
34
34
## Known gaps
35
35
36
-
Measured against the official Stellar RPC surface for protocol 22 (the OpenRPC spec plus the `protocols/rpc` structs in go-stellar-sdk, which are what real stellar-rpc emits).
37
-
38
-
### Missing methods
39
-
40
-
`simulateTransaction`, `getLedgerEntries`, `getEvents`, `getTransactions`, `getLedgers`, `getFeeStats`, and `getVersionInfo` are not implemented and return `-32601` Method not found. TTL/footprint operations are likewise unsupported.
41
-
42
-
### Deviations in the implemented methods
43
-
44
-
-`getHealth` returns only `{status}`; the spec adds `latestLedger`, `oldestLedger`, and `ledgerRetentionWindow` (numbers).
45
-
-`getNetwork` returns `protocolVersion` as a string (`"22"`) where the spec has a number, and `friendbotUrl: null` where real stellar-rpc omits the field entirely.
46
-
-`getLatestLedger` returns `protocolVersion` as a string, and `id` is a constant all-zeros hash instead of a per-ledger value.
47
-
-`sendTransaction` returns `latestLedger` as a string; the statuses `DUPLICATE`, `ERROR`, and `TRY_AGAIN_LATER` are never returned (resubmitting a transaction re-executes it instead of reporting `DUPLICATE`), and `errorResultXdr` / `diagnosticEventsXdr` are never returned.
48
-
-`getTransaction` is missing the required `oldestLedger` / `oldestLedgerCloseTime` (all statuses) and `applicationOrder` / `feeBump` (success/failed); `latestLedger` and `ledger` are strings where the spec has numbers; `resultXdr` / `resultMetaXdr` are empty-string stubs (contract return values are not surfaced); the `hash` parameter is not validated against the 64-lowercase-hex format.
49
-
50
-
### Cross-cutting
51
-
52
-
- The `xdrFormat` parameter is accepted on `getTransaction` and `sendTransaction`, but only the default `'base64'` is supported; `'json'` is rejected with `-32602`.
53
-
- The K semantics' unknown-method fallback answers with `result: null` instead of error `-32601`. This is currently unreachable — the Python layer rejects unknown methods before the semantics run — but latent.
36
+
-`resultXdr` / `resultMetaXdr` are empty stubs (contract return values not surfaced).
37
+
-`sendTransaction`'s `errorResultXdr` is always a generic `txMALFORMED` result (no per-cause codes such as `txBAD_SEQ` — sequence numbers, fees, and signatures are not modelled), and `diagnosticEventsXdr` is never populated (both fields are optional in the spec). `TRY_AGAIN_LATER` is never returned by design: it signals mempool backpressure, which cannot arise in a synchronous node without a mempool.
54
38
-`SCVec` / `SCMap` contract arguments are not yet encoded.
55
-
-`traceTransaction` is a komet-specific extension, absent from real Stellar RPC (see [server.md](server.md#tracetransaction-komet-specific-extension)).
39
+
- The `xdrFormat` parameter is accepted on `getTransaction` and `sendTransaction`, but only the default `'base64'` is supported; `'json'` is rejected with `-32602`.
40
+
-`simulateTransaction`, `getEvents`, `getLedgerEntries`, `getFeeStats`, and TTL/footprint operations are not implemented.
41
+
- Receipts are not a stable format: older io-dirs store `ledger`/`createdAt` with pre-spec types and lack `applicationOrder`/`feeBump`. Resume only io-dirs written by the same version, or start fresh.
For `sendTransaction` it builds the request envelope with `encoder.build_tx_request` and runs it with `interpreter.run`; for the read-only methods (`getHealth`, `getNetwork`, `getLatestLedger`, `getTransaction`, `traceTransaction`) it builds a small envelope and runs it. In every case the *content* of the response is produced by the semantics (`node.md`), not by Python — the one exception is the failure fallback (below). Each call is logged to stderr.
45
+
For `sendTransaction` it builds the request envelope with `encoder.build_tx_request` and runs it with `interpreter.run` (skipping the `<program>` wasm injection when the hash already has a receipt, so a duplicate is not re-executed — the semantics answer `DUPLICATE`); for the read-only methods (`getHealth`, `getNetwork`, `getLatestLedger`, `getTransaction`, `traceTransaction`) it builds a small envelope and runs it. In every case the *content* of the response is produced by the semantics (`node.md`), not by Python — the one exception is the failure fallback (below). Each call is logged to stderr.
46
46
47
47
---
48
48
@@ -97,24 +97,30 @@ Because these artifacts live on disk, the server can be stopped and restarted wi
97
97
98
98
## RPC methods
99
99
100
-
All methods are answered by the K semantics. All follow the [Stellar RPC specification](https://developers.stellar.org/docs/data/apis/rpc/methods) except `traceTransaction`, which is a komet-specific extension (see below).
100
+
All methods are answered by the K semantics and follow the [Stellar RPC specification](https://developers.stellar.org/docs/data/apis/rpc/methods)— except `traceTransaction`, which is a komet-specific extension (see below) — including its serialization quirks: ledger sequence numbers and `protocolVersion` are JSON numbers, while close-time fields (`latestLedgerCloseTime`, `oldestLedgerCloseTime`, `createdAt`) are int64s serialized as JSON strings holding a decimal integer, matching what real stellar-rpc emits.
The node retains every ledger since genesis, so `oldestLedger` is always 0 and the retention window covers all ledgers so far. Per-ledger close times are not recorded: the latest close time is approximated by the request time and the oldest by the epoch.
`friendbotUrl` is omitted (not `null`) because the node runs no friendbot — matching real stellar-rpc's `omitempty` serialization.
117
+
112
118
### `getLatestLedger`
113
119
114
-
`getLatestLedger` returns the current ledger sequence (the `latest_ledger` from `metadata.json`), which increments by 1 per successfully committed transaction.
120
+
`getLatestLedger` returns the current ledger sequence (the `latest_ledger` from `metadata.json`), which increments by 1 per successfully committed transaction. The `id` is a deterministic per-ledger stand-in for the ledger-header hash, derived from the sequence (see `#ledgerId` in [node-semantics.md](node-semantics.md)).
Resubmitting a transaction whose hash already has a receipt returns status `DUPLICATE` without re-executing it: the ledger does not advance and the stored receipt is untouched.
138
+
139
+
A transaction that decodes as XDR but cannot be processed (e.g. it contains an operation the semantics do not support) is rejected at admission time with status `ERROR` and an `errorResultXdr` carrying a `txMALFORMED``TransactionResult` — mirroring how real stellar-rpc reports core's admission rejections. Such a transaction never reaches the ledger: no receipt is stored (`getTransaction` stays `NOT_FOUND`) and the ledger does not advance. Undecodable XDR remains a plain `-32602 Invalid params` error, as in real stellar-rpc. `TRY_AGAIN_LATER` is never returned: it signals mempool backpressure, and komet-node executes synchronously without a mempool, so the condition it reports cannot arise.
140
+
131
141
### `traceTransaction` (komet-specific extension)
132
142
133
143
`traceTransaction` is **not part of the Stellar RPC specification** — it exists only on komet-node, and clients must not expect it from real Stellar RPC endpoints. It keeps its plain name rather than a vendor-prefixed one (`komet_traceTransaction`): the official spec has no method of that name and none is announced, so there is no collision to avoid, and renaming would break every existing client for no gain. If stellar-rpc ever claims the name, the method will be renamed with a prefix.
@@ -140,7 +150,7 @@ All methods are answered by the K semantics. All follow the [Stellar RPC specifi
140
150
141
151
### `getTransaction`
142
152
143
-
`getTransaction` reads the hash's `receipts/receipt_<hash>.json` file.
153
+
`getTransaction` reads the hash's `receipts/receipt_<hash>.json` file. The `hash` parameter must be a 64-character hex string; anything else is rejected with `-32602 Invalid params` (this and `traceTransaction` share the validation).
144
154
145
155
| Status | Meaning |
146
156
|---|---|
@@ -151,13 +161,17 @@ All methods are answered by the K semantics. All follow the [Stellar RPC specifi
`resultXdr` and `resultMetaXdr` are currently empty stubs. The receipt carries no trace — use `traceTransaction` with the same hash to fetch it.
172
+
The ledger-range fields (`latestLedger` through `oldestLedgerCloseTime`) are present on every response, `NOT_FOUND` included. Every ledger contains exactly one transaction, so `applicationOrder` is always 1; fee-bump envelopes are not supported, so `feeBump` is always false. `resultXdr` and `resultMetaXdr` are currently empty stubs. The receipt carries no trace — use `traceTransaction` with the same hash to fetch it.
173
+
174
+
Receipts are an internal format, not a stable one: receipts written by older komet-node versions stored `ledger` as a string and lack `applicationOrder`/`feeBump`, and `getTransaction` returns stored receipts verbatim. Start from a fresh io-dir after upgrading rather than resuming one with old receipts.
0 commit comments