Skip to content

Commit 94f2dc3

Browse files
feat: implement getVersionInfo and getFeeStats
Add the two parameterless read-only methods per the Stellar RPC spec. getVersionInfo reports the komet-node package version, all-zeros commit hash and epoch build timestamp (nothing is baked in at build time), the komet package as the Captive Core, and protocolVersion 22 as a JSON number. getFeeStats returns constant fee distributions (there is no fee market; every statistic is the 100-stroop network minimum over an empty sample) with the stellar-rpc wire types — all fields except ledgerCount as decimal strings — and a live latestLedger number from metadata.json. Dispatch and response formatting live in node.md; server.py only supplies the version strings, which come from Python package metadata that K cannot read.
1 parent 521b69a commit 94f2dc3

6 files changed

Lines changed: 103 additions & 4 deletions

File tree

docs/architecture.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ flowchart TB
5151

5252
**[Detailed documentation](server.md)**
5353

54-
The server implements six RPC methods — `getHealth`, `getNetwork`, `getLatestLedger`, `sendTransaction`, `getTransaction`, and `traceTransaction` — and the K semantics answer all of them.
54+
The server implements eight RPC methods — `getHealth`, `getNetwork`, `getLatestLedger`, `getVersionInfo`, `getFeeStats`, `sendTransaction`, `getTransaction`, and `traceTransaction` — and the K semantics answer all of them.
5555

5656
`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.
5757

@@ -177,6 +177,6 @@ sequenceDiagram
177177

178178
- `resultXdr` / `resultMetaXdr` in `getTransaction` responses (contract return values)
179179
- `simulateTransaction` (dry-run without state mutation)
180-
- `getEvents`, `getLedgerEntries`, `getFeeStats` and other read-only RPC methods
180+
- `getEvents`, `getLedgerEntries`, `getTransactions`, `getLedgers` and other read-only RPC methods
181181
- `ExtendFootprintTTL` and `RestoreFootprint` operations
182182
- `SCVec` / `SCMap` contract-argument types in the request encoder (`scval_to_json`)

docs/node-semantics.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ insert-handleRequestFile → handleRequestFile
3939
4040
#dispatchMethod(method, request) ← routes on the "method" field
4141
42-
├─ getHealth / getNetwork / getLatestLedger / getTransaction / traceTransaction → #respond(...)
42+
├─ getHealth / getNetwork / getLatestLedger / getVersionInfo / getFeeStats
43+
│ / getTransaction / traceTransaction → #respond(...)
4344
4445
└─ sendTransaction → #runTx → run steps
4546
→ #finalizeTx → record receipt + bump ledger → #respond(...)
@@ -61,6 +62,8 @@ If `request.json` is absent, `insert-handleRequestFile` does not fire and K halt
6162
- `getHealth``{ "status": "healthy" }`
6263
- `getNetwork``{ "friendbotUrl": null, "passphrase": ..., "protocolVersion": ... }` (passphrase/version come from the request, keeping the semantics network-agnostic)
6364
- `getLatestLedger` → reads `metadata.json` and returns `{ "id": <64 zeros>, "protocolVersion": ..., "sequence": <latest_ledger> }`
65+
- `getVersionInfo` → echoes the version fields the server put into the envelope (`version`, `commitHash`, `buildTimestamp`, `captiveCoreVersion` — package metadata lives on the Python side); `protocolVersion` is a JSON number
66+
- `getFeeStats` → constant `#feeDistribution` objects (komet-node has no fee market) for `sorobanInclusionFee`/`inclusionFee`, plus a live `latestLedger` number from `metadata.json`; all distribution fields except `ledgerCount` are decimal strings, matching real stellar-rpc's Go `,string` encoding
6467
- `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
6568

6669
`#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.

docs/notes.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,5 @@ The tests do not yet cover `bytes` / `address` SCVal arguments or `SCVec` / `SCM
3535

3636
- `resultXdr` / `resultMetaXdr` are empty stubs (contract return values not surfaced).
3737
- `SCVec` / `SCMap` contract arguments are not yet encoded.
38-
- `simulateTransaction`, `getEvents`, `getLedgerEntries`, `getFeeStats`, and TTL/footprint operations are not implemented.
38+
- `simulateTransaction`, `getEvents`, `getLedgerEntries`, `getTransactions`, `getLedgers`, and TTL/footprint operations are not implemented.
39+
- `getFeeStats` reports constant distributions (there is no fee market); only its `latestLedger` is live.

docs/server.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,22 @@ All methods are answered by the K semantics and follow the [Stellar RPC specific
103103
{ "id": "0000...0000", "protocolVersion": "22", "sequence": 4 }
104104
```
105105

106+
### `getVersionInfo`
107+
108+
`getVersionInfo` reports the komet-node package version as the RPC server version and the komet package (the K semantics of Soroban executing the transactions) as the "Captive Core". komet-node is a Python package, so no commit hash or build timestamp is baked in at build time — those fields are all-zeros / epoch placeholders with the spec-correct types. `protocolVersion` is a JSON number.
109+
110+
```json
111+
{ "version": "0.1.0", "commitHash": "0000...0000", "buildTimestamp": "1970-01-01T00:00:00", "captiveCoreVersion": "komet 0.1.79 (K semantics of Soroban)", "protocolVersion": 22 }
112+
```
113+
114+
### `getFeeStats`
115+
116+
`getFeeStats` returns constant fee distributions: komet-node has no fee market, so every statistic is the network minimum inclusion fee of 100 stroops over an empty sample (`transactionCount: "0"`, `ledgerCount: 0`). Matching real stellar-rpc, every distribution field except `ledgerCount` is a JSON string holding a decimal number; `latestLedger` is a JSON number read live from `metadata.json`.
117+
118+
```json
119+
{ "sorobanInclusionFee": { "max": "100", "min": "100", "mode": "100", "p10": "100", ..., "p99": "100", "transactionCount": "0", "ledgerCount": 0 }, "inclusionFee": { ... }, "latestLedger": 4 }
120+
```
121+
106122
### `sendTransaction`
107123

108124
`sendTransaction` submits a base64-encoded XDR transaction envelope.

src/komet_node/kdist/node.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,62 @@ rule. `#respond(ID, RESULT)` writes the JSON-RPC envelope to `response.json`, re
203203
</k>
204204
```
205205

206+
`getVersionInfo` echoes the version strings the Python server put into the request envelope
207+
(the package versions live in Python's package metadata, which K cannot read). Its
208+
`protocolVersion` is a JSON number, per the spec and real stellar-rpc.
209+
210+
```k
211+
rule <k> #dispatchMethod( "getVersionInfo", REQ )
212+
=> #respond( #getJSON( "id", REQ ), {
213+
"version" : #getString( "version", REQ ),
214+
"commitHash" : #getString( "commitHash", REQ ),
215+
"buildTimestamp" : #getString( "buildTimestamp", REQ ),
216+
"captiveCoreVersion" : #getString( "captiveCoreVersion", REQ ),
217+
"protocolVersion" : #getInt( "protocolVersion", REQ )
218+
})
219+
...
220+
</k>
221+
```
222+
223+
`getFeeStats` reports a constant fee distribution: komet-node has no fee market (transactions
224+
execute immediately and pay no fees), so every statistic is the network minimum inclusion fee
225+
of 100 stroops over an empty sample. Real stellar-rpc serialises every distribution field
226+
except `ledgerCount` with Go's `,string` option, so those are JSON strings holding decimal
227+
numbers, while `ledgerCount` and `latestLedger` are JSON numbers. `latestLedger` is read live
228+
from `metadata.json`.
229+
230+
```k
231+
rule <k> #dispatchMethod( "getFeeStats", REQ )
232+
=> #respond( #getJSON( "id", REQ ), {
233+
"sorobanInclusionFee" : #feeDistribution,
234+
"inclusionFee" : #feeDistribution,
235+
"latestLedger" : #getInt( "latest_ledger", String2JSON( {#readFile("metadata.json")}:>String ) )
236+
})
237+
...
238+
</k>
239+
240+
syntax JSON ::= "#feeDistribution" [function, symbol(feeDistribution)]
241+
// ----------------------------------------------------------------------
242+
rule #feeDistribution => {
243+
"max" : "100",
244+
"min" : "100",
245+
"mode" : "100",
246+
"p10" : "100",
247+
"p20" : "100",
248+
"p30" : "100",
249+
"p40" : "100",
250+
"p50" : "100",
251+
"p60" : "100",
252+
"p70" : "100",
253+
"p80" : "100",
254+
"p90" : "100",
255+
"p95" : "100",
256+
"p99" : "100",
257+
"transactionCount" : "0",
258+
"ledgerCount" : 0
259+
}
260+
```
261+
206262
## getTransaction
207263

208264
Look up the stored receipt by hash in its `receipts/receipt_<hash>.json` file. If the file

src/komet_node/server.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import importlib.metadata
34
import json
45
import logging
56
import sys
@@ -20,6 +21,15 @@
2021

2122
_PROTOCOL_VERSION: Final = '22'
2223

24+
# getVersionInfo fields. komet-node is a Python package, not a Go binary, so there is no
25+
# commit hash or build timestamp baked in at compile time; report all-zeros / epoch
26+
# placeholders with the correct spec types instead. The "Captive Core" of komet-node is the
27+
# komet package (the K semantics of Soroban that execute the transactions).
28+
_VERSION: Final = importlib.metadata.version('komet-node')
29+
_COMMIT_HASH: Final = '0' * 40
30+
_BUILD_TIMESTAMP: Final = '1970-01-01T00:00:00'
31+
_CAPTIVE_CORE_VERSION: Final = f'komet {importlib.metadata.version("komet")} (K semantics of Soroban)'
32+
2333
# Only sendTransaction executes a transaction. traceTransaction is a read-only lookup of the
2434
# trace stored on a previously executed transaction's receipt (see _read_only_envelope).
2535
_TX_METHODS: Final = ('sendTransaction',)
@@ -225,6 +235,19 @@ def _read_only_envelope(
225235
return {**base, 'passphrase': self.encoder.network_passphrase, 'protocolVersion': _PROTOCOL_VERSION}
226236
if method == 'getLatestLedger':
227237
return {**base, 'protocolVersion': _PROTOCOL_VERSION}
238+
if method == 'getVersionInfo':
239+
# protocolVersion is a JSON number here (a Go uint32 in stellar-rpc), unlike the
240+
# string the older methods still emit.
241+
return {
242+
**base,
243+
'version': _VERSION,
244+
'commitHash': _COMMIT_HASH,
245+
'buildTimestamp': _BUILD_TIMESTAMP,
246+
'captiveCoreVersion': _CAPTIVE_CORE_VERSION,
247+
'protocolVersion': int(_PROTOCOL_VERSION),
248+
}
249+
if method == 'getFeeStats':
250+
return base
228251
if method in ('getTransaction', 'traceTransaction'):
229252
tx_hash = params.get('hash')
230253
if not isinstance(tx_hash, str):

0 commit comments

Comments
 (0)