Skip to content

Commit 63fbd10

Browse files
authored
Merge pull request #3210 from apalache-mc/igor/json-rpc-assume-state
Refactor JSON handling code towards `assumeState`
2 parents 19606d0 + 1aacc60 commit 63fbd10

File tree

32 files changed

+990
-707
lines changed

32 files changed

+990
-707
lines changed

json-rpc/README.md

Lines changed: 136 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
**Authors:** [Igor Konnov][] and [Thomas Pani][]
44

5-
A simple JSON-RPC server module for Apalache. This is work in progress:
5+
A simple JSON-RPC server module for Apalache. **This is work in progress.**
66

77
This server is not meant to be a replacement for the current gRPC server (SHAI).
88
Rather, it is a lightweight alternative that can be used for symbolic
@@ -41,7 +41,7 @@ exploration of TLA+ specifications.
4141
- [Jetty Server][]. Yes, it is about 30 years old.
4242
It works. It is fast, simple, reliable, maintained, and is well-documented.
4343
- [Jackson][]. It is fast, simple, reliable, maintained, and is well-documented.
44-
It uses plain-old Java objects, and it supports basic Scala types. No super-advanced
44+
It uses plain-old Java objects, and it supports basic Scala types. No advanced
4545
FP here.
4646

4747
## 2. JSON-RPC methods
@@ -53,6 +53,15 @@ See the [JSON-RPC specification][] for more details. It is real short.
5353

5454
This is work in progress. More methods to be added in the future.
5555

56+
In the JSON format below, we write `"${placeholders}"` to indicate values that
57+
should be replaced with actual values. They have to be of proper types, e.g.,
58+
strings, numbers, booleans, arrays, or objects. We write them in quotes to
59+
produce valid JSON snippets.
60+
61+
Some of the methods accept and return traces and expressions in the
62+
[ITF Format][]. You can use the [itf-py][] library to produce and parse
63+
such traces and expressions in Python.
64+
5665
**Running the server.** To try the examples below, you need to start the server
5766
first:
5867

@@ -83,21 +92,21 @@ be used in subsequent calls to refer to this session.
8392
"method": "loadSpec",
8493
"params": {
8594
"sources": [
86-
<rootModuleInBase64>,
87-
<importedModule1InBase64>,
88-
...
95+
"${root-module-in-base64}",
96+
"${imported-module1-in-base64}",
97+
"..."
8998
],
90-
"init": "optional-initializer-predicate",
91-
"next": "optional-transition-predicate",
99+
"init": "${optional-initializer-predicate}",
100+
"next": "${optional-transition-predicate}",
92101
"invariants": [
93-
"invariant 1",
94-
...,
95-
"invariant N"
102+
"${invariant-1}",
103+
"...",
104+
"${invariant-N}"
96105
],
97106
"exports": [
98-
"exported-operator-1",
99-
...,
100-
"exported-operator-M"
107+
"${exported-operator-1}",
108+
"...",
109+
"${exported-operator-M}"
101110
]
102111
}
103112
}
@@ -113,13 +122,13 @@ operators such as `Init`, `Next`, and the invariants.
113122
```json
114123
{
115124
"result": {
116-
"sessionId": "unique-session-identifier",
117-
"snapshotId": snapshot-identifier-after-loading-the-spec,
125+
"sessionId": "${unique-session-identifier}",
126+
"snapshotId": "${snapshot-identifier-after-loading-the-spec}",
118127
"specParameters": {
119-
"initTransitions": init-metadata,
120-
"nextTransitions": next-metadata,
121-
"stateInvariants": state-invariants-metadata,
122-
"actionInvariants": action-invariants-metadata,
128+
"initTransitions": "${init-metadata}",
129+
"nextTransitions": "${next-metadata}",
130+
"stateInvariants": "${state-invariants-metadata}",
131+
"actionInvariants": "${action-invariants-metadata}"
123132
}
124133
}
125134
}
@@ -129,8 +138,8 @@ The metadata entries look like:
129138

130139
```json
131140
{
132-
"index": i,
133-
"labels": ["label1", "label2", ...]
141+
"index": "${integer-index}",
142+
"labels": ["label1", "label2", "..."]
134143
}
135144
```
136145

@@ -206,7 +215,7 @@ session identifier. No further calls should be made with this session identifier
206215
{
207216
"method": "disposeSpec",
208217
"params": {
209-
"sessionId": "session-identifier"
218+
"sessionId": "${session-identifier}"
210219
}
211220
}
212221
```
@@ -219,7 +228,7 @@ This identifier cannot be used in the future calls.
219228
```json
220229
{
221230
"result": {
222-
"sessionId": "session-identifier"
231+
"sessionId": "${session-identifier}"
223232
}
224233
}
225234
```
@@ -262,8 +271,8 @@ roll back to it again later.
262271
{
263272
"method": "rollback",
264273
"params": {
265-
"sessionId": "session-id",
266-
"snapshotId": snapshot-id
274+
"sessionId": "${session-id}",
275+
"snapshotId": "${snapshot-id}"
267276
}
268277
}
269278
```
@@ -276,8 +285,8 @@ that was rolled back to:
276285
```json
277286
{
278287
"result": {
279-
"sessionId": "session-id",
280-
"snapshotId": snapshot-id
288+
"sessionId": "${session-id}",
289+
"snapshotId": "${snapshot-id}"
281290
}
282291
}
283292
```
@@ -323,10 +332,10 @@ snapshot.
323332
{
324333
"method": "assumeTransition",
325334
"params": {
326-
"sessionId": "session-identifier",
327-
"transitionId": transition-identifier,
328-
"checkEnabled": check-if-transition-is-enabled,
329-
"timeoutSec": timeout-in-seconds-or-0,
335+
"sessionId": "${session-identifier}",
336+
"transitionId": "${integer-transition-identifier}",
337+
"checkEnabled": "${boolean-flag}",
338+
"timeoutSec": "${timeout-in-seconds-or-0}"
330339
}
331340
}
332341
```
@@ -336,9 +345,9 @@ snapshot.
336345
```json
337346
{
338347
"result": {
339-
"sessionId": "session-identifier",
340-
"snapshotId": new-snapshot-id,
341-
"transitionId": transition-identifier,
348+
"sessionId": "${session-identifier}",
349+
"snapshotId": "${new-snapshot-id}",
350+
"transitionId": "${integer-transition-identifier}",
342351
"status": "ENABLED|DISABLED|UNKNOWN"
343352
}
344353
}
@@ -381,7 +390,7 @@ new constraints, `nextStep` takes a new snapshot.
381390
{
382391
"method": "nextState",
383392
"params": {
384-
"sessionId": "session-identifier"
393+
"sessionId": "${session-identifier}"
385394
}
386395
}
387396
```
@@ -391,9 +400,9 @@ new constraints, `nextStep` takes a new snapshot.
391400
```json
392401
{
393402
"result": {
394-
"sessionId": "session-identifier",
395-
"snapshotId": new-snapshot-id,
396-
"newStepNo": new-step-number
403+
"sessionId": "${session-identifier}",
404+
"snapshotId": "${new-snapshot-id}",
405+
"newStepNo": "${new-step-number}"
397406
}
398407
}
399408
```
@@ -446,10 +455,10 @@ checking the invariant, the context is rolled back to the state before the call.
446455
{
447456
"method": "checkInvariant",
448457
"params": {
449-
"sessionId": "session-identifier",
450-
"invariantId": invariant-identifier,
458+
"sessionId": "${session-identifier}",
459+
"invariantId": "${integer-invariant-identifier}",
451460
"kind": "STATE|ACTION",
452-
"timeoutSec": timeout-in-seconds-or-0
461+
"timeoutSec": "${timeout-in-seconds-or-0}"
453462
}
454463
}
455464
```
@@ -459,9 +468,9 @@ checking the invariant, the context is rolled back to the state before the call.
459468
```json
460469
{
461470
"result": {
462-
"sessionId": "session-identifier",
471+
"sessionId": "${session-identifier}",
463472
"invariantStatus": "SATISFIED|VIOLATED|UNKNOWN",
464-
"trace": trace-in-itf-or-null,
473+
"trace": "${trace-in-itf-or-null}"
465474
}
466475
}
467476
```
@@ -520,10 +529,10 @@ set, or it is set to `0`, then the timeout is infinite.
520529
{
521530
"method": "query",
522531
"params": {
523-
"sessionId": "session-identifier",
524-
"kinds": [ kind1, kind2, ... ],
525-
"operator": optional-operator-name,
526-
"timeoutSec": timeout-in-seconds-or-0
532+
"sessionId": "${session-identifier}",
533+
"kinds": [ "${kind1}", "${kind2}", "..." ],
534+
"operator": "${optional-operator-name}",
535+
"timeoutSec": "${timeout-in-seconds-or-0}"
527536
}
528537
}
529538
```
@@ -537,9 +546,9 @@ and `expr` are in the [ITF Format][].
537546
```json
538547
{
539548
"result": {
540-
"sessionId": "session-identifier",
541-
"trace": trace-in-itf-or-null,
542-
"operatorValue": expr-in-itf-or-null
549+
"sessionId": "${session-identifier}",
550+
"trace": "${trace-in-itf-or-null}",
551+
"operatorValue": "${expr-in-itf-or-null}"
543552
}
544553
}
545554
```
@@ -592,9 +601,9 @@ returned by `nextState`).
592601
{
593602
"method": "nextModel",
594603
"params": {
595-
"sessionId": "session-identifier",
596-
"operator": <operator-name>,
597-
"timeoutSec": timeout-in-seconds-or-0
604+
"sessionId": "${session-identifier}",
605+
"operator": "${operator-name}",
606+
"timeoutSec": "${timeout-in-seconds-or-0}"
598607
}
599608
}
600609
```
@@ -619,10 +628,10 @@ The output contains the following fields:
619628
```json
620629
{
621630
"result": {
622-
"sessionId": "session-identifier",
623-
"oldValue": expr-in-itf-or-null,
624-
"hasOld": (TRUE|FALSE|UNKNOWN),
625-
"hasNext": (TRUE|FALSE|UNKNOWN)
631+
"sessionId": "${session-identifier}",
632+
"oldValue": "${expr-in-itf-or-null}",
633+
"hasOld": "TRUE|FALSE|UNKNOWN",
634+
"hasNext": "TRUE|FALSE|UNKNOWN"
626635
}
627636
}
628637
```
@@ -658,6 +667,73 @@ The output is as follows:
658667
}
659668
```
660669
670+
### 2.9. Method assumeState
671+
672+
Given a session identifier and concrete equalities `x = e` over a subset of the state
673+
variables, add the equality constraints to the SMT context. The expressions in the
674+
right-hand sides are given as expressions in the [ITF Format][].
675+
676+
Additionally, if `checkEnabled` is set to `true`, the server checks, whether there is a
677+
state that is reachable via the current transition prefix, including the added
678+
constraints. The parameter `timeoutSec` sets the timeout for this check in seconds.
679+
If `timeout` is not set, or it is set to `0`, then the timeout is infinite.
680+
681+
**Precondition.** The call to `assumeState` can be made only after at least single
682+
call to `assumeTransition` and `nextStep`.
683+
684+
**Effect.** The concrete equalities are translated as SMT equality constraints in
685+
the current context against the current symbolic state (sometimes, called a frame).
686+
These constraints do not override the existing assignments in the context. Rather,
687+
they pose additional constraints on top of the existing ones.
688+
689+
**Input:**
690+
691+
```json
692+
{
693+
"method": "assumeState",
694+
"params": {
695+
"sessionId": "${session-identifier}",
696+
"checkEnabled": "${boolean-flag}",
697+
"timeoutSec": "${timeout-in-seconds-or-0}",
698+
"equalities": {
699+
"${var-1}": "${expr-in-itf}",
700+
"...": "...",
701+
"${var-k}": "${expr-in-itf}"
702+
}
703+
}
704+
}
705+
```
706+
707+
**Output:**
708+
709+
```json
710+
{
711+
"result": {
712+
"sessionId": "${session-identifier}",
713+
"snapshotId": "${new-snapshot-id}",
714+
"status": "ENABLED|DISABLED|UNKNOWN"
715+
}
716+
}
717+
```
718+
719+
**Example**:
720+
721+
Execute the following command:
722+
723+
```sh
724+
$ curl -X POST http://localhost:8822/rpc \
725+
-H "Content-Type: application/json" \
726+
-d '{"jsonrpc":"2.0","method":"assumeState",
727+
"params":{"sessionId":"1","checkEnabled":true,"equalities": {"x":{"#bigint":"1"}}},"id":4}'
728+
```
729+
730+
It produces the following output:
731+
732+
```json
733+
{"jsonrpc":"2.0","id":2,"result":{"sessionId":"1","snapshotId":3,"status":"ENABLED"}}
734+
```
735+
736+
661737
[Jetty Server]: https://jetty.org/
662738
663739
[Jackson]: https://github.com/FasterXML/jackson
@@ -672,4 +748,6 @@ The output is as follows:
672748
673749
[Apalache IR]: https://apalache-mc.org/docs/adr/005adr-json.html
674750
675-
[SMT randomization]: https://apalache-mc.org/docs/apalache/tuning.html#randomization
751+
[SMT randomization]: https://apalache-mc.org/docs/apalache/tuning.html#randomization
752+
753+
[itf-py]: https://github.com/konnov/itf-py/

json-rpc/src/main/scala/com/github/apalachemc/apalache/jsonrpc/ExplorationServiceParams.scala

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.github.apalachemc.apalache.jsonrpc
22

3+
import com.fasterxml.jackson.databind.JsonNode
4+
35
sealed abstract class ExplorationServiceParams
46

57
/**
@@ -161,3 +163,33 @@ case class NextModelParams(
161163
operator: String = "",
162164
timeoutSec: Int = 0)
163165
extends ExplorationServiceParams
166+
167+
/**
168+
* Parameters for preparing a symbolic transition in the solver context.
169+
* @param sessionId
170+
* the ID of the previously loaded specification
171+
* @param equalities
172+
* a JSON-encoded list of equalities to assume in the current state (in the ITF format)
173+
* @param checkEnabled
174+
* whether to check if the transition is enabled. If `false`, the transition is prepared and assumed, but no
175+
* satisfiability is checked
176+
* @param timeoutSec
177+
* the timeout in seconds for checking satisfiability. If `0`, the default timeout is used. This parameter is ignored
178+
* if `checkEnabled` is `false`.
179+
*/
180+
case class AssumeStateParams(
181+
sessionId: String,
182+
equalities: JsonNode,
183+
checkEnabled: Boolean,
184+
timeoutSec: Int = 0)
185+
extends ExplorationServiceParams
186+
187+
object AssumeStateParams {
188+
def apply(
189+
sessionId: String,
190+
equalities: JsonNode,
191+
checkEnabled: Boolean = true,
192+
timeoutSec: Int = 0): AssumeStateParams = {
193+
new AssumeStateParams(sessionId, equalities, checkEnabled, timeoutSec)
194+
}
195+
}

0 commit comments

Comments
 (0)