Skip to content

Commit f337764

Browse files
docs: overhaul documentation and ignore runtime artifacts
Refine README, docs/, and in-code comments for clarity and accuracy: remove a self-referential link, correct the "empty state.kore" description, drop a filler preamble, and clarify that json.md is K's built-in module. Ignore the server/interpreter runtime artifacts (state.kore, metadata.json, transactions.json, response.json, trace.jsonl) so they no longer show up as untracked. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 9117428 commit f337764

12 files changed

Lines changed: 78 additions & 64 deletions

File tree

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,10 @@
22
__pycache__/
33
.coverage
44
.direnv/
5+
6+
# Runtime artifacts written by the server / interpreter in the io dir
7+
metadata.json
8+
response.json
9+
state.kore
10+
trace.jsonl
11+
transactions.json

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
## 🌟 Overview
1818

19-
`komet-node` extends the standard Stellar RPC with a `traceTransaction` method that provides instruction-level execution traces. The node's state is persisted to disk, allowing the same ledger state to be reproduced and transactions to be replayed. The RPC logic itself runs inside the K formal semanticsPython is only a thin shim that decodes Stellar XDR.
19+
`komet-node` extends the standard Stellar RPC with a `traceTransaction` method that provides instruction-level execution traces. The node's state is persisted to disk, allowing the same ledger state to be reproduced and transactions to be replayed. The RPC logic itself runs inside the K formal semantics. The compiled semantics are a one-shot interpreter — given a state, it runs a single request to completion and exits — so a small Python program wraps it into a long-running server: it holds the HTTP connection, keeps the ledger state on disk between runs, and decodes Stellar's binary XDR transaction format, which K cannot read.
2020

2121
## 🚀 Quick Start
2222

@@ -75,7 +75,7 @@ komet-node --trace # enable instruction-level execution tracing
7575
| `--state-file` | `state.kore` | Path to the persistent state file |
7676
| `--trace` | off | Enable instruction-level execution tracing |
7777

78-
On first start the server creates an empty `state.kore`, alongside `metadata.json` (the ledger counter) and `transactions.json` (the transaction store). Delete `state.kore` to reset the chain (the sidecar files are re-seeded), or point `--state-file` at a pre-built configuration to resume from a snapshot.
78+
On first start the server creates a fresh `state.kore` (the idle configuration — an empty chain), alongside `metadata.json` (the ledger counter) and `transactions.json` (the transaction store). Delete `state.kore` to reset the chain (the sidecar files are re-seeded), or point `--state-file` at a pre-built configuration to resume from a snapshot.
7979

8080
#### Verify the server with `curl`
8181

@@ -174,7 +174,7 @@ This produces `state.kore` plus `state_<n>_<step>.pretty` files under `./out`, l
174174

175175
## For Developers
176176

177-
Prerequisites: `python >= 3.10`, [`uv`](https://docs.astral.sh/uv/), [`wabt`](https://github.com/WebAssembly/wabt) (for `wat2wasm`), and the K Framework. The [Dev Container](#for-developers) provisions all of these for you.
177+
Prerequisites: `python >= 3.10`, [`uv`](https://docs.astral.sh/uv/), [`wabt`](https://github.com/WebAssembly/wabt) (for `wat2wasm`), and the K Framework. The Dev Container provisions all of these for you.
178178

179179
1. Install [VS Code](https://code.visualstudio.com/) and the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers).
180180
2. Open this repository in VS Code and choose **Reopen in Container** when prompted.
@@ -201,14 +201,15 @@ Common tasks are driven by `make` (see the [Makefile](Makefile) for the complete
201201
To build the node from source use:
202202

203203
```bash
204+
make build-kdist
204205
make build
205206
pip install dist/*.whl
206207
```
207208

208209
### Documentation
209210

210211
- [Architecture overview](docs/architecture.md) — how the pieces fit together
211-
- [Server](docs/server.md) — the HTTP/RPC shim, state lifecycle, and full method reference
212+
- [Server](docs/server.md) — the long-running HTTP server that wraps the K interpreter, state lifecycle, and full method reference
212213
- [Transaction encoding](docs/transaction.md) — Stellar XDR → K request envelope
213214
- [Interpreter](docs/interpreter.md) — running request envelopes through the K semantics
214215
- [K semantics](docs/node-semantics.md) — the on-chain RPC dispatch and execution model

docs/architecture.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22

33
## Overview
44

5-
komet-node is a local Stellar testnet whose execution engine is the [K formal semantics](https://github.com/runtimeverification/komet) of Soroban. Rather than running a real Stellar validator, it decodes incoming Stellar transactions into K steps and executes them through the compiled K semantics according to the formal Soroban specification.
5+
komet-node is a local Stellar testnet whose execution engine is the [K formal semantics](https://github.com/runtimeverification/komet) of Soroban. It decodes incoming Stellar transactions into K steps and executes them through the compiled semantics.
66

7-
The design keeps Python as a thin shim and pushes everything that is *grounded in the formal semantics* into K. Python only does what K cannot: decode the Stellar XDR envelope (and parse uploaded wasm). Everything else — RPC method dispatch, the transaction store, ledger-sequence accounting, status determination, and JSON-RPC response formatting — lives in the K semantics (`node.md`).
7+
The split between Python and K follows one rule: K does everything that is part of the Soroban/Stellar protocol, and Python does only what K structurally cannot. The compiled K semantics are a *one-shot interpreter* — one invocation runs one request to completion and exits, with no network, no memory between invocations, and no decoder for Stellar's binary XDR format. Python supplies exactly those three missing pieces: it is the long-running process that holds the HTTP socket, it keeps the world state on disk between invocations, and it decodes the XDR envelope (and parses uploaded wasm). Everything else — RPC method dispatch, the transaction store, ledger-sequence accounting, status determination, and JSON-RPC response formatting — runs inside the K semantics (`node.md`).
88

99
```
1010
Stellar client
1111
│ JSON-RPC request (base64 XDR transaction)
1212
13-
StellarRpcServer ← server.py (raw http.server, no business logic)
13+
StellarRpcServer ← server.py (long-running http.server; holds the socket + state files)
1414
1515
├─ TransactionEncoder ← transaction.py
1616
│ XDR → request envelope (+ kasmer steps for wasm uploads)
@@ -31,37 +31,37 @@ StellarRpcServer ← server.py (raw http.server, no busines
3131

3232
### `server.py``StellarRpcServer`
3333

34-
A raw `http.server.HTTPServer` shim. It receives JSON-RPC requests, uses `TransactionEncoder` to turn a transaction into a request envelope, hands the envelope to `NodeInterpreter`, and returns the `response.json` the semantics produced. It holds **no** ledger counter, transaction store, or response-formatting logic — those now live in K.
34+
`StellarRpcServer` is the long-running process around the semantics: a plain `http.server.HTTPServer` that keeps the HTTP socket open and the state files on disk across requests — the networking and persistence the one-shot K interpreter has no notion of. It receives JSON-RPC requests, uses `TransactionEncoder` to turn a transaction into a request envelope, hands the envelope to `NodeInterpreter`, and returns the `response.json` the semantics produced. It holds **no** ledger counter, transaction store, or response-formatting logic — those live in K.
3535

3636
`handle_rpc(method, params, id)` is the dispatch entry point and is usable without the HTTP layer (scripts, tests).
3737

3838
**[Detailed documentation](server.md)**
3939

40-
Implemented RPC methods: `getHealth`, `getNetwork`, `getLatestLedger`, `sendTransaction`, `getTransaction`, `traceTransaction`. All are answered by the K semantics.
40+
The server implements six RPC methods`getHealth`, `getNetwork`, `getLatestLedger`, `sendTransaction`, `getTransaction`, and `traceTransaction` — and the K semantics answer all of them.
4141

4242
`sendTransaction` always returns `PENDING` and clients poll `getTransaction` for the result — matching the Stellar RPC async pattern even though the transaction executes synchronously. See [server.md](server.md) for details.
4343

4444
---
4545

4646
### `transaction.py``TransactionEncoder`
4747

48-
The XDR boundary. Decodes a `stellar_sdk` transaction envelope into a JSON request envelope: the RPC method, the transaction hash, the envelope XDR, and the decoded operations as JSON "steps". For the one case K cannot consume as JSON — a wasm upload, whose `ModuleDecl` has no JSON form — it produces the kasmer steps in K-AST form for direct injection into the `<program>` cell.
48+
`TransactionEncoder` is the XDR boundary. It decodes a `stellar_sdk` transaction envelope into a JSON request envelope containing the RPC method, the transaction hash, the envelope XDR, and the decoded operations as JSON "steps". For the one case K cannot consume as JSON — a wasm upload, whose `ModuleDecl` has no JSON form — it produces the kasmer steps in K-AST form for direct injection into the `<program>` cell.
4949

5050
**[Detailed documentation](transaction.md)**
5151

5252
---
5353

5454
### `interpreter.py``NodeInterpreter`
5555

56-
The K-execution boundary. Builds the initial configuration, runs a request envelope through the LLVM interpreter against `state.kore`, and persists the resulting state. It knows nothing about Stellar. It performs **no** whole-configuration `kast``kore` conversions — the initial config is built directly in KORE and request steps are spliced into the `<program>` cell at the KORE level.
56+
`NodeInterpreter` is the K-execution boundary. It builds the initial configuration, runs a request envelope through the LLVM interpreter against `state.kore`, and persists the resulting state. It knows nothing about Stellar. It performs **no** whole-configuration `kast``kore` conversions — the initial config is built directly in KORE and request steps are spliced into the `<program>` cell at the KORE level.
5757

5858
**[Detailed documentation](interpreter.md)**
5959

6060
---
6161

6262
### `kdist/node.md` — K Semantics
6363

64-
The K module compiled into the LLVM binary. Implements the whole RPC layer on the K side: reads `request.json`, dispatches on the `method` field, reads/updates the bookkeeping files (`metadata.json`, `transactions.json`), executes transaction steps via KASMER, and writes the JSON-RPC `response.json`.
64+
`node.md` is the K module compiled into the LLVM binary. It implements the whole RPC layer on the K side: it reads `request.json`, dispatches on the `method` field, reads and updates the bookkeeping files (`metadata.json`, `transactions.json`), executes transaction steps via KASMER, and writes the JSON-RPC `response.json`.
6565

6666
**[Detailed documentation](node-semantics.md)**
6767

@@ -96,7 +96,7 @@ per failed (stuck) transaction:
9696
ledger is not bumped. The server synthesises a FAILED receipt.
9797
```
9898

99-
Because all three files live on disk, the server can be stopped and restarted without losing state — the ledger counter and transaction store now survive restarts. To resume from a session, point `--state-file` at a saved `state.kore` (its sidecar files are read if present). To start fresh, delete the files.
99+
Because all three files live on disk, the server can be stopped and restarted without losing the world state, the ledger counter, or the transaction store. To resume a session, point `--state-file` at a saved `state.kore` (its sidecar files are read if present). To start fresh, delete the files.
100100

101101
---
102102

docs/interpreter.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,36 +11,39 @@ class NodeInterpreter:
1111
definition: SimbolikDefinition # compiled K definition (komet-node.simbolik)
1212
```
1313

14-
`SimbolikDefinition` is a thin subclass of `komet.SorobanDefinition`, pointing to the `komet-node.simbolik` compiled K definition (cached under `~/.cache/kdist-*/komet-node/simbolik/`). Note there is no `network_passphrase` or `trace` here — those belong to the request side (`TransactionEncoder`); the interpreter is purely about running K.
14+
`SimbolikDefinition` is a thin subclass of `komet.SorobanDefinition`, pointing to the `komet-node.simbolik` compiled K definition (cached under `~/.cache/kdist-*/komet-node/simbolik/`). There is no `network_passphrase` or `trace` here — those belong to the request side (`TransactionEncoder`); the interpreter only runs K.
1515

1616
---
1717

1818
## No `kast``kore` conversions
1919

20-
A guiding constraint: whole-configuration `kore_to_kast` / `kast_to_kore` conversions take seconds and get slower as the configuration grows, so the interpreter avoids them entirely. `state.kore` is only ever parsed with `KoreParser` (KORE text → KORE AST, which is cheap) and handed straight to `llvm_interpret`. Terms that must be constructed are built directly in KORE.
20+
Whole-configuration `kore_to_kast` / `kast_to_kore` conversions take seconds and get slower as the configuration grows, so the interpreter avoids them entirely. `state.kore` is only ever parsed with `KoreParser` (KORE text → KORE AST, which is cheap) and handed straight to `llvm_interpret`. Terms that must be constructed are built directly in KORE.
2121

2222
### `empty_config()`
2323

24-
Produces the initial blank-slate `state.kore`. It builds the top-cell initializer **in KORE** — seeding `$PGM` with a single `setExitCode(0)` step and `$TRACE` with an empty string — and runs it through `llvm_interpret`. No `krun` subprocess and no kast conversion are involved.
24+
`empty_config()` produces the initial blank-slate `state.kore`. It builds the top-cell initializer **in KORE** — seeding `$PGM` with a single `setExitCode(0)` step and `$TRACE` with an empty string — and runs it through the interpreter. No `krun` subprocess and no kast conversion are involved.
2525

2626
```python
2727
config = top_cell_initializer({
2828
'$PGM': inj(SortSteps, K_ITEM, kasmerSteps(setExitCode(0), .Steps)), # built in KORE
2929
'$TRACE': inj(SortString, K_ITEM, str_dv('')),
3030
})
31-
return llvm_interpret(self.definition.path, config, check=False).text
31+
with tempfile.TemporaryDirectory() as isolated_dir: # see note below
32+
return _llvm_interpret(self.definition.path, config, cwd=isolated_dir).text
3233
```
3334

3435
The result is the empty idle K configuration — no accounts, no contracts, no storage.
3536

37+
The run happens in a throwaway empty directory on purpose. The idle configuration ends with empty `<k>`/`<program>` cells, which is exactly the precondition that makes the request-handling rule fire if a `request.json` is present. Running in an empty directory guarantees no stray `request.json` is picked up and dispatched into the configuration that is about to be saved as `state.kore`.
38+
3639
### `run(state_file, io_dir, request, program_steps=None)`
3740

38-
The main entry point. Runs a single RPC request envelope:
41+
`run` is the main entry point. It runs a single RPC request envelope through the following steps:
3942

4043
1. Write the request envelope to `request.json` in `io_dir`, and delete any stale `response.json`.
4144
2. Parse `state.kore` with `KoreParser` (no kast conversion).
4245
3. For a wasm upload only, splice the upload steps into the `<program>` cell (see below).
43-
4. `chdir` into `io_dir` (so the K file-system hooks resolve the relative paths `request.json`, `response.json`, `metadata.json`, `transactions.json`, `trace.jsonl`) and call `llvm_interpret`.
46+
4. Run the interpreter with its subprocess working directory set to `io_dir` (so the K file-system hooks resolve the relative paths `request.json`, `response.json`, `metadata.json`, `transactions.json`, `trace.jsonl`). The directory is set on the subprocess only — the server's own process never `chdir`s, so concurrent requests in other threads are unaffected.
4447
5. If the semantics wrote `response.json`, persist the new configuration to `state.kore` and return the response text. If not, the transaction got stuck (failed) — leave `state.kore` unchanged and return `None`, so the caller can synthesise a failure response.
4548

4649
### `_inject_program(pattern, steps)` — the wasm-upload path
@@ -58,7 +61,7 @@ Because Soroban allows only a single host-function operation per transaction, a
5861

5962
### `pretty_print(kore_str)`
6063

61-
A debugging helper that pretty-prints a KORE configuration string using `krun --output pretty --depth 0`. Used by `demo.py` to render each step of a contract lifecycle.
64+
`pretty_print` is a debugging helper that pretty-prints a KORE configuration string using `krun --output pretty --depth 0`. `demo.py` uses it to render each step of a contract lifecycle.
6265

6366
---
6467

@@ -77,4 +80,4 @@ The mapping from Stellar operations to kasmer steps is performed by [`Transactio
7780

7881
## Error handling
7982

80-
`NodeInterpreterError` is raised for interpreter-level failures (e.g. `pretty_print`). A *transaction* failure is not an exception: the semantics simply get stuck without writing `response.json`, so `run` returns `None` and the server records a `FAILED` receipt while leaving `state.kore` unchanged (the state effectively rolls back).
83+
`NodeInterpreterError` is raised for interpreter-level failures (e.g. `pretty_print`). A *transaction* failure is not an exception: the semantics get stuck without writing `response.json`, so `run` returns `None` and the server records a `FAILED` receipt while leaving `state.kore` unchanged (the state effectively rolls back).

0 commit comments

Comments
 (0)