A local Stellar testnet node based on K formal semantics of Soroban.
Installation β’ Usage β’ Contribute
komet-node is a Stellar testnet node you run on your own machine. It serves the standard Stellar RPC API, so the SDKs, wallets, and tooling you already use with Stellar work against it unchanged β you point them at localhost instead of a public network.
It is built for developing, testing, and debugging Soroban contracts locally, and adds two capabilities a public Stellar network does not offer:
- Instruction-level traces. Every transaction is traced as it runs, and the
traceTransactionmethod returns that step-by-step record of every WebAssembly instruction the contract executed, so you can see exactly what happened β and where it went wrong. - Reproducible, replayable state. The ledger state is persisted to disk, so you can stop and restart the node, save a state, and replay transactions against it to reproduce a result.
komet-node is distributed through kup, Runtime Verification's Nix-based package manager. It pulls prebuilt binaries (including the matching K Framework and compiled semantics) from RV's binary cache, so there is nothing to compile.
# 1. Install the kup package manager (one time)
bash <(curl https://kframework.org/install)
# 2. Install komet-node
kup install komet-node
# 3. Verify the installation
komet-node --helpTo upgrade later, run kup update komet-node.
Alternatively, a prebuilt image is published to Docker Hub for each release. It bundles K, the compiled semantics, and komet-node ready to serve.
# Pull the image (replace the tag with the release you want)
docker pull runtimeverificationinc/komet-node:ubuntu-jammy-0.1.0
# Start the server, exposing the RPC port on the host
docker run --rm -p 8000:8000 \
runtimeverificationinc/komet-node:ubuntu-jammy-0.1.0 \
komet-node --host 0.0.0.0 --port 8000The server binds to
localhostby default; pass--host 0.0.0.0inside the container so the port is reachable from the host.
Run komet-node to start the server; komet-node --help prints the full usage:
usage: komet-node [-h] [--host HOST] [--port PORT] [--io-dir IO_DIR]
Komet Node β a local Stellar testnet backed by the K semantics of Soroban.
options:
-h, --help show this help message and exit
--host HOST bind address (default: localhost)
--port PORT port to listen on (default: 8000)
--io-dir IO_DIR directory for all input/output artifacts (default: a fresh
temporary directory)
examples:
komet-node serve on localhost:8000 in a fresh temp dir
komet-node --port 9000 use a custom port
komet-node --io-dir ./chain keep all artifacts under ./chain (persistent)
komet-node --host 0.0.0.0 accept connections from outside localhost
Every submitted transaction is traced as it executes, and the instruction-level trace is stored on its receipt. traceTransaction retrieves that stored trace, looked up by transaction hash β the same hash getTransaction takes. So tracing a contract invocation is two calls: sendTransaction to run it, then traceTransaction with the returned hash. Tracing is always on; there is no flag to enable.
Submitting transactions uses the standard two-step Stellar pattern β sendTransaction with a base64 XDR envelope, then poll getTransaction by hash. Because there is no mempool, komet-node executes the transaction synchronously inside sendTransaction, so the result is already available by the time you poll. See docs/server.md for the full RPC reference.
A trace requires a deployed contract. The four envelopes below are pre-built and signed (a tiny contract whose foo() returns void, deployed from a fixed key) so you can paste them straight in β the local node does not check signatures, sequence numbers, or timebounds, so they work as-is on a fresh chain. After a quick health check, run them in order against the server started above.
# Is the server alive?
curl -s http://localhost:8000 -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"getHealth","params":{}}'
# => {"jsonrpc":"2.0","id":1,"result":{"status":"healthy"}}
# 1. Create the deployer account
curl -s http://localhost:8000 -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"sendTransaction","params":{"transaction":"AAAAAgAAAAADoQe/884Qvh1w3RjnS8CZZ+TWMJulDV8d3IZkElUxuAAAAGQAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAADoQe/884Qvh1w3RjnS8CZZ+TWMJulDV8d3IZkElUxuAAAAAJUC+QAAAAAAAAAAAESVTG4AAAAQMOMXdUuK9E9tF0pgpqX+z+nXFlE6Mn5e7rqOFL8jIolInsXc7XHPgvYs4VWDqlCGI/fom9SpYiHOQYUqKTvDAc="}}'
# 2. Upload the contract wasm
curl -s http://localhost:8000 -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"sendTransaction","params":{"transaction":"AAAAAgAAAAADoQe/884Qvh1w3RjnS8CZZ+TWMJulDV8d3IZkElUxuAAAAGQAAAAAAAAAAgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAGAAAAAIAAABzAGFzbQEAAAABCAJgAAF+YAAAAwMCAAEFAwEAEAYZA38BQYCAwAALfwBBgIDAAAt/AEGAgMAACwcvBQZtZW1vcnkCAANmb28AAAFfAAEKX19kYXRhX2VuZAMBC19faGVhcF9iYXNlAwIKCQIEAEICCwIACwAAAAAAAAAAAAAAAAESVTG4AAAAQOk89R0Qlko4dCBI3XziT3XTjdm4kyKtpy9ky3uVksIYsSFWXKHTHOiCDaxNKdecQKbhQnD/9ELWxxr98D5ecQ4="}}'
# 3. Deploy a contract instance
curl -s http://localhost:8000 -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"sendTransaction","params":{"transaction":"AAAAAgAAAAADoQe/884Qvh1w3RjnS8CZZ+TWMJulDV8d3IZkElUxuAAAAGQAAAAAAAAAAwAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAGAAAAAMAAAAAAAAAAAAAAAADoQe/884Qvh1w3RjnS8CZZ+TWMJulDV8d3IZkElUxuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFdPOLtg6vmmrgodRyN6P3wk1UfHrQxVekpbnsYYOcpvAAAAAAAAAAAAAAAAAAAAARJVMbgAAABAnLtNirBI7XdD2xwH3ws3rTDEhCxJ8mCRNU66d7b4MR2Ih9WtZzqb6akBqK6yA1GIavzVa7ahq2FNBflk+JpOBg=="}}'
# 4. Invoke foo() β sendTransaction runs it and returns its hash
curl -s http://localhost:8000 -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"sendTransaction","params":{"transaction":"AAAAAgAAAAADoQe/884Qvh1w3RjnS8CZZ+TWMJulDV8d3IZkElUxuAAAAGQAAAAAAAAABAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAGAAAAAAAAAABaiD+wakIF3Ol8jzjcPkl8jY0blEEON3W1A9rJxHBNOAAAAADZm9vAAAAAAAAAAAAAAAAAAAAAAESVTG4AAAAQKB9w/QmdK59UzXVbxXJp+5qfNpFSa495yajOyPM5KmYblE3/AbWqnnZMxTiBea0ShGZehgvo12AIyw48Lb1Xw0="}}'
# => {"jsonrpc":"2.0","id":1,"result":{"hash":"c7099cbe10a9bfa1cdf9c9d368e1e1c932f535a70e4403b7aa409ce19fc36805","status":"PENDING", ...}}
# 5. Retrieve the trace for that hash
curl -s http://localhost:8000 -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"traceTransaction","params":{"hash":"c7099cbe10a9bfa1cdf9c9d368e1e1c932f535a70e4403b7aa409ce19fc36805"}}'traceTransaction returns the stored trace as its result. The trace is a JSONL string (one JSON record per executed WebAssembly instruction); it is shown decoded here for readability:
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 for the full trace format.
The bundled demo deploys and invokes a Soroban contract end-to-end (create account β upload wasm β deploy β invoke):
uv run python -m komet_node.demo src/tests/integration/data/wasm/empty.watThis produces state.kore plus state_<n>_<step>.pretty files under ./out, letting you inspect exactly how the ledger state evolves at each step. (Requires wat2wasm from wabt on your PATH.)
Prerequisites: python >= 3.10, uv, wabt (for wat2wasm), and the K Framework. The Dev Container provisions all of these for you.
-
Install VS Code and the Dev Containers extension.
-
Open this repository in VS Code and choose Reopen in Container when prompted.
-
Once the container finishes building, build the semantics and run the test suite:
make kdist-build # compile the K semantics (first run only; takes a while) make test-unit # quick sanity check
Common tasks are driven by make (see the Makefile for the complete list):
| Target | Description |
|---|---|
make kdist-build |
Compile the K semantics (required before running integration tests) |
make build |
Build the wheel |
make test-unit |
Run unit tests |
make test-integration |
Run integration tests |
make test |
Run the full test suite |
make cov |
Run tests with a coverage report |
make check |
Run all style/type checks (flake8, mypy, autoflake, isort, black) |
make format |
Auto-format the codebase |
To build the node from source use:
make build-kdist
make build
pip install dist/*.whl- Architecture overview β how the pieces fit together
- Server β the long-running HTTP server that wraps the K interpreter, plus the state lifecycle and the full method reference
- Transaction encoding β Stellar XDR β K request envelope
- Interpreter β running request envelopes through the K semantics
- K semantics β the on-chain RPC dispatch and execution model
komet-node is developed by Runtime Verification. It builds on Komet, the K semantics of Soroban smart contracts, and the K Framework.
{ "jsonrpc": "2.0", "id": 1, "result": [ {"pos": 3, "instr": ["const", "i32", 1048576], "stack": [], "locals": {}}, {"pos": 11, "instr": ["const", "i32", 1048576], "stack": [], "locals": {}}, {"pos": 19, "instr": ["const", "i32", 1048576], "stack": [], "locals": {}}, {"pos": null, "instr": ["block"], "stack": [], "locals": {}}, {"pos": 3, "instr": ["const", "i64", 2], "stack": [], "locals": {}} ] }