|
5 | 5 | import logging |
6 | 6 | import re |
7 | 7 | import sys |
8 | | -import tempfile |
9 | 8 | import time |
10 | 9 | import traceback |
11 | 10 | from datetime import datetime, timezone |
12 | 11 | from http.server import BaseHTTPRequestHandler, HTTPServer |
13 | | -from pathlib import Path |
14 | | -from typing import TYPE_CHECKING, Any, Final, TypedDict |
| 12 | +from typing import TYPE_CHECKING, Any, Final |
15 | 13 |
|
16 | | -from stellar_sdk import Network, StrKey, TransactionEnvelope, xdr |
| 14 | +from stellar_sdk import StrKey, TransactionEnvelope, xdr |
17 | 15 |
|
18 | 16 | from komet_node.errors import RpcError |
19 | | -from komet_node.interpreter import NodeInterpreter |
20 | 17 | from komet_node.ledger import build_ledger_artifacts |
21 | 18 | from komet_node.ledger_entries import InvalidParamsError, format_ledger_entries_response, ledger_key_descriptors |
22 | 19 | from komet_node.result_xdr import transaction_meta_xdr, transaction_result_xdr |
23 | 20 | from komet_node.scval import scval_from_json |
24 | | -from komet_node.store import ChainStore, EventRecord, LedgerRecord |
25 | | -from komet_node.transaction import SimulationRejected, TransactionEncoder, malformed_tx_result_xdr |
| 21 | +from komet_node.transaction import SimulationRejected, malformed_tx_result_xdr |
26 | 22 |
|
27 | 23 | if TYPE_CHECKING: |
28 | 24 | from collections.abc import Mapping |
29 | 25 | from http.server import HTTPServer as HTTPServerType |
| 26 | + from pathlib import Path |
| 27 | + |
| 28 | + from komet_node.interfaces import Encoder, EventRecord, Interpreter, LedgerRecord, SimulateResult, Store |
30 | 29 |
|
31 | 30 | _PROTOCOL_VERSION: Final = 22 |
32 | 31 |
|
@@ -103,53 +102,50 @@ def _empty_transaction_data() -> str: |
103 | 102 |
|
104 | 103 | _log = logging.getLogger('komet_node') |
105 | 104 |
|
106 | | -# The internal simulateTransaction result K emits (node.md): always ``latestLedger``, then |
107 | | -# either ``error`` (a failed simulation) or ``returnValue`` (a JSON SCVal for a successful |
108 | | -# one). ``total=False`` — the two outcomes are mutually exclusive. |
109 | | -SimulateResult = TypedDict( |
110 | | - 'SimulateResult', |
111 | | - {'latestLedger': int, 'error': str, 'returnValue': Any}, |
112 | | - total=False, |
113 | | -) |
114 | | - |
115 | 105 |
|
116 | 106 | class StellarRpcServer: |
117 | 107 | """ |
118 | 108 | Long-running HTTP/JSON-RPC server that wraps the one-shot K node semantics. |
119 | 109 |
|
120 | 110 | The compiled semantics run one request per process invocation and hold no state |
121 | 111 | between runs, so this server supplies what they lack: it keeps the HTTP socket open, |
122 | | - persists state to disk, and decodes the Stellar XDR envelope (:class:`TransactionEncoder`) |
123 | | - that K cannot parse. It then runs the request envelope through the semantics |
124 | | - (:class:`NodeInterpreter`). All RPC dispatch, receipt bookkeeping, ledger accounting, |
125 | | - and response formatting are performed in K (``node.md``). The on-disk artifacts — input |
126 | | - and output — belong to the :class:`~komet_node.store.ChainStore`, which owns the io-dir |
127 | | - layout; this server holds one and asks it for receipts, ledgers, events, and the ledger |
128 | | - counter rather than touching paths itself. |
| 112 | + persists state to disk, and decodes the Stellar XDR envelope (an injected |
| 113 | + :class:`~komet_node.interfaces.Encoder`) that K cannot parse. It then runs the request |
| 114 | + envelope through the semantics (an :class:`~komet_node.interfaces.Interpreter`). All RPC |
| 115 | + dispatch, receipt bookkeeping, |
| 116 | + ledger accounting, and response formatting are performed in K (``node.md``). The on-disk |
| 117 | + artifacts — input and output — belong to the injected :class:`~komet_node.interfaces.Store`, |
| 118 | + which owns the io-dir layout; this server holds one and asks it for receipts, ledgers, |
| 119 | + events, and the ledger counter rather than touching paths itself. |
| 120 | +
|
| 121 | + The three collaborators (interpreter, encoder, store) are injected rather than constructed |
| 122 | + here, so the server depends only on the protocols in :mod:`komet_node.interfaces` and a test |
| 123 | + can drive it with fakes. A composition root wires the concretes — see ``build_server`` in |
| 124 | + ``__main__.py``. |
129 | 125 | """ |
130 | 126 |
|
131 | | - interpreter: NodeInterpreter |
132 | | - encoder: TransactionEncoder |
133 | | - store: ChainStore |
| 127 | + interpreter: Interpreter |
| 128 | + encoder: Encoder |
| 129 | + store: Store |
134 | 130 | io_dir: Path |
135 | 131 | state_file: Path |
136 | 132 |
|
137 | 133 | def __init__( |
138 | 134 | self, |
| 135 | + *, |
| 136 | + interpreter: Interpreter, |
| 137 | + encoder: Encoder, |
| 138 | + store: Store, |
139 | 139 | host: str = 'localhost', |
140 | 140 | port: int = 8000, |
141 | | - io_dir: Path | None = None, |
142 | | - network_passphrase: str = Network.TESTNET_NETWORK_PASSPHRASE, |
143 | 141 | ) -> None: |
| 142 | + self.interpreter = interpreter |
| 143 | + self.encoder = encoder |
| 144 | + self.store = store |
144 | 145 | self.host = host |
145 | 146 | self._port = port |
146 | | - self.interpreter = NodeInterpreter() |
147 | | - self.encoder = TransactionEncoder(network_passphrase) |
148 | 147 | self._httpd: HTTPServerType | None = None |
149 | 148 |
|
150 | | - # With no io-dir given, run against a fresh temporary directory: a throwaway chain |
151 | | - # that starts empty on every launch and leaves the working directory untouched. |
152 | | - self.store = ChainStore(Path(tempfile.mkdtemp(prefix='komet-node-')) if io_dir is None else io_dir) |
153 | 149 | self._fresh = self.store.initialize(self.interpreter.empty_config) |
154 | 150 | # state_file and io_dir are the paths the interpreter subprocess resolves against; kept |
155 | 151 | # as attributes so callers (and tests) can reach the io-dir root directly. |
|
0 commit comments