You are currently viewing a new version of our website. To view the old version click .
Mathematics
  • Article
  • Open Access

10 November 2025

Runtime Verification Tool for the Calculus of Context-Aware Ambients

School of Computer Science and Informatics, De Montfort University, Leicester LE1 9BH, UK

Abstract

A context-aware system is a system that adapts its behaviours in response to changes in the system’s environment (i.e., context). Ensuring the correctness of such a system is difficult because the state of the environment changes frequently in an unpredictable manner according to the laws of physics. Hence, formal verification techniques like model-checking and theorem proving do not work in many cases. Runtime Verification (RV) is a lightweight formal verification technique that consists of checking at runtime whether the execution of the system violates the requirements of the system. The Calculus of Context-aware Ambients (CCA) is a process calculus for modelling context-aware systems and reasoning about their behaviours. This paper proposes an RV tool for CCA, called ccaRV. Given a model of a system in CCA and a property of the system written in LTL (Linear Temporal Logic), ccaRV verifies automatically at runtime if the execution of the system violates the property. We propose a semantic approach to RV, where the RV mechanism is defined at the semantics level and not as an add-on. A consequence of this is that there is no need for generating a monitor from the property specification nor for the instrumentation of a system during verification. We define a labelled reduction relation for CCA, where the labels are used to capture the execution traces at the semantics level. Then we extend LTL with spatial operators and context expressions in order to formulate properties about the system context. We use a case study of the MQTT (Message Queue Telemetry Transport) protocol to evaluate the proposed RV approach. The results show that the ccaRV tool is scalable and its decisions are accurate.

1. Introduction

An important goal in software engineering is to produce software systems that are free from faults and runtime errors. However, achieving this goal has proven to be difficult in many cases, and in particular for context-aware systems where the behaviours of a system change dynamically to adapt to new situations. Many software verification techniques have been proposed that can help to assure the correctness of a piece of software. Software testing techniques [] review the software code to detect faults (static analysis), or run the software in a variety of conditions in the search of software errors (dynamic analysis). Since exhaustive testing is impossible in general, software testing techniques cannot guarantee that a software works correctly in all conditions. Model checking techniques [,], in contrast, explore automatically all possible reachable states of (a model of) the software. However, they do not scale well and suffer from the state explosion problem when the number of reachable states is too large. Deductive verification techniques [] can be used in some cases where model checking fails. Deductive verification techniques allow the generation of a collection of verification conditions (from the software and its specification) that must be proven to establish the correctness of the software. These verification conditions can be discharged using theorem-proving tools (e.g., HOL [], Vampire [], and CVC4 []). However, a deductive verification approach requires some specialist knowledge in formal methods and a detailed understanding of the correct behaviour of the software, which makes it not easily accessible to software engineers.
Runtime verification (RV) [,] is a lightweight software verification technique that checks the behaviour of the software automatically at runtime against a desired property. This technique is scalable and much easier to understand compared to model checking and theorem proving approaches []. Moreover, RV can allow the system to recover from errors by performing specific actions when errors are detected; these actions may be to reconfigure the system, to log the traces that lead to the errors, or to notify the user []. However, RV does not guarantee that all possible executions of the software satisfy the property. Traditional software verification techniques assume that the system requirements are fixed and do not change in response to changes in the system context. This is not the case for IoT systems. The requirements of an IoT system are predominantly context-dependent and the system adapts its behaviours as the context changes. Such systems are also known as context-aware systems or simply smart systems. Context is key to the behaviour of IoT systems and is constructed from data acquired through sensors. For example, a self-driving car uses context information such as location, direction, speed, and the objects in the surrounding environment to guide itself autonomously on the road. Commonly, IoT devices are resource-constrained devices, are often powered by batteries with low computational and memory capacity, communicate wirelessly, and are mobile. Thus, context such as the network bandwidth, device location, battery level, available memory, and the physical environment changes frequently and affects the behaviour of the software running on these devices. The formal verification of context-aware systems is more challenging because the context of a system can change in an unpredictable manner.
The interest of applying RV techniques to IoT systems is growing in the software verification community [,,,,]. However, most works focus on the analysis of the execution traces generated by the software under test and do not consider the context under which these execution traces were generated. Context is extremely dynamic and changes in an unpredictable way in that it is practically impossible to reproduce in a testing laboratory. Making context an integral part of an RV technique is important for effective verification of IoT systems because it will be possible to discover faults caused by changes in the context. Current approaches think of RV as an add-on after the system has been implemented and involve in general the following tasks: (i) the system’s source code is instrumented to produce the execution traces; (ii) a monitor is generated from the formal specification of the property; (iii) the system and the monitor are run to perform the RV (online or offline). The Calculus of Context-aware Ambients (CCA) [] is a process calculus for modelling context-aware systems and to reason about their behaviours. Specifications written in CCA are executable by the CCA interpreter ccaPL []. This paper proposes an RV tool for CCA, called ccaRV. As shown in Figure 1, the tool takes as input an CCA process and an LTL formula and produces in output a verification report that says whether the LTL formula has been violated during the execution of the CCA process. We propose a semantic approach to RV, where the RV mechanism is defined at the semantics level and not as an add-on. We define a labelled reduction relation for CCA, where the labels are used to capture the execution traces at the semantics level. Then we extend LTL with spatial operators and context expressions in order to formulate properties about the system context. We use a case study of the MQTT (Message Queue Telemetry Transport) protocol to evaluate the proposed RV approach. The results show that the ccaRV tool is scalable and its decisions are accurate. The contributions of this paper are fourfold:
  • A novel labelled reduction semantics is proposed for CCA, where the traditional reduction relation is annotated with a label representing the execution trace of the reduction step (Section 2.2).
  • A new property specification language is proposed that extends LTL with context expressions and other operators to cater for the specification of properties about both the system and its context (Section 2.3). A context expression defined in Table 1 is a logical formula upon the state of the environment (context). A formal semantics of the property specification language is given over an infinite sequence of states, where a state contains both the current context information and the trace of the last reduction step. In this way, the semantic model provides a holistic view of the system execution (Section 2.3).
    Table 1. Syntax of CCA.
  • A novel RV mechanism is defined based on the labelled reduction semantics of CCA and the semantics of the property specification language (Definition 5). A consequence of this is that there is no need for generating a monitor from the property specification nor for the instrumentation of a system during verification. This RV mechanism is implemented into a software tool called ccaRV depicted in Figure 1 (Section 3.1).
  • A formal specification of the MQTT protocol is given in CCA and used to evaluate the RV tool ccaRV. The MQTT protocol is de facto standard messaging protocol for IoT device communication as it is designed for resource-constrained devices and low-bandwidth, high-latency or unreliable networks. It is chosen as a case study for its scalability (Section 2.4). The experimental results show that ccaRV decisions are accurate and the RV overhead is low relative to the execution time, but increases with the size of system and the size of the property (Section 3.2).
Figure 1. The CCA runtime verification tool ccaRV.
The remaining of the paper is structured as follows. Section 2 presents the syntax and the labelled reduction semantics of CCA, the syntax and the semantics of the property specification language, and a formal specification of the MQTT protocol. The ccaRV tool is presented in Section 3 as well as the experiment results. Section 4 discusses the related work and Section 5 concludes the paper and points to future research directions.

2. Materials and Methods

2.1. Overview of CCA

The Calculus of Context-aware Ambients (CCA) [] is a process calculus for modelling the behaviour of context-aware, mobile, and concurrent systems. The concept of ambient is used in CCA to represent any entity in which a computation can take place. This is representedas n [ P ] , where n is the ambient’s name and P a process specifying the computation taking place in the ambient. We also say that the process P is executed by the ambient n. Ambients can communicate by sending messages to each other through a hand-shake message passing mechanism. A concurrent system can be specified as a parallel composition of ambients (see Section 2.1.2). An ambient can contain other ambients, forming a hierarchy of ambients. Such a hierarchical structure can be used to specified complex systems. An ambient can also be mobile, either by performing the movementcapabilities “ in ” and “ out ” (see Section 2.1.1) to move from one location to another or by being inside a moving ambient. Context-awareness is an important feature in CCA. Indeed, an ambient can be aware of its context so as to adapt its behaviour accordingly when the context changes. This is achieved using the notion of a context-guarded process, where the execution of a process is guarded by a context expression, which is a predicate over the state of the ambient hierarchy. The grammar in Table 1 describes formally the syntax of CCA, where P (or Q) denotes a process, M a capability, and κ a context expression. We use the symbols n, x, y, and z to represent names. The formal semantics of CCA are presented in Section 2.2.

2.1.1. Capabilities

In CCA, a process is formed from atomic actions called capabilities. The syntax of a capability M is given in Table 1. The capability skip corresponds to one transition, i.e., one execution step. An ambient can perform the capability in n to move into a sibling ambient n. The capability out allows an ambient to move out of its parent ambient. Communications between ambients are performed using the output capability α send ( z 1 , , z ) and the input capability α recv ( y 1 , , y ) , where the parameters are names and 0 . The location α can be @ for any parent, n @ for a specific parent n, # for any child, n # for a specific child n, : : for any sibling, n : : for a specific sibling n, or ϵ (empty string) for the executing ambient itself. An ambient of the form n [ 0 ] can be deleted using the capability del n . A process abstraction x defined at a location α is called using a capability of the form α x ( z 1 , , z ) , where the number of actual parameters, 0 , must match the number of formal parameters of x.

2.1.2. Processes

The syntax of a process P is given in Table 1. The inactivity process 0 does nothing and terminates immediately. Two processes can be composed in parallel using the operator ‘|’. A process of the form { P } behaves just like P. A name restriction ( new n ) P limits the scope of a new name n to the process P. The process ! P denotes a replication of the process P. A process of the form < κ > M . P is guarded by a context expression (i.e., a predicate over contexts, see Section 2.1.3) κ . This process can execute only if the environment satisfies the condition κ . An if-then statement in CCA can have many branches, each guarded by a context expression. An if-then statement is blocking, i.e., it waits until one of the branches is executed. On the contrary, an if-then-else statement does not wait and executes the else-part if none of the branches of the then-part can be executed. The let-statement let x 1 = e 1 , , y = e in P substitutes in P the values of the expressions to the variables. A search process find x 1 , , x : κ for P looks for a list of values for which the context expression κ holds and substitutes those values in P. A definition of a process abstraction x has the form proc x ( y 1 , , y ) P , where P is the body of the process abstraction and the list of formal parameters are between the parentheses. At the call of a process abstraction, the number of the actual parameters must match that of the formal parameters. Process abstractions are similar to procedures or functions in programming languages.

2.1.3. Context Expressions

A context is modelled in CCA as a process with possibly a single hole in it. A hole (represented by the symbol ⊙) in a context C is a placeholder for the process of which C is a context. For example, let us consider a system modelled by the process n [ P | m [ Q | R ] ] | S , where P, Q, R, S are processes. The context of the ambient m in that system is n [ P | ] | S and that of the process R is n [ P | m [ Q | ] ] | S . The grammar in Table 2 depicts the syntax of a context, where P denotes a process. It is assumed that the following congruence rules hold over contexts: 0 | C C , { C } C , and C | P P | C .
Table 2. Syntax of contexts.
A context expression (CE in short) is a predicate over contexts. Table 1 presents the syntax of a context expression κ . A formal semantics of context expressions is given in Table 3, with respect to the context model of Table 2. The notation C κ means that a context C satisfies a context expression κ . If a context express κ is valid, i.e., is satisfied by all the contexts, we write κ .
Table 3. Satisfaction relation for context expressions.
The CE true always holds, while the CE false (i.e., not true ) never holds. A CE n = m holds if the names n and m are identical. The CE 0 stands for the empty context 0 . The CE this stands for the hole context. The propositional operators ( not , and and or ) keep their usual semantics. A CE κ 1 | κ 2 holds for a parallel composition of two contexts such that κ 1 holds for one and κ 2 holds for the other. A CE n [ κ ] holds for an ambient named n such that κ holds inside that ambient. A CE next κ holds for a context if κ holds for a child context of that context. A CE somewhere κ holds for a context if κ holds for some sub-context of that context. Table 4 shows some examples of context expressions. The following section presents the formal semantics of CCA.
Table 4. Examples of context expressions.

2.2. A Labelled Reduction Semantics of CCA

This section presents the formal semantics of CCA based on a structural congruence and a labelled reduction relation. The structural congruence is denoted by ≡. It is the smallest congruence relation on processes that satisfies the axioms in Table 5. These axioms are used to manipulate the structure of processes. The axioms S1–S6 assert that the structural congruence is a commutative monoid for ( 0 , | ) . The axiom S7 says that the parallel composition of processes preserves the structural congruence. The axiom S8 says that an ambient n [ P ] behaves the same as an ambient n [ Q ] if P and Q are equivalent processes. The name restriction can be safely manipulated using the axioms S9–S13. The replication operator satisfies the axioms S14–S16. The axiom S17 states that the process abstraction definition is monotonic with respect to the structural congruence. A process guarded with true behaves the same as that process without guards (axiom S18). The axiom S19 states that if two processes are equivalent, then their context-guarded prefixes, with equivalent guards, are also equivalent. The let-statement unifies a name with the value of an expression; see the axiom S20, where v a l ( e i ) denotes the value of the expression e i , for i 1 . We denoteby fn ( Q ) , the set of the free names in a process Q. This set is calculated as in Table 6.
Table 5. Structural congruence relation for processes, where fn ( Q ) is the set of the free names in a process Q.
Table 6. Definition of the free names function fn .
The rules in Table 7, Table 8 and Table 9 define the labelled reduction relation of processes, s , where P { z 1 / y 1 , , z / y } represents the process obtained by replacing each free occurrence of the name y i in P by z i , i = 1 , , . A substitution { z 1 / y 1 , , z / y } is sometimes denoted by δ . The label s of a reduction relation is a string of one of the forms described in Table 10, which represents the trace of the reduction step. The first set of rules R1–R7 (in Table 7) defines the semantics of a process abstraction call. Thus, a process abstraction call is executed only if a definition of the process abstraction exists at the specified location. The next set of rules R8–R20 (in Table 8) deals with message passing communication between processes. The semantics of mobile and context-aware processes is given in Table 9. The mobility rules R21–R22 are inheritedfrom []. The rule R3 states that the capability skip denotes a single reduction step. An empty ambient (of the form n [ 0 ] ) can be deleted from the system (rule R24). The labelled reduction relation propagates across scopes (rule R25). The labelled reduction relation is closed under structural congruence (rule R26). The rule R27 says that a context-guarded prefix is executed only if the environment satisfies the guard. In this rule, δ stands for a substitution of names. The rule R28 states that one of the enabled branches of an if-then statement is chosen non-deterministically for execution. If no branch is enabled, then no reduction takes place. On the contrary, for the if-then-else statement, the else branch is executed if no other branch is enabled (see rules R29 to R30). Finally, the rule R31 defines the formal semantics of the search statement find .
Table 7. Labelled reduction relation for process abstractions.
Table 8. Labelled reduction relation for message passing, where   0 is the number of parameters.
Table 9. Labelled reduction relation for mobile and context-aware processes, where δ denotes a substitution of names and ϵ stands for an empty string.
Table 10. General forms of the execution traces.
Definition 1 
(An enabled process). We say that a process P is enabled, denoted by e n a b l e d ( P ) , if there exists a process Q such that P s Q , for some string of characters s. For example, the process skip . P is enabled (see the semantic rule R23), while the process 0 is not enabled.
Definition 2 
(An execution or run of a process). An execution or run of a process P 0 is a sequence of reductions P 0 s 1 P 1 s 2 P 2 P n 1 s n P n , n 0 , such that e n a b l e d ( P i ) for all i < n and if n < , ¬ e n a b l e d ( P n ) .
Definition 3 
(A terminating execution). A terminating execution or run of a process P 0 is a finite sequence of reductions P 0 s 1 P 1 s 2 P 2 P n 1 s n P n , n 0 , such that P n = 0 and e n a b l e d ( P i ) for all i < n .
Definition 4 
(A continuation process). We say that a process Q is a continuation process of another process P, denoted by P * Q , if there exists a finite sequence of reductions of the form P s 1 P 1 s 2 P 2 P n 1 s n Q , n 0 and e n a b l e d ( P i ) for all i < n .
Example 1 
(A specification of a persistent memory cell in CCA). A persistent memory cell is a data structure that stores a value persistently until the value is replaced by writing a new value to the memory cell. The value stored in a persistent memory cell can be read at any time by taking a copy of that value. Unlike a write action, a read action does not modify the content of a memory cell. A persistent memory cell that stores a pair of values can be specified in CCA as an ambient of the following form, where (z1,z2) is the initial content of the memory cell.
1. 
mem[
2. 
  !@recv().recv(v1,v2).{ @send(v1,v2).0 | send(v1,v2).0 }
3. 
| !@recv(x1,x2).recv(y1,y2).{ send(x1,x2).0 | @send().0 }
4. 
| send(z1,z2).0
5. 
]
To read the value of a memory cell mem, the following process segment must be used in parallel to the memory ambient: mem#send().mem#recv(x,y). This process segment communicates with the process in line 2 to get into the variables x and y a copy of the values stored in the memory cell. To write a pair of values (v1,v2) into a memory cell, the following process segment must be used in parallel to the memory ambient: mem#send(v1,v2).mem#recv(). This process segment synchronises with the process in line 3 to set (v1,v2) as the new content of the memory cell.

2.3. The Property Specification Language

RV consists of checking at runtime whether the execution of a process violates a given property. The grammar in Table 11 describes the syntax of the property specification language for ccaRV. It is an extension of the Linear Temporal Logic (LTL) [] with new constructs like a context expression κ (explained above), the predicate t r a c e ( " r " ) (which holds if the string of characters r is a substring of the execution trace), and the operator post. A formula p o s t f holds if the formula f is true in the final execution state.
Table 11. The syntax of LTL.
The propositional operators && (and), || (or), ! (negation), -> (implication), and <-> (equivalence) keep their usual semantics. Note that the spatial operators next and somewhere are defined in context expressions. Their semantics are explained in Section 2.1.3. The semantics of the temporal operators, over an infinite sequence of states, can be explained as follows. A formula X f is true in the current state if f is true in the next state. A formula f U g is true if f remains true at least until g becomes true in the current or a future state. A formula f V g is true if g remains true until and including the point where f first becomes true; if f never becomes true, g must remain true forever. A formula f W g is true if f is true permanently or until g becomes true. A formula [ ] f is true if f is true at every state. Finally, a formula < > f is true if f is true in the current state or sometime in the future. Table 12 illustrates graphically the semantics of the temporal formulas.
Table 12. Graphical illustration of the semantics of the temporal operators, where • represents a state, ⟶ denotes a single execution step, and > an infinite sequence of execution steps.
The formal semantics of a formula are defined over an infinite sequence of states σ = σ 0 σ 1 σ 2 , where a state is a pair σ i = ( P i , s i ) of a process P i (defined in Table 1) and a string s i . We write σ a f to say that σ satisfies a formula f. Let σ i = σ i σ i + 1 denote the suffix of σ from the ith state, i 0 . Note that the first state of σ i , ( σ i ) 0 , is σ i , for i 0 . The satisfaction relation a is defined as follows.
  • σ a t r a c e ( " r " ) iff σ 0 = ( P 0 , s 0 ) and r is a substring of the string s 0 .
  • σ a κ iff σ 0 = ( P 0 , s 0 ) and P 0 κ (⊧ is defined in Table 3).
  • σ a p o s t f iff there exists i 0 such that σ i a f and not e n a b l e d ( P i ) and for all 0 k < i , e n a b l e d ( P k ) .
  • σ a ! f iff σ a f .
  • σ a f | | g iff σ a f or σ a g .
  • σ a X f iff σ 1 a f .
  • σ a f U g iff there exists i 0 such that σ i a g and for all 0 k < i , σ k a f .
The semantics of the remaining formulas can be deduced from the following equalities.
  • f & & g = ! ( ! f |   | ! g )
  • f > g = ! f |   | g
  • f < > g = ( f > g ) & & ( g > f )
  • f V g = ! ( ! f U ! g )
  • < > f = t r u e U f
  • [ ] f = ! < > ! f
  • f W g = ( f U g ) |   | [ ] g
We give some examples of LTL formulas in Example 2. The RV mechanism can now be formalised as in Definition 5.
Example 2. 
Examples of LTL formulas.
  • < > [ ] f , i.e., eventually f holds forever. This is a safety property, i.e., nothing bad can happen.
  • [ ] < > f , i.e., f always eventually holds. This is a liveness property, i.e., something good will always eventually happen.
  • < > t r a c e ( " ( X ) " ) , i.e., a message X is sent.
  • < > t r a c e ( " ( X ) = = = > B " ) , i.e., a message X is received by ambient B.
  • < > t r a c e ( " A = = = ( X ) " ) , i.e., a message X is sent by ambient A.
  • < > t r a c e ( " A = = = ( X ) = = = > B " ) , i.e., ambient A sent a message  X to ambient B.
Definition 5 
(Runtime Verification). An execution or run of a process P 0 s 1 P 1 s 2 P 2 P n 1 s n P n , n 0 , does not violate an LTL formula f if σ a f , where σ = ( P 0 , ϵ ) ( P 1 , s 1 ) ( P 2 , s 2 ) ( P n 1 , s n 1 ) ( P n , s n ) ( P n , s n ) and ϵ denotes an empty string. Note that the suffix σ n is an infinite sequence of the state ( P n , s n ) .
Definition 6 
(Partial satisfaction relation). We say that a process P partially satisfies a formula f, denoted by P f , if there exists a run of the process P that does not violate the formula f.
Definition 7 
(Satisfaction relation). We say that a process P satisfies a formula f, denoted by P s f , if every run of the process P does not violate the formula f.
Note that the partial satisfaction relation ⊢ guarantees the correctness of a single execution of a process, while the satisfaction relation s guarantees the correctness of all possible executions of a process. The relationship between the two is established by Theorem 1. In relation to RV, if the RV of a process P against an LTL formula f detects a violation then this proves that P s f . If it does not, then it establishes that P f .
Theorem 1. 
P s f implies P f , for a process P and an LTL formula f.
The proof of Theorem 1 is straightforward from Definitions 6 and 7.
Theorem 2. 
If there exists a terminating execution for a process P, then P p o s t 0 .
Theorem 3. 
If t is a substring of s and P and Q are two processes such that P s Q and Q f , then
P X ( t r a c e ( " t " ) & & f ) .
Theorem 4. 
If P and Q are two processes such that P * Q and Q f , then
P < > f .
The proofs of the Theorems 2–4 are given in Appendix A.

2.4. Case Study: The MQTT Protocol

2.4.1. Overview of the MQTT Protocol

The Message Queue Telemetry Transport (MQTT) protocol is a lightweight publish-subscribe messaging transport protocol developed for reliable machine-to-machine (M2M) communication over unreliable channels. It is an open, simple and easy-to-implement protocol developed for resource-constrained devices and high-latency, low-bandwidth, or unreliable networks. These make MQTT a suitable messaging protocol for IoT devices. The protocol runs over TCP/IP and other network protocols that provide ordered, lossless, bi-directional connections. A variant, MQTT-SN (MQTT for Sensor Networks), is used over other transport protocols such as UDP, Zigbee, or Bluetooth. MQTT is an OASIS (Organization for the Advancement of Structured Information Standards) standard [,] and an ISO (International Organization for Standardization) standard [], for IoT connectivity. The MQTT protocol assumes two types of network entities: a message broker and a number of clients. Information is organised in a hierarchy of topics. A general architecture of the MQTT protocol is depicted in Figure 2. A client can act as a publisher by publishing a message on a topic, or as a subscriber to a topic to receive all the messages published on that topic, or both. A broker is a server that receives messages from publishers and pushes these messages to the subscribers of the corresponding topics. In this way the publishers do not need to know who the subscribers are and vice versa. If the broker receives a message on a topic for which there is no current subscribers, the message is discarded unless the publisher of the message instructs the broker to retain a copy of the message by setting the message’s retain flag to 1. The broker keeps only the latest retain message per topic. This retain message is automatically sent to a client when they subscribe to the topic, which is the latest known message on the topic.
Figure 2. Architecture of the MQTT protocol.
There are 14 types of message packets in the MQTT protocol as summarised in Table 13. A client establishes a connection to a broker by sending a CONNECT packet to the broker. After connecting to a broker, a client can subscribe to a topic by sending a SUBSCRIBE packet to the broker or can publish a message on a topic using a PUBLISH packet. A client can cancel its subscription to a topic by sending an UNSUBSCRIBE packet to the broker. To end a connection with a broker, a client sends a DISCONNECT packet to the broker. The MQTT protocol provides three Quality of Service (QoS) levels that indicate the level of assurance for delivery of a PUBLISH packet between the sender and receiver:
  • QoS 0 means at most one delivery. A packet is sent once; no acknowledgement is required. This is illustrated in Figure 3a. This also means the delivery is not guaranteed, the packet can get lost. Therefore, QoS level 0 is recommended when message loss is acceptable or the network bandwidth is at premium.
    Figure 3. MQTT protocol control flow for each QoS.(a) QoS 0; (b) QoS 1; (c) QoS 2.
  • QoS 1 means at least once delivery. A packet is sent repeatedly until acknowledgement is received. The packet delivery is guaranteed upon acknowledgement. This is illustrated in Figure 3b. This is the default QoS level and it offers the best trade-off between bandwidth and delivery guarantee.
  • QoS 2 means exactly once delivery. A packet is sent once and is guaranteed to be delivered. There is an increased overhead associated with this quality of service. This is the highest quality of service, for use when neither loss nor duplication of messages are acceptable. The network traffic engendered by this QoS is illustrated in Figure 3c.
Table 13. MQTT packet types.
Table 13. MQTT packet types.
NameDirection of FlowDescription
CONNECTClient to BrokerClient request to connect to Broker
CONNACKBroker to ClientConnect acknowledgment
PUBLISHClient to Broker orPublish message
Broker to Client
PUBACKClient to Broker orPublish acknowledgment
Broker to Client
PUBRECClient to Broker orPublish received
Broker to Client
PUBRELClient to Broker orPublish release
Broker to Client
PUBCOMPClient to Broker orPublish complete
Broker to Client
SUBSCRIBEClient to BrokerClient subscribe request
SUBACKBroker to ClientSubscribe acknowledgment
UNSUBSCRIBEClient to BrokerUnsubscribe request
UNSUBACKBroker to ClientUnsubscribe acknowledgment
PINGREQClient to BrokerPING request
PINGRESPBroker to ClientPING response
DISCONNECTClient to BrokerClient is disconnecting

2.4.2. A Formal Specification of the MQTT Protocol in CCA

In this section, the behaviour of the MQTT protocol is formally described in CCA. A client and a broker can be represented as ambients in CCA. The behaviour of a client can be described by an ambient (aka client ambient) of the form in Listing 1, where the continuation process P represents a sequence of message exchanges between the client and the broker (e.g., SUBSCRIBE, PUBLISH, UNSUBSCRIBE, and DISCONNECT); and Q is a continuation process for processing the PUBLISH packets received from the broker. The line 2 allows a client to connect to the broker and then subscribe to topics or publish messages; while lines 3–8 are used for receiving messages from the broker and process them. Therefore, a client that only publishes messages need not contain the lines 3–8.
Listing 1. General form of a client ambient.
1. 
clientId[
2. 
 broker::send(CONNECT, clientId).broker::recv(ack).P
3. 
 | !broker::recv(packetType, _, topic, qos, retainFlag, payload).if
4. 
  < packetType=PUBLISH and qos=0 > Q
5. 
  < packetType=PUBLISH and qos=1 > broker::send(PUBACK).Q
6. 
  < packetType=PUBLISH and qos=2 > broker::send(PUBACK).
7. 
      broker::recv(pubrel).broker::send(PUBCOMP).Q
8. 
  fi
9. 
]
The first message packet a client can send to a broker is a CONNECT packet to establish a network connection with the broker. The broker acknowledges the connection by sending a CONNACK packet to the client. Then the client can subscribe to a topic by sending a SUBSCRIBE packet to the broker, which must respond with a SUBACK packet. The client will now receive from the broker all the messages published on that topic. In the course of a session, a client can also send a UNSUBSCRIBE packet to the broker to stop receiving messages on a specific topic. In this case, the broker will respond with a UNSUBACK packet. A PUBLISH packet can be sent by a client to a broker or by a broker to a client; in any case, the delivery process is guided by the QoS level of the packet (see Figure 3). Eventually, the client will send a DISCONNECT packet to close the network connection with the broker.
A topic is represented in CCA as a process abstraction (named after the topic), which sends each message published on the topic to all the clients that have subscribed to the topic. For each client C that connects to the broker successfully, a child ambient of the form C [ 0 ] is created. This child ambient is deleted when the client disconnects from the broker. Similarly, when a client C subscribes to a topic T, a child ambient of the form C _ T [ 0 ] is created. This child ambient is deleted when the client unsubscribes from the topic. The existence of these child ambients tells the broker whether a client is currently connected and what topics they have subscribed to. The context expression c o n t a i n s ( n ) , defined in Table 4, holds if the broker contains a child ambient of the form n [ 0 ] . The general form of a process abstraction for a topic is given in Listing 2. Basically, the process abstraction takes four arguments (a topic’s name, a QoS level, a retain flag, and a payload) and sends a PUBLISH packet to all the clients that have subscribed to the topic.
The general form of an ambient representing a broker (aka broker ambient) is given in Listing 3. The lines 3–5 correspond to the definitions of the process abstractions representing the topics managed by the broker. According to the MQTT protocol documentation [] if a client sends a PUBLISH packet with a retain flag of 1, the broker must store the message and its QoS. Therefore, for each topic the broker ambient contains a persistent memory cell (defined in Example 1), named after the topic, which will store the retain message and the retain QoS of the topic (see lines 7–9 in Listing 3). Recall that the broker must send the retain message of a topic and the retain QoS to a client immediately after the client subscribes to the topic []. The lines 10–16 describe how the broker processes a CONNECT packet or a DISCONNECT packet received from a client. A child ambient of the form clientId[0] is created upon receiving a CONNECT from a client clientId, to mark that the client is connected (see line 13). This child ambient is deleted when the broker receives a DISCONNECT packet from the client (see line 15). In lines 18–29, a child ambient of the form clientId_topic is created when a client clientId subscribes to a topic (see lines 25–28); and this child ambient is deleted when the client unsubscribes from the topic (see line 32). The MQTT documentation says that when sending a PUBLISH Packet to a client, the server must set the retain flag to 1 if a message is sent as a result of a new subscription being made by a client. The broker must set the retain flag to 0 when a PUBLISH Packet is sent to a client because it matches an established subscription regardless of how the flag was set in the message it received. This requirement is specified in lines 20, 38, 40, 42, 44, 47, and 50 in Listing 3.
Listing 2. General form of a process abstraction representing a topic with N subscribers.
proc topic(topic, qos, retainFlag, payload){
  if
   < contains(clientId1) and contains(clientId1_topic) >
     clientId1::send(PUBLISH,broker,topic,qos,retainFlag,payload).if
       <qos=0> skip.0
       <qos=1> clientId1::recv(ack).0
       <qos=2> clientId1::recv(pubrec).clientId1::send(PUBREL).
        clientId1::recv(pubcomp).0
     fi
   < not(contains(clientId1) and contains(clientId1_topic)) > skip.0
  fi
  | if
   < contains(clientId2) and contains(clientId2_topic) >
     clientId2::send(PUBLISH,broker,topic,qos,retainFlag,payload).if
       <qos=0> skip.0
       <qos=1> clientId2::recv(ack).0
       <qos=2> clientId2::recv(pubrec).clientId2::send(PUBREL).
        clientId2::recv(pubcomp).0
     fi
   < not(contains(clientId2) and contains(clientId2_topic)) > skip.0
fi
| ...
| if
   < contains(clientIdN) and contains(clientIdN_topic) >
     clientIdN::send(PUBLISH,broker,topic,qos,retainFlag,payload).if
       <qos=0> skip.0
       <qos=1> clientIdN::recv(ack).0
       <qos=2> clientIdN::recv(pubrec).clientIdN::send(PUBREL).
         clientIdN::recv(pubcomp).0
     fi
   < not(contains(clientIdN) and contains(clientIdN_topic)) > skip.0
  fi
}
Listing 3. General form of an ambient representing a broker.
1. 
broker[
2. 
    // One process abstraction per topic
3. 
    proc topic1(topic, qos, retainFlag, payload){ ... }
4. 
 | ...
5. 
 | proc topicK(topic, qos, retainFlag, payload){ ... }
6. 
  // One persistent memory cell per topic
7. 
 | topic1[ ... ]
8. 
 | ...
9. 
 | topicK[ ... ]
10. 
 | !::recv(packetType, clientId).if
11. 
      <packetType=CONNECT> clientId::send(CONNACK).if
12. 
        <contains(clientId)> skip.0
13. 
        <not contains(clientId)> skip.clientId[0]
14. 
       fi
15. 
      <packetType=DISCONNECT and contains(clientId)> del clientId.0
16. 
    fi
17. 
 | !::recv(packetType, clientId, topic).if
18. 
      <packetType=SUBSCRIBE> let x=clientId+_+topic in topic#send().
19. 
       topic#recv(qos,msg).clientId::send(SUBACK).
20. 
        clientId::send(PUBLISH,broker,topic,qos,1,msg).if
21. 
         <qos=0 and contains(x)> skip.0
22. 
          <qos=1 and contains(x)> clientId::recv(ack).0
23. 
          <qos=2 and contains(x)> clientId::recv(pubrec).
24. 
            clientId::send(PUBREL).clientId::recv(pubcomp).0
25. 
          <qos=0 and not contains(x)> skip.x[0]
26. 
          <qos=1 and not contains(x)> clientId::recv(ack).x[0]
27. 
          <qos=2 and not contains(x)> clientId::recv(pubrec).
28. 
            clientId::send(PUBREL).clientId::recv(pubcomp).x[0]
29. 
       fi
30. 
      <packetType=UNSUBSCRIBE> let x=clientId+_+topic in topic#send().
31. 
       topic#recv(qos,msg).clientId::send(UNSUBACK).if
32. 
         <contains(x)> del x.0
33. 
         <not contains(x)> skip.0
34. 
       fi
35. 
    fi
36. 
 | !::recv(packetType,clientId,topic,qos,retainFlag, payload).if
37. 
    <packetType=PUBLISH and qos=0 and retainFlag=1> topic#send(qos,payload).
38. 
       topic#recv().topic(topic,qos,0,payload).0
39. 
    <packetType=PUBLISH and qos=0 and retainFlag=0>
40. 
       topic(topic,qos,0,payload).0
41. 
    <packetType=PUBLISH and qos=1 and retainFlag=1> topic#send(qos,payload).
42. 
       topic#recv().clientId::send(PUBACK).topic(topic,qos,0,payload).0
43. 
    <packetType=PUBLISH and qos=1 and retainFlag=0> clientId::send(PUBACK).
44. 
       topic(topic,qos,0,payload).0
45. 
    <packetType=PUBLISH and qos=2 and retainFlag=1> topic#send(qos,payload)
46. 
       topic#recv().clientId::send(PUBREC).clientId::recv(pubrel).
47. 
       clientId::send(PUBCOMP).topic(topic,qos,0,payload).0
48. 
    <packetType=PUBLISH and qos=2 and retainFlag=0> clientId::send(PUBREC).
49. 
       clientId::recv(pubrel).clientId::send(PUBCOMP).
50. 
       topic(topic,qos,0,payload).0
51. 
    fi
52. 
]

2.4.3. An Example

Let us consider a simple IoT system composed of a motion sensor, a light sensor, a lamp and a CCTV camera. An arduino microcontroller is used to control the lamp and the camera based on the sensors’ outputs. The motion sensor outputs 1 if movement is detected nearby and 0 otherwise. The light sensor outputs the light level as a value between 0 and 1000. When it gets sufficiently dark (light level < 330) the microcontroller must turn the lamp on. The lamp is turned off otherwise. When movement is detected, the microcontroller must turn the CCTV camera on to record the scene; otherwise the camera is off. These devices communicate using the MQTT protocol, as depicted in Figure 4. It is assumed that the broker runs on some server in the network. The sensors are publishers and the actuators (i.e., the lamp and the camera) are subscribers. The microcontroller is both a publisher and a subscriber. The light sensor publishes the light level on the topic “light” and the motion sensor publishes its measurement on the topic “motion”. The microcontroller subscribes to both the “light” and the “motion” topics. It also publishes on two topics: the “lamp” topic to send control commands to the lamp, and the “camera” topic to control the camera. Consequently, the lamp subscribes to the “lamp” topic and the camera subscribes to the “camera” topic. The motion sensor and the light sensor are specified in Listing 4 using the general template provided in Listing 1. Note that these are pure publishers. The specifications of the lamp and the CCTV camera are given in Listing 5. The microcontroller is specified in Listing 6, and the full system specifications are given in Appendix B.
Figure 4. Architecture of a simple MQTT-based IoT system.
Listing 4. Specification of the motion sensor and the light sensor.
motion_sensor[
  broker::send(CONNECT, motion_sensor).broker::recv(ack).
  broker::send(PUBLISH, motion_sensor, motion, 1, 1, 1).
  broker::recv(pubrec).broker::send(PUBREL).broker::recv(pubcomp).0
]
|
light_sensor[
  broker::send(CONNECT, light_sensor).broker::recv(ack).
  broker::send(PUBLISH, light_sensor, light, 1, 1, 300).
  broker::recv(pubrec).broker::send(PUBREL).broker::recv(pubcomp).0
]
Listing 5. Specification of the lamp and the camera.
lamp[
   !broker::recv(packetType, _, topic, qos, retainFlag, payload).if
       <packetType=PUBLISH and qos=0 > skip.0
       <packetType=PUBLISH and qos=1 > broker::send(PUBACK).0
       <packetType=PUBLISH and qos=2 > broker::send(PUBREC).
          broker::recv(pubrel).broker::send(PUBCOMP).0
       fi
   | broker::send(CONNECT, lamp).broker::recv(ack).
       broker::send(SUBSCRIBE, lamp, lamp).broker::recv(suback).0
]
|
camera[
   !broker::recv(packetType, _, topic, qos, retainFlag, payload).if
       <packetType=PUBLISH and qos=0> skip.0
       <packetType=PUBLISH and qos=1> broker::send(PUBACK).0
       <packetType=PUBLISH and qos=2> broker::send(PUBREC).
          broker::recv(pubrel).broker::send(PUBCOMP).0
    fi
   | broker::send(CONNECT, camera).broker::recv(ack).
       broker::send(SUBSCRIBE, camera, camera).broker::recv(suback).0
]
Listing 6. Specification of the microcontroller.
mcu[
    !broker::recv(packetType, _, topic, qos, retainFlag, payload).if
        <packetType=PUBLISH and qos=0> Q(topic,payload,broker,mcu,lamp,camera).0
        <packetType=PUBLISH and qos=1> broker::send(PUBACK).
           Q(topic,payload,broker,mcu,lamp,camera).0
        <packetType=PUBLISH and qos=2> broker::send(PUBREC).broker::recv(pubrel).
           broker::send(PUBCOMP).Q(topic,payload,broker,mcu,lamp,camera).0
        fi
    | broker::send(CONNECT, mcu).broker::recv(ack).
        broker::send(SUBSCRIBE, mcu, light).broker::recv(suback).
        broker::send(SUBSCRIBE, mcu, motion).broker::recv(suback).0
    | proc Q(topic,payload,broker,mcu,lamp,camera) {
        if
           <topic=light and payload<330> broker::send(PUBLISH,mcu,lamp,1,0,on).
             broker::recv(puback).0
           <topic=light and payload>=330> broker::send(PUBLISH,mcu,lamp,1,0,off).
             broker::recv(puback).0
           <topic=motion and payload=1> broker::send(PUBLISH,mcu,camera,1,0,on).
             broker::recv(puback).0
           <topic=motion and payload=0> broker::send(PUBLISH,mcu,camera,1,0,off).
             broker::recv(puback).0
        fi
    }
]

3. Results

The formal semantics of CCA presented in Section 2.2 and the RV mechanism formalised in Definition 5 are implemented into a software tool in Java called ccaRV (for CCA Runtime Verification) []. The tool takes in input an CCA process and a property and checks at runtime if the execution of the process violates the property. This section presents an overview of the ccaRV tool and the experimental evaluation of the tool.

3.1. Overview of the ccaRV Tool

In order to verify an CCA process P against a property f with the ccaRV tool, one must write a program that consists of the process P and a declaration block that contains the property f. The grammar in Listing 7 describes the syntax of an ccaRV program. The keywords BEGIN_DECLS and END_DECLS delimit the declaration block. Note that ccaRV programs are case-sensitive. An example of an ccaRV program is given in Appendix B. Inside the declaration block, one can define context expressions using the keyword def. For example, defcontains(d,n) = { somewhere (d[n[0] | true] | true) }. One can also add the declaration of global names (using the keyword name) and the execution directives to control the execution of parallel processes.
Listing 7. Syntax of an ccaRV program, where P is a process and k is a context expression defined in Table 1; f is an LTL formula defined in Table 11; <Id> stands for an identifier (i.e., a name); e for the empty string; and <Val> is a non-negative integer number.
<Program> ::= <DeclarationBlock> P
<DeclarationBlock> ::= e | "BEGIN_DECLS" <DeclarationList> "END_DECLS"
<DeclarationList> ::= e | <Declaration> <DeclarationList>
<Declaration> ::= "def" <Id> "(" <ParamList> ") = {" k "}" | "mode random"
                | "display code" | "length = " <Val> | "display congruence"
                | "name" <Id> <Params>
                | "ltl" (<Id> | e) "{" f "}"
<ParamList> ::= e | <Id> <Params><Params> ::= e | "," <Id> <Params>
By default, the interleaving execution of parallel processes follows the FIFO (First In, First Out) policy in order to minimise the time a process waits to be executed. In the case of conflict (i.e., two or more processes are willing to execute), the sequential order as the processes appear in the program code applies. The execution directive mode random applies the FIFO policy, but in the case of conflict one of the conflicting processes is selected randomly for execution. In both cases, the process selected for execution performs only a single reduction and passes the control to another enabled process (if any). In practice, we strive for fair execution of concurrent processes so no process waits indefinitely for execution. The directive display code prints out the program code after each execution step. By default, only reduction steps are shown in the execution traces; the directive display congruence adds the congruence steps to the execution traces. The directive length = n causes the execution of the program to stop after n reductions, for some non-negative integer n. Global names are declared using the keyword name. The property for RV is specified using the keyword ltl followed by an LTL formula f between curly brackets. Optionally, a name can be given to the formula. Comments can be added anywhere in a program text in the same way as in the Java programming language.
The command line for executing an ccaRV program file myprog.cca is as follows.
java   - jar ccaRV . jar   [ - rn ] myprog . cca
The option -rn specifies the number n of runs, for some positive integer n. For example, with the option -r200 the process is executed 200 times and each run is checked against the property to verify. The default number of runs is one. This command line produces a verification report that tells for each run whether the property has been violated and how many execution steps have been performed. A verification report also shows the number of failed runs, the number of passed runs, and the total and average elapsed times. An excerpt of a verification report is shown in Figure 5. The execution trace of a failed run is logged till the point of failure in order to assist with debugging.
Figure 5. Example of a verification report.

3.2. Experimental Results

This section presents two sets of experiments designed to evaluate: (i) the accuracy of the RV decisions, and (ii) the RV overhead. The experiment environment comprised a 64-bit Toshiba laptop with an Intel Core i7 processor and 16 GB of RAM running a Windows 10 Pro operating system. The Java jar file for the ccaRV tool (version 1.0) used in the experiments can be obtained from []. It runs on Java RE version 11 and above. The source code of the ccaRV program for the first experiment is given in Appendix B and the source codes of the ccaRV programs for the second experiment can be generated using the Python program provided in Appendix C. All other applications were closed during the experiments.

3.2.1. Runtime Verification Accuracy

Consider the simple IoT system described in Section 2.4.3. The ccaRV program for this system can be found in Appendix B. Recall that the IoT devices communicate using an MQTT protocol. Sensor readings are published with the QoS 1 (see Listing 4). Since the purpose of RV is to detect the violation of a given property during the system execution, we have identified 10 properties that we know are violated or not based on the MQTT protocol requirements and the IoT system requirements. These properties are summarised in Table 14, where Z denotes any of the IoT devices and Y stands for a topic. For example, the property P7(Z,Y) is not violated if the client Z publishes on the topic Y without having subscribed to the topic Y. However, the property P7(Z,Y) is violated if the client Z subscribes to the topic Y before publishing (for the first time) on that topic. The property P7(Z,Y) is also violated if the client Z does not publish on the topic Y. The simple IoT system is verified for each of these properties using the command line:
java   - jar ccaRV . jar   - r 2000 iot . cca
where iot.cca is the program file for the simple IoT system given in Appendix B. This means that for each property, the program is checked over 2000 runs in order to cover a large number of execution paths. The results are shown in Table 15, where “pass” means that no execution path violates the property, and “fail” means that one or more execution paths violate the property. It follows that the verification decisions of the ccaRV tool are accurate. Note that an RV decision “pass” does not guarantee the correctness of the system with respect to the corresponding property, since not all possible execution paths over all possible input values have been tested. But it gives more confidence that the system is correct with respect to that property.
Table 14. Property specifications.
Table 15. RV accuracy.

3.2.2. Runtime Verification Overhead

An RV overhead measures the additional load put on the system by an RV mechanism. This load must not affect the performance of the system too much for the RV mechanism to be practical and scalable. First, the system is run without RV, and the execution time is recorded. Then the system is run with the RV of some property. The difference in execution times is an estimate of the RV overhead. Three experiments are carried out to evaluate the RV overhead of the ccaRV tool. The first experiment considers the RV of a single MQTT system against a series of properties of increasing size. The size of a property is equal to 1 plus the number of (temporal, spatial, and Boolean) operators that occur in the property. We let 0 be the size of the property when RV is not applied during execution. The second experiment analyses the RV time overhead of context-related properties (context expressions) based on their sizes and the (execution) states at which they are verified. In the third experiment, a single property is checked against a series of MQTT systems of increasing size (i.e., the number of MQTT clients in a system). For the first experiment, we consider the MQTT-based IoT system presented in Section 2.4.3 and formally specified by the ccaRV program in Appendix B. This system is verified in ccaRV against a combination of the properties in Table 14. For each combination of properties, the system is run 200 times and the average execution time is measured, i.e.,
java   - jar ccaRV . jar   - r 200 iot . cca
The 200 runs offer a good balance between the duration of the experiment and the coverage of the system execution paths. The results are given in Table 16 and depicted in Figure 6. It follows that the RV overhead increases with the size of the property.
Table 16. Average RV time over 200 runs with respect to the size of the property. The properties P1 to P7 are defined in Table 14. The property size 0 corresponds to an execution without RV, i.e., the baseline.
Figure 6. Graphical representation of the results in Table 16. It can be seen that the RV overhead increases with the size of the property. The property size 0 corresponds to an execution without RV.
The second experiment considers a number of context expressions of various sizes, describing specific locations in the ambient tree and compares the RV time of these context expressions at different execution states. These context expressions and their sizes are specified in Table 17. The temporal operator X (i.e., the temporal next) is used to determine the state at which a context expression is verified. For example, the context expression κ is verified in the first state for the LTL formula κ , in the second state for the LTL formula X ( κ ) , in the third state for the LTL formula X ( X ( κ ) ) , and so on. The MQTT-based IoT system presented in Section 2.4.3 is used for this experiment. Three execution states are chosen for this experiment: the initial state (state 0), the state 20 and the state 50. For each context expression in Table 17 and each of these states the system is run 200 times and the average RV time is measured. These measurements are summarised in Table 18. The results depicted in Figure 7 show that in each state the RV time increases with the size of the context expression; and for each context expression the RV time is practically the same across the states. These observations are due to the fact the ambient tree does not change much during the execution of this system.
Table 17. Examples of context expressions of various sizes. The size is equal to 1 plus the number of operators in the context expression.
Table 18. Average RV time over 200 runs of context expressions of various sizes verified at specific states. The context expressions are defined in Table 17. The size 0 corresponds to an execution without RV, i.e., the baseline.
Figure 7. Comparison of RV time of context expressions of different sizes verified at different execution states.
For the third experiment, we consider a series of MQTT-based systems with 1 broker, 10 topics and a varying number of clients. The MQTT protocol is chosen in this experiment for its high scalability. The number of clients grows from 5 to 45, and each system comprises 40% of clients that are publishers, 40% of clients that are subscribers and 20% of clients that are both publishers and subscribers. The system is checked against the property <>(somewhere (broker[true] | true)), which says that the MQTT broker is eventually available. The results are recorded in Table 19. The column “Average execution time” shows the average execution time (without RV) of the system over 200 runs for each QoS level. The next column “Average verification time” shows the average execution time (with RV) of the system over 200 runs for each QoS level. The last column “Average verification overhead” shows the difference of the two execution times for each QoS level. The standard deviations of the measurements are given in Table 20. The graphs in Figure 8 show that the RV overhead increases with the size of the system. However, the QoS 0 incurs the least overhead and the QoS 1 the most. This is due to the difference in complexity of the MQTT protocol QoS levels as explained in Section 2.4.1. Moreover, Figure 9 shows that the RV overhead is small compared to the system execution time. Finally, Figure 10 confirms that QoS 0 is the fastest of the three MQTT protocol QoS levels. The MQTT-based systems used in this experiment can be reproduced automatically using the Python program in Appendix C.
Table 19. RV overhead for an MQTT protocol system. The first column represents the number of MQTT clients. The average time per run, in milliseconds (ms), is calculated over 200 runs.
Table 20. The standard deviation (over 200 runs) in milliseconds (ms) of the execution time and the verification time (in Table 19).
Figure 8. Comparison of RV overheads between MQTT QoS levels.
Figure 9. Comparison of the execution time and the RV time for each QoS level.
Figure 10. Comparison of the execution time of the three QoS levels of an MQTT protocol.

4. Discussion

RV is widely investigated in the literature. A survey presented in [] addresses the problem of uncertainty in runtime monitoring and provided a detailed analysis of the different methods for handling incomplete traces. Similarly, [] gives a survey on formal verification methods for smart contracts. LARVA [] is an RV tool for Java programs that uses symbolic automata as the basis of the property specification language. ContractLAVA [] is a LARVA-based RV tool for smart contracts. A study on the application of RV in robotics is carried out in []. They provide guidelines to help with the testing and verification of ROS (Robot Operating System)-based applications. Many researches have investigated the RV of distributed systems. Since a distributed system consists of subsystems that execute independently and interact through some message passing interfaces, the challenge is how to verify the correctness of a distributed system based on the different traces produced by the subsystems. The paper [] proposes an approach to offline RV of distributed systems against interactions, which are formal models describing communications within a distributed system. They proposed a mechanism for spontaneously removing from subsystems the traces from an interaction that are no longer observed. A different approach is proposed in [] that improves the asynchronous algorithm for RV of linearisability of concurrent algorithms []. They propose a generic methodology for building a lightweight runtime-verified linearisable algorithm from any presumably linearizable algorithm. All these approaches require the instrumentation of the program code for the purpose of RV. In our approach there is no need for the instrumentation of a program code because the RV mechanism is defined at the semantics level (see Definition 5).
Many approaches to decentralised monitoring in distributed systems have been investigated. Falcone [] gives a review of some approaches to decentralized monitoring for systems with units of computation that are physically or logically distributed. An important challenge is how to synthesise decentralised monitors for a given property. The paper [] proposes an approach for decentralised monitoring based on the Linear Temporal Logic (LTL). Similarly, ref. [] argues that the LTL property to verify against needs to be decomposed into sub-formulae, which are then distributed amongst the local monitors attached to the subsystems of a distributed system. They propose an algorithm for distributing and monitoring LTL formulae, such that violations can be detected by local monitors alone. Ganguly et al. [] propose an automata-based synchronous monitoring algorithm for the RV of synchronous distributed systems. In addition, ref. [] proposes a framework to recommend efficient configurations of the monitoring system depending on the target specification using Machine Learning (ML). Past time CTL (Computation Tree Logic) is used in [] to specify properties of IoT systems. They propose a procedure to derive, from a past-CTL formula, monitors that can be distributed on the edge devices. A domain-specific language for RV RML (Runtime Monitoring Language) is proposed in []. The formalization and implementation of RML is based on a trace calculus with fully deterministic rewriting semantics. Leotta et al. [] propose an approach for RV of IoT systems based on the Prolog logic programming language. Compared to our approach, there is no need for generating a separate monitor for each property; rather a single monitor embedded in the semantics is used to evaluate any LTL formulae in accordance with Definition 5.
Some researchers have focused on developing tools for RV [,,,]. Aceto et al. [] propose a synthetic benchmarking framework that targets the systematic evaluation of RV tools for message-based concurrent systems. An RV tool, detectEr, for software applications written in Erlang/OTP is presented in []. The tool runtime checks properties expressed in a safety fragment of the linear-time modal μ -calculus. DAME [] is an RV tool for multithreaded applications that detects violations of safety properties expressed in an epistemic logic, which is an extension of a past-time LTL. They propose an algorithm that automatically synthesizes decentralized monitors to evaluate the information at each thread and to detect and predict safety violations. In [], an RV tool, R2U2, is presented that runs on an Arduino microcontroller and is designed to detect Aerobraking Control System (ACS) faults in rockets and trigger the appropriate mitigations. Compared to the ccaRV tool, none of these tools provides a mechanism for the RV of context-related properties.
It is challenging to design, develop, and validate a context-aware system that satisfies requirements, particularly when requirements can change at run time []. Fredericks et al. [] propose run-time monitoring and adaptation of tests as suitable techniques for evaluating whether a Dynamic Adaptive System (DAS) satisfies, or is even capable of satisfying, its requirements given its current execution context. As stated in [], systems that need to operate autonomously necessitate on-board RV technologies, to cater for the changing environment context. They identified important research questions to foster the discussion of ways to improve how to evaluate and compare tools for RV, particularly for cyber-physical systems. Some of these questions are addressed in [] for unmanned aircraft systems to monitor safe operation with respect to operational limits such as geofencing. They propose an algorithm and describe parameters for the buffer distance used for the geo-fence boundary values. Ensuring the safety of complex Industrial Control Systems (ICSs) cannot be fully achieved during the design and development phases []. They propose a stream-based RV approach to ensure the safety of ICS at runtime. These works are closer to ours in the sense that the context of the system is catered for in the RV approaches. However, they still generate monitors from properties and introduce the system code, which are not necessary in our approach. Moreover, most of these works used artificially generated execution traces to evaluate the quality of their RV algorithms. In our case, real execution traces obtained from the actual execution of the system are used to evaluate the performance of our proposed approach.

5. Conclusions and Outlook

In this paper we have presented an RV tool for CCA called ccaRV. This tool can be used to verify that a system modelled in CCA satisfies desired properties. The property specification language is an extension of LTL with context expressions and other predicates so as to be able to specify context-dependent properties. This is particularly important for the verification of context-aware systems such as the IoT systems. A semantic approach to RV is proposed, where the RV mechanism is defined at the semantics level and not as an add-on. A consequence of this is that there is no need for the instrumentation of a system during verification. To achieve this, labelled reduction semantics is proposed for CCA, where a label represents the execution trace of a reduction step. The formal semantics of the property specification language is defined over an infinite sequence of states; each state in the sequence contains the current context and the execution trace of the last reduction step. Based on these semantics, the RV mechanism is formally defined and implemented into a software tool, ccaRV. This tool takes in input an CCA process and a property specified in LTL and produces a verification report that tells whether the execution of the process has violated the property. The ccaRV tool is evaluated using a case study of the MQTT protocol, which is de facto standard for IoT device communication. The results of the experiments show that the tool scales well and makes an accurate decision about whether a property is violated or not. In practice, the proposed ccaRV tool can be used during the development of a context-aware system (e.g., an IoT system) to verify that the system design specified in CCA satisfies the requirements of the system before the system is actually implemented in a high-level programming language like Java, C++, or Python.
In future work, we are interested in implementing the proposed semantic approach to RV for a high level programming language such as Python or Java. A key advantage of this approach is that users do not need to instrument their program code or to generate monitors for the property to verify. They just need to provide the property to check against their program. This is in contrast to current RV approaches that require that the program code be instrumented and the monitor be generated from the property to verify against.

Funding

This research received no external funding.

Institutional Review Board Statement

Not applicable.

Data Availability Statement

The original contributions presented in the study are included in the article, further inquiries can be directed to the corresponding author.

Conflicts of Interest

The author declares no conflicts of interest.

Appendix A. Proofs of the Theorems

Proof of Theorem 2. 
Suppose there exists a terminating execution of a process P. From Definition 3, that terminating execution has the form P s 1 P 1 s 2 P 2 P n 1 s n 0 , such that e n a b l e d ( P i ) for all i < n . Let σ = ( P , ϵ ) ( P 1 , s 1 ) ( P 2 , s 2 ) ( P n 1 , s n 1 ) ( 0 , s n ) ( 0 , s n ) , where σ n is an infinite sequence of ( 0 , s n ) . Recall that ¬ e n a b l e d ( 0 ) holds, from Definition 1. It follows that σ a p o s t 0 . From Definition 5, the terminating execution P s 1 P 1 s 2 P 2 P n 1 s n 0 satisfies the formula p o s t 0 . From Definition 6, we conclude that P p o s t 0 . □
Proof Theorem 3. 
  • Qf
{from Definition 6}
  • There exists an execution or run Q s 1 Q 1 s 2 Q 2 Q n 1 s n Q n , n 0 that satisfies the formula f.
{from Definition 5}
  • ( Q , ϵ ) ( Q 1 , s 1 ) ( Q 2 , s 2 ) ( Q n , s n ) ( Q n , s n ) a f
{from the semantics of t r a c e ( " t " ) }
  • ( Q , s ) ( Q 1 , s 1 ) ( Q 2 , s 2 ) ( Q n , s n ) ( Q n , s n ) a ( t r a c e ( " t " ) & & f )
{from the semantics of the X operator}
  • ( P , ϵ ) ( Q , s ) ( Q 1 , s 1 ) ( Q 2 , s 2 ) ( Q n , s n ) ( Q n , s n ) a X ( t r a c e ( " t " ) & & f )
{since P s Q }
  • There exists an execution or run of the process P of the form P s Q s 1 Q 1 s 2 Q 2 Q n 1 s n Q n , n 0 that satisfies the formula X ( t r a c e ( " t " ) & & f ) .
{from Definition 6}
  • P X ( t r a c e ( " t " ) & & f )
Proof of Theorem 4. 
From Definition 4, P * Q if there exists a finite sequence of reductions of the form P s 1 P 1 s 2 P 2 P n 1 s n Q , n 0 . We will give the proof by induction on n, the number of reduction steps from P to Q.
  • Base case: n = 0
    • P Q and Q f
{by Definition 6}
  • P f
{by the semantics of LTL}
  • P < > f
Induction hypothesis: assume that the statement is true for n = k .
Induction step: we must prove that the statement is also true for n = k + 1 .
  • Suppose there exists a finite sequence of reductions P s 1 P 1 s 2 P 2 P k s k + 1 Q and Q f .
{by Definition 4}
  • P s 1 P 1 and P 1 s 2 P 2 P k s k + 1 Q and Q f .
{by the induction hypothesis}
  • P s 1 P 1 and P 1 < > f .
{by Theorem 3}
  • P X ( t r a c e ( " " ) & & < > f ) .
{by the semantics of LTL}
  • P X ( < > f ) .
{by the semantics of LTL}
  • P < > f .
Conclusion: We can conclude that the statement holds for all values of n 0 .

Appendix B. CCA Specification of the Simple IoT System of Section 2.4.3

BEGIN_DECLS
  def contains(n) = { somewhere (this | n[0] | true) }
  def contains(d,n) = { somewhere (d[n[0] | true] | true) }
  mode random
  //display code
  //display congruence
  //length = 100
  ltl P9 { <>(contains(broker,mcu) && !contains(broker,lamp)) }
END_DECLS
lamp[
  !broker::recv(packetType, _, topic, qos, retainFlag, payload).if
      <packetType=PUBLISH and qos=0 > skip.0
      <packetType=PUBLISH and qos=1 > broker::send(PUBACK).0
      <packetType=PUBLISH and qos=2 > broker::send(PUBREC).
         broker::recv(pubrel).broker::send(PUBCOMP).0
   fi
  | broker::send(CONNECT, lamp).broker::recv(ack).
   broker::send(SUBSCRIBE, lamp, lamp).broker::recv(suback).0
]
| camera[
  !broker::recv(packetType, _, topic, qos, retainFlag, payload).if
      <packetType=PUBLISH and qos=0> skip.0
      <packetType=PUBLISH and qos=1> broker::send(PUBACK).0
      <packetType=PUBLISH and qos=2> broker::send(PUBREC).
       broker::recv(pubrel).broker::send(PUBCOMP).0
   fi
  | broker::send(CONNECT, camera).broker::recv(ack).
   broker::send(SUBSCRIBE, camera, camera).broker::recv(suback).0
]
| mcu[
  !broker::recv(packetType, _, topic, qos, retainFlag, payload).if
      <packetType=PUBLISH and qos=0> Q(topic,payload,broker,mcu,lamp,camera).0
      <packetType=PUBLISH and qos=1> broker::send(PUBACK).
        Q(topic,payload,broker,mcu,lamp,camera).0
      <packetType=PUBLISH and qos=2> broker::send(PUBREC).broker::recv(pubrel).
        broker::send(PUBCOMP).Q(topic,payload,broker,mcu,lamp,camera).0
   fi
  | broker::send(CONNECT, mcu).broker::recv(ack).
   broker::send(SUBSCRIBE, mcu, light).broker::recv(suback).
   broker::send(SUBSCRIBE, mcu, motion).broker::recv(suback).0
  | proc Q(topic,payload,broker,mcu,lamp,camera) {
    if
        <topic=light and payload<330> broker::send(PUBLISH,mcu,lamp,1,0,on).
          broker::recv(puback).0
        <topic=light and payload>=330> broker::send(PUBLISH,mcu,lamp,1,0,off).
          broker::recv(puback).0
        <topic=motion and payload=1> broker::send(PUBLISH,mcu,camera,1,0,on).
          broker::recv(puback).0
        <topic=motion and payload=0> broker::send(PUBLISH,mcu,camera,1,0,off).
          broker::recv(puback).0
   fi
  }
]
| motion_sensor[
  broker::send(CONNECT, motion_sensor).broker::recv(ack).
  broker::send(PUBLISH, motion_sensor, motion, 1, 1, 1).
  broker::recv(pubrec).broker::send(PUBREL).broker::recv(pubcomp).0
]
| light_sensor[
  broker::send(CONNECT, light_sensor).broker::recv(ack).
  broker::send(PUBLISH, light_sensor, light, 1, 1, 300).
  broker::recv(pubrec).broker::send(PUBREL).broker::recv(pubcomp).0
]
| broker[
  proc lamp(topic, qos, retainFlag, payload) {
   if
      <contains(lamp) and contains(lamp_lamp)>
      lamp::send(PUBLISH,broker,topic,qos,0,payload).if
          <qos=0> skip.0
          <qos=1> lamp::recv(ack).0
          <qos=2> lamp::recv(pubrec).lamp::send(PUBREL). lamp::recv(pubcomp).0
      fi
      <not(contains(lamp) and contains(lamp_lamp))> skip.0
   fi
}
| proc camera(topic,qos,retainFlag,payload) {
   if
      <contains(camera) and contains(camera_camera)>
      camera::send(PUBLISH,broker,topic,qos,0,payload).if
          <qos=0> skip.0
          <qos=1> camera::recv(ack).0
          <qos=2> camera::recv(pubrec).camera::send(PUBREL).
            camera::recv(pubcomp).0
      fi
      <not(contains(camera) and contains(camera_camera))> skip.0
   fi
}
| proc light(topic,qos,retainFlag,payload) {
   if
      <contains(mcu) and contains(mcu_light)>
      mcu::send(PUBLISH, broker, topic, qos, 0, payload).if
          <qos=0> skip.0
          <qos=1> mcu::recv(ack).0
          <qos=2> mcu::recv(pubrec).mcu::send(PUBREL).mcu::recv(pubcomp).0
      fi
   <not(contains(mcu) and contains(mcu_light))> skip.0
   fi
}
| proc motion(topic, qos, retainFlag, payload) {
  if
   <contains(mcu) and contains(mcu_motion)>
   mcu::send(PUBLISH, broker, topic, qos, 0, payload).if
          <qos=0> skip.0
          <qos=1> mcu::recv(ack).0
          <qos=2> mcu::recv(pubrec).mcu::send(PUBREL).mcu::recv(pubcomp).0
   fi
  <not(contains(mcu) and contains(mcu_motion))> skip.0
 fi
}
| lamp[
   !@recv().recv(v1,v2).{ @send(v1,v2).0 | send(v1,v2).0 }
  | !@recv(x1,x2).recv(y1,y2).{ send(x1,x2).0 | @send().0 }
  | send(1,off).0
 ]
| camera[
   !@recv().recv(v1,v2).{ @send(v1,v2).0 | send(v1,v2).0 }
  | !@recv(x1,x2).recv(y1,y2).{ send(x1,x2).0 | @send().0 }
  | send(1,off).0
 ]
| light[
   !@recv().recv(v1,v2).{ @send(v1,v2).0 | send(v1,v2).0 }
  | !@recv(x1,x2).recv(y1,y2).{ send(x1,x2).0 | @send().0 }
  | send(1,50).0
 ]
| motion[
   !@recv().recv(v1,v2).{ @send(v1,v2).0 | send(v1,v2).0 }
  | !@recv(x1,x2).recv(y1,y2).{ send(x1,x2).0 | @send().0 }
  | send(1,0).0
 ]
| !::recv(packetType, clientId).if
 <packetType=CONNECT> clientId::send(CONNACK).if
          <contains(clientId)> skip.0
          <not contains(clientId)> skip.clientId[0]
  fi
 <packetType=DISCONNECT and contains(clientId)> del clientId.0
fi
| !::recv(packetType, clientId, topic).if
 <packetType=SUBSCRIBE> let x=clientId+_+topic in topic#send().
  topic#recv(qos,msg).clientId::send(SUBACK).
  clientId::send(PUBLISH,broker,topic,qos,1,msg).if
     <qos=0 and contains(x)> skip.0
     <qos=1 and contains(x)> clientId::recv(ack).0
     <qos=2 and contains(x)> clientId::recv(pubrec).clientId::send(PUBREL).
       clientId::recv(pubcomp).0
     <qos=0 and not contains(x)> skip.x[0]
     <qos=1 and not contains(x)> clientId::recv(ack).x[0]
     <qos=2 and not contains(x)> clientId::recv(pubrec).
       clientId::send(PUBREL).clientId::recv(pubcomp).x[0]
  fi
 <packetType=UNSUBSCRIBE> let x=clientId+_+topic in topic#send().
    topic#recv(qos,msg).clientId::send(UNSUBACK).if
     <contains(x)> del x.0
     <not contains(x)> skip.0
  fi
fi
| !::recv(packetType,clientId,topic,qos,retainFlag, payload).if
  <packetType=PUBLISH and qos=0 and retainFlag=1> topic#send(qos,payload).
    topic#recv().topic(topic,qos,0,payload).0
  <packetType=PUBLISH and qos=0 and retainFlag=0>
    topic(topic,qos,0,payload).0
  <packetType=PUBLISH and qos=1 and retainFlag=1> topic#send(qos,payload).
    topic#recv().clientId::send(PUBACK).topic(topic,qos,0,payload).0
  <packetType=PUBLISH and qos=1 and retainFlag=0> clientId::send(PUBACK).
    topic(topic,qos,0,payload).0
  <packetType=PUBLISH and qos=2 and retainFlag=1> topic#send(qos,payload).
    topic#recv().clientId::send(PUBREC).clientId::recv(pubrel).
    clientId::send(PUBCOMP).topic(topic,qos,0,payload).0
  <packetType=PUBLISH and qos=2 and retainFlag=0> clientId::send(PUBREC).
    clientId::recv(pubrel).clientId::send(PUBCOMP).
    topic(topic,qos,0,payload).0
 fi
]

Appendix C. A Python Program for Generating an MQTT Protocol System

##
## This program creates an MQTT protocol system in CCA with:
## - nbTopic topics
## - nbSub subscribers
## - nbPub publishers
## - nbPubSub both subscribers and publishers
## - 1 broker
## - QoS level is qos.
## The CCA program is saved in the file specified in the variable PATH.
##
## Copyright F. Siewe, 03 April 2025.
##
PATH = "mqtt.cca"
SPACE = " "
nbTopic = 10
nbSub = 4
nbPub = 4
nbPubSub = 2
qos = 2
def topic(topicId):
    topicN = "topic"+str(topicId)
    slashL = "{"
    slashR = "}"
    trouve = False;
    res = ""
    res += f" proc {topicN}(topic, qos, retainFlag, payload) {slashL}"+"\n"
    for i in range(nbSub):
      if topicId == i%nbTopic:
        clientN = "subscriber"+str(i)
        res += f’’’ if
      <contains({clientN}) and contains({clientN}_{topicN})>
      {clientN}::send(PUBLISH, broker, topic, qos, 0, payload).if
        <qos=0> skip.0
        <qos=1> {clientN}::recv(puback).0
        <qos=2> {clientN}::recv(pubrec).{clientN}::send(PUBREL).
          {clientN}::recv(pubcomp).0
      fi
    < not(contains({clientN}) and contains({clientN}_{topicN})) > skip.0
    fi
’’’
        trouve = ((i + nbTopic) >= nbSub)
        if not trouve:
          res += " |\n"
    if topicId >= nbPubSub:
      res += " }\n"
      return res
    for i in range(nbPubSub):
      if topicId == i%nbTopic:
        clientN = "pubsuber"+str(i)
        if trouve:
          res += " |\n"
        res += f’’’ if
        <contains({clientN}) and contains({clientN}_{topicN})>
        {clientN}::send(PUBLISH, broker, topic, qos, 0, payload).if
          <qos=0> skip.0
          <qos=1> {clientN}::recv(puback).0
          <qos=2> {clientN}::recv(pubrec).{clientN}::send(PUBREL).
            {clientN}::recv(pubcomp).0
        fi
< not(contains({clientN}) and contains({clientN}_{topicN})) > skip.0
      fi
’’’
    res += " }\n"
    return res
def mem(topicN):
    res = " "+topicN+"[\n"
    res += ’’’ !@recv().recv(v1,v2).{ @send(v1,v2).0 | send(v1,v2).0 }
      | !@recv(x1,x2).recv(y1,y2).{ send(x1,x2).0 | @send().0 }
      | send(1,off).0
    ]
’’’
    return res
def publisher(clientId, topicN):
    client = "publisher"+str(clientId)
    res = client+"[\n"
    res += SPACE+"broker::send(CONNECT, "+client+").broker::recv(connack).\n"
    res += SPACE+"broker::send(PUBLISH, "+client+", "+topicN+", "+str(qos)
    res += ", 1, "+client+")."
    if qos == 0:
      res += "0\n]\n"
    elif qos == 1:
      res += "\n"+SPACE+"broker::recv(puback).0\n]\n"
    else:
      res += "\n"+SPACE
      res += "broker::recv(pubrec).broker::send(PUBREL).broker::recv(pubcomp).0\n"
      res += "]\n"
    return res
def subscriber(clientId, topicN):
    client = "subscriber"+str(clientId)
    res = client+"[\n"
    res += SPACE+’’’!broker::recv(packetType, _, topic, qos, retainFlag, payload).if
      <packetType=PUBLISH and qos=0> skip.0
      <packetType=PUBLISH and qos=1> broker::send(PUBACK).0
      <packetType=PUBLISH and qos=2> broker::send(PUBREC).
        broker::recv(pubrel).broker::send(PUBCOMP).0
     fi\n’’’
    res += SPACE+"| broker::send(CONNECT, "+client+").broker::recv(connack).\n"
    res += SPACE+" broker::send(SUBSCRIBE, "+client+", "
    res += topicN+").broker::recv(suback).0\n]\n"
    return res
def pubsuber(clientId, subtopicN, pubtopicN):
    client = "pubsuber"+str(clientId)
    res = client+"[\n"
    res += SPACE+’’’!broker::recv(packetType, _, topic, qos, retainFlag, payload).if
      <packetType=PUBLISH and qos=0> skip.0
      <packetType=PUBLISH and qos=1> broker::send(PUBACK).0
      <packetType=PUBLISH and qos=2> broker::send(PUBREC).
        broker::recv(pubrel).broker::send(PUBCOMP).0
     fi\n’’’
    res += SPACE+"| broker::send(CONNECT, "+client+").broker::recv(connack).\n"
    res += SPACE+" broker::send(SUBSCRIBE, "+client+", "+subtopicN
    res += ").broker::recv(suback).\n"+SPACE+" broker::send(PUBLISH, "
    res += client+", "+pubtopicN+", "+str(qos)+", 1, "+client+")."
    if qos == 0:
      res += "0\n]\n"
    elif qos == 1:
      res += "\n"+SPACE+" broker::recv(puback).0\n]\n"
    else:
      res += "\n"+SPACE+" broker::recv(pubrec).broker::send(PUBREL)."
      res += "broker::recv(pubcomp).0\n]\n"
    return res
def broker():
    res = "broker[\n"
    for i in range(nbTopic):
      if (i >= nbSub) and (i >= nbPubSub):
        break
      res += topic(i)
      if (i < nbTopic - 1) and (i+1 < nbSub or i+1 < nbPubSub):
        res += " |\n"
    for i in range(nbTopic):
      if (i >= nbSub) and (i >= nbPubSub):
        break
      res += " |\n"
      topicN = "topic"+str(i)
      res += mem(topicN)
    if (nbSub+nbPubSub > 0):
      res += " |\n"
    res += ’’’ !::recv(packetType, clientId).if
      <packetType=CONNECT> clientId::send(CONNACK).if
        <contains(clientId)> skip.0
        <not contains(clientId)> skip.clientId[0]
      fi
      <packetType=DISCONNECT and contains(clientId)> del clientId.0
fi
| !::recv(packetType, clientId, topic).if
      <packetType=SUBSCRIBE> let x=clientId+_+topic in topic#send().
        topic#recv(qos,msg).clientId::send(SUBACK).
        clientId::send(PUBLISH,broker,topic,qos,1,msg).if
        <qos=0 and contains(x)> skip.0
        <qos=1 and contains(x)> clientId::recv(ack).0
        <qos=2 and contains(x)> clientId::recv(pubrec).clientId::send(PUBREL).
          clientId::recv(pubcomp).0
        <qos=0 and not contains(x)> skip.x[0]
        <qos=1 and not contains(x)> clientId::recv(ack).x[0]
        <qos=2 and not contains(x)> clientId::recv(pubrec).
          clientId::send(PUBREL).clientId::recv(pubcomp).x[0]
      fi
      <packetType=UNSUBSCRIBE> let x=clientId+_+topic in topic#send().
        topic#recv(qos,msg).clientId::send(UNSUBACK).if
          <contains(x)> del x.0
          <not contains(x)> skip.0
      fi
fi
| !::recv(packetType,clientId,topic,qos,retainFlag, payload).if
    <packetType=PUBLISH and qos=0 and retainFlag=1> topic#send(qos,payload).
      topic#recv().topic(topic,qos,0,payload).0
    <packetType=PUBLISH and qos=0 and retainFlag=0>
      topic(topic,qos,0,payload).0
    <packetType=PUBLISH and qos=1 and retainFlag=1> topic#send(qos,payload).
      topic#recv().clientId::send(PUBACK).topic(topic,qos,0,payload).0
    <packetType=PUBLISH and qos=1 and retainFlag=0> clientId::send(PUBACK).
      topic(topic,qos,0,payload).0
    <packetType=PUBLISH and qos=2 and retainFlag=1> topic#send(qos,payload).
      topic#recv().clientId::send(PUBREC).clientId::recv(pubrel).
      clientId::send(PUBCOMP).topic(topic,qos,0,payload).0
    <packetType=PUBLISH and qos=2 and retainFlag=0> clientId::send(PUBREC).
      clientId::recv(pubrel).clientId::send(PUBCOMP).
      topic(topic,qos,0,payload).0
    fi
]’’’
    return res
def mqtt():
    res = ’’’BEGIN_DECLS
    def contains(n) = { somewhere (this | n[0] | true) }
    def contains(d,n) = { somewhere (d[n[0] | true] | true) }
    mode random
    //display code
    //display congruence
    //length = 100
    //ltl { <>(somewhere (broker[true] | true)) }
END_DECLS
  ’’’
    if nbTopic <= 0:
      res += broker()
      return res
    for i in range(nbSub):
      topic = "topic"+str(i%nbTopic)
      res += subscriber(i, topic)
      res += "|\n"
    for i in range(nbPubSub):
      subtopic = "topic"+str(i%nbTopic)
      pubtopic = "topic"+str((i+1)%nbTopic)
      res += pubsuber(i, subtopic, pubtopic)
      res += "|\n"
    for i in range(nbPub):
      topic = "topic"+str(i%nbTopic)
      res += publisher(i, topic)
      res += "|\n"
    res += broker()
    return res
# output
F = open(PATH, "w")
result = mqtt()
print(result)
F.write(result)
F.close()

References

  1. Myers, G.J.; Sandler, C.; Badgett, T. The Art of Software Testing; John Wiley & Sons: Hoboken, NJ, USA, 2011; Volume 3. [Google Scholar]
  2. Clarke, E.M.; Emerson, E.A.; Sistla, A.P. Automatic verification of finite-state concurrent systems using temporal logic specifications. ACM Trans. Program. Lang. Syst. (TOPLAS) 1986, 8, 244–263. [Google Scholar] [CrossRef]
  3. Holzmann, J.G. The Model Checker SPIN. IEEE Trans. Softw. Eng. 1997, 23, 279–295. [Google Scholar] [CrossRef]
  4. Nipkow, T.; Wenzel, M.; Paulson, L.C. Isabelle/HOL: A Proof Assistant for Higher-Order Logic; Springer: Berlin/Heidelberg, Germany, 2002. [Google Scholar]
  5. Gordon, M.J.; Melham, T.F. Introduction to HOL: A Theorem Proving Environment for Higher Order Logic; Cambridge University Press: Cambridge, UK, 1993. [Google Scholar]
  6. Riazanov, A.; Voronkov, A. Vampire. In Proceedings of the International Conference on Automated Deduction, Trento, Italy, 7–10 July 1999; Springer: Berlin/Heidelberg, Germany, 1999; pp. 292–296. [Google Scholar]
  7. Barrett, C.W.; Conway, C.L.; Deters, M.; Hadarean, L.; Jovanovic, D.; King, T.; Reynolds, A.; Tinelli, C. CVC4. In Proceedings of the Computer Aided Verification—23rd International Conference, CAV 2011, Snowbird, UT, USA, 14–20 July 2011; Gopalakrishnan, G., Qadeer, S., Eds.; Lecture Notes in Computer Science; Springer: Berlin/Heidelberg, Germany, 2011; Volume 6806, pp. 171–177. [Google Scholar] [CrossRef]
  8. Havelund, K.; Peled, D. Runtime Verification: From Propositional to First-Order Temporal Logic. In Proceedings of the Runtime Verification, Limassol, Cyprus, 10–13 November 2018; Colombo, C., Leucker, M., Eds.; Springer: Cham, Switzerland, 2018; pp. 90–112. [Google Scholar]
  9. Bartocci, E.; Falcone, Y.; Francalanza, A.; Reger, G. Introduction to Runtime Verification. In Lectures on Runtime Verification: Introductory and Advanced Topics; Bartocci, E., Falcone, Y., Eds.; Springer International Publishing: Cham, Switzerland, 2018; pp. 1–33. [Google Scholar]
  10. Havelund, K.; Roşu, G. Runtime verification-17 years later. In Proceedings of the International Conference on Runtime Verification, Limassol, Cyprus, 10–13 November 2018; Springer: Berlin/Heidelberg, Germany, 2018; pp. 3–17. [Google Scholar]
  11. Meredith, P.; Roşu, G. Runtime verification with the RV system. In Proceedings of the International Conference on Runtime Verification, St. Julians, Malta, 1–4 November 2010; Springer: Berlin/Heidelberg, Germany, 2010; pp. 136–152. [Google Scholar]
  12. Reger, G.; Cruz, H.C.; Rydeheard, D. MarQ: Monitoring at runtime with QEA. In Proceedings of the International Conference on Tools and Algorithms for the Construction and Analysis of Systems, Hamilton, ON, Canada, 6–11 April 2015; Springer: Berlin/Heidelberg, Germany, 2015; pp. 596–610. [Google Scholar]
  13. İnçki, K.; Ari, I. A novel runtime verification solution for IoT systems. IEEE Access 2018, 6, 13501–13512. [Google Scholar] [CrossRef]
  14. Kane, A. Runtime Monitoring for Safety-Critical Embedded Systems. Ph.D. Thesis, Carnegie Mellon University, Pittsburgh, PA, USA, 2015. [Google Scholar]
  15. Medhat, R.; Bonakdarpour, B.; Kumar, D.; Fischmeister, S. Runtime monitoring of cyber-physical systems under timing and memory constraints. ACM Trans. Embed. Comput. Syst. (TECS) 2015, 14, 1–29. [Google Scholar] [CrossRef]
  16. Leotta, M.; Ancona, D.; Franceschini, L.; Olianas, D.; Ribaudo, M.; Ricca, F. Towards a runtime verification approach for internet of things systems. In Proceedings of the International Conference on Web Engineering, Cáceres, Spain, 5–8 June 2018; Springer: Berlin/Heidelberg, Germany, 2018; pp. 83–96. [Google Scholar]
  17. Siewe, F.; Zedan, H.; Cau, A. The Calculus of Context-aware Ambients. J. Comput. Syst. Sci. 2011, 77, 597–620. [Google Scholar] [CrossRef]
  18. Siewe, F. ccaPL: An CCA Programming Environment. Available online: https://fsiewe.afrilocode.net/CCA/index.html (accessed on 23 November 2023).
  19. Cardelli, L.; Gordon, A.D. Mobile Ambients. Theor. Comput. Sci. 2000, 240, 177–213. [Google Scholar] [CrossRef]
  20. Pnueli, A. The temporal logic of programs. In Proceedings of the 18th Annual Symposium on Foundations of Computer Science (sfcs 1977), Providence, RI, USA, 31 October–2 November 1977; pp. 46–57. [Google Scholar]
  21. MQTT Version 3.1.1. Edited by Andrew Banks and Rahul Gupta. 29 October 2014. OASIS Standard. Available online: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html (accessed on 17 March 2025).
  22. MQTT Version 5.0. Edited by Andrew Banks, Ed Briggs, Ken Borgendale, and Rahul Gupta. 07 March 2019. OASIS Standard. Available online: https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html (accessed on 17 March 2025).
  23. ISO/IEC 20922:2016; Information Technology—Message Queuing Telemetry Transport (MQTT) v3.1.1.2016. ISO/IEC: Geneva, Switzerland, 2016. Available online: https://www.iso.org/standard/69466.html (accessed on 17 March 2025).
  24. Siewe, F. ccaRV: A Runtime Verification Tool for CCA. Available online: https://fsiewe.afrilocode.net/CCA/index.html (accessed on 23 April 2025).
  25. Taleb, R.; Hallé, S.; Khoury, R. Uncertainty in runtime verification: A survey. Comput. Sci. Rev. 2023, 50, 100594. [Google Scholar] [CrossRef]
  26. Almakhour, M.; Sliman, L.; Samhat, A.E.; Mellouk, A. Verification of smart contracts: A survey. Pervasive Mob. Comput. 2020, 67, 101227. [Google Scholar] [CrossRef]
  27. Colombo, C.; Pace, G.J.; Schneider, G. LARVA—Safer Monitoring of Real-Time Java Programs (Tool Paper). In Proceedings of the 2009 7th IEEE International Conference on Software Engineering and Formal Methods (SEFM 2009), Los Alamitos, CA, USA, 23–27 November 2009; pp. 33–37. [Google Scholar] [CrossRef]
  28. Ellul, J.; Pace, G.J. Runtime Verification of Ethereum Smart Contracts. In Proceedings of the 2018 14th European Dependable Computing Conference (EDCC), Iasi, Romania, 10–14 September 2018; pp. 158–163. [Google Scholar] [CrossRef]
  29. Caldas, R.; García, J.A.P.; Schiopu, M.; Pelliccione, P.; Rodrigues, G.; Berger, T. Runtime verification and field-based testing for ros-based robotic systems. IEEE Trans. Softw. Eng. 2024, 50, 2544–2567. [Google Scholar] [CrossRef]
  30. Mahe, E.; Bannour, B.; Gaston, C.; Le Gall, P. Efficient interaction-based offline runtime verification of distributed systems with lifeline removal. Sci. Comput. Program. 2025, 241, 103230. [Google Scholar] [CrossRef]
  31. Rodríguez, G.V.; Castañeda, A. Towards Efficient Runtime Verified Linearizable Algorithms. In Proceedings of the Runtime Verification, Istanbul, Turkey, 15–17 October 2024; Ábrahám, E., Abbas, H., Eds.; Springer: Cham, Switzerland, 2024; pp. 262–281. [Google Scholar]
  32. Castañeda, A.; Rodríguez, G.V. Asynchronous wait-free runtime verification and enforcement of linearizability. In Proceedings of the 2023 ACM Symposium on Principles of Distributed Computing, Orlando, FL, USA, 19–23 June 2023; pp. 90–101. [Google Scholar]
  33. Falcone, Y. On Decentralized Monitoring. In Proceedings of the Verification and Evaluation of Computer and Communication Systems, Tbilisi, Georgia, 28–30 September 2022; Nouri, A., Wu, W., Barkaoui, K., Li, Z., Eds.; Springer: Cham, Switzerland, 2022; pp. 1–16. [Google Scholar]
  34. Bonakdarpour, B.; Fraigniaud, P.; Rajsbaum, S.; Rosenblueth, D.; Travers, C. Decentralized Asynchronous Crash-resilient Runtime Verification. J. ACM 2022, 69, 34. [Google Scholar] [CrossRef]
  35. Bauer, A.; Falcone, Y. Decentralised LTL monitoring. Form. Methods Syst. Des. 2016, 48, 46–93. [Google Scholar] [CrossRef]
  36. Ganguly, R.; Kazemloo, S.; Bonakdarpour, B. Crash-Resilient Decentralized Synchronous Runtime Verification. IEEE Trans. Dependable Secur. Comput. 2024, 21, 1017–1031. [Google Scholar] [CrossRef]
  37. Visconti, E.; Bartocci, E.; Falcone, Y.; Nenzi, L. Adaptable Configuration of Decentralized Monitors. In Proceedings of the Formal Techniques for Distributed Objects, Components, and Systems, Groningen, The Netherlands, 17–21 June 2024; Springer: Berlin/Heidelberg, Germany, 2024; pp. 197–217. [Google Scholar] [CrossRef]
  38. Audrito, G.; Damiani, F.; Stolz, V.; Torta, G.; Viroli, M. Distributed runtime verification by past-CTL and the field calculus. J. Syst. Softw. 2022, 187, 111251. [Google Scholar] [CrossRef]
  39. Ancona, D.; Franceschini, L.; Ferrando, A.; Mascardi, V. RML: Theory and practice of a domain specific language for runtime verification. Sci. Comput. Program. 2021, 205, 102610. [Google Scholar] [CrossRef]
  40. Barrnger, H.; Goldberg, A.; Havelund, K.; Sen, K. Eagle monitors by collecting facts and generating obligations. In Proceedings of the TACAS”04, Warsaw, Poland, 7–11 April 2003. [Google Scholar]
  41. Meredith, P.O.; Jin, D.; Griffith, D.; Chen, F.; Roşu, G. An overview of the MOP runtime verification framework. Int. J. Softw. Tools Technol. Transf. 2012, 14, 249–289. [Google Scholar] [CrossRef]
  42. Garavel, H.; Mateescu, R. SEQ. OPEN: A tool for efficient trace-based verification. In Proceedings of the International SPIN Workshop on Model Checking of Software, Barcelona, Spain, 1–3 April 2004; Springer: Berlin/Heidelberg, Germany, 2004; pp. 151–157. [Google Scholar]
  43. Ferrando, A.; Malvone, V. Runtime Verification via Rational Monitor with Imperfect Information. ACM Trans. Softw. Eng. Methodol. 2025. [Google Scholar] [CrossRef]
  44. Aceto, L.; Attard, D.P.; Francalanza, A.; Ingólfsdóttir, A. On benchmarking for concurrent runtime verification. In Proceedings of the International Conference on Fundamental Approaches to Software Engineering, Luxembourg, 27 March–1 April 2021; Springer International Publishing: Cham, Switzerland, 2021; pp. 3–23. [Google Scholar]
  45. Aceto, L.; Achilleos, A.; Attard, D.P.; Exibard, L.; Francalanza, A.; Ingólfsdóttir, A. A monitoring tool for linear-time μHMLImage 1. Sci. Comput. Program. 2024, 232, 103031. [Google Scholar] [CrossRef]
  46. Sen, K.; Vardhan, A.; Agha, G.; Rosu, G. Decentralized runtime analysis of multithreaded applications. In Proceedings of the 20th IEEE International Parallel & Distributed Processing Symposium, Rhodes Island, Greece, 25–29 April 2006; p. 11. [Google Scholar]
  47. Hertz, B.; Luppen, Z.; Rozier, K.Y. Integrating runtime verification into a sounding rocket control system. In Proceedings of the NASA Formal Methods Symposium, Virtual, 24–28 May 2021; Springer: Berlin/Heidelberg, Germany, 2021; pp. 151–159. [Google Scholar]
  48. Zhao, Y.; Zhu, E.; Hoxha, B.; Fainekos, G.; Deshmukh, J.; Lindemann, L. Distributionally Robust Predictive Runtime Verification under Spatio-Temporal Logic Specifications. ACM Trans. Cyber Phys. Syst. 2025, 9, 1–26. [Google Scholar] [CrossRef]
  49. Fredericks, E.M.; Ramirez, A.J.; Cheng, B.H. Towards run-time testing of dynamic adaptive systems. In Proceedings of the 2013 8th International Symposium on Software Engineering for Adaptive and Self-Managing Systems (SEAMS), San Francisco, CA, USA, 20–21 May 2013; pp. 169–174. [Google Scholar]
  50. Rozier, K.Y. On the Evaluation and Comparison of Runtime Verification Tools for Hardware and Cyber-Physical Systems. In Proceedings of the RV-CuBES, Seattle, WA, USA, 13–16 September 2017; pp. 123–137. [Google Scholar]
  51. Schirmer, S.; Torens, C. Safe operation monitoring for specific category unmanned aircraft. In Automated Low-Altitude Air Delivery: Towards Autonomous Cargo Transportation with Drones; Springer: Berlin/Heidelberg, Germany, 2021; pp. 393–419. [Google Scholar]
  52. Li, Q.; Li, Y.; Mao, X.; Wang, T.; Li, T. A Framework for Runtime Safety of Industrial Control Systems Through Runtime Verification. IEEE Internet Things J. 2025, 12, 15587–15599. [Google Scholar] [CrossRef]
Disclaimer/Publisher’s Note: The statements, opinions and data contained in all publications are solely those of the individual author(s) and contributor(s) and not of MDPI and/or the editor(s). MDPI and/or the editor(s) disclaim responsibility for any injury to people or property resulting from any ideas, methods, instructions or products referred to in the content.

Article Metrics

Citations

Article Access Statistics

Multiple requests from the same IP address are counted as one view.