如何正式验证以下协议是否正确?

时间:2018-03-09 14:16:58

标签: model-checking spin promela

在以下代码示例中,发件人接收器交换 - 未确定的时间 - 多个数据包。由发件人发送的每封邮件都包含01以及序列号。每当 Receiver 消息时,它会检查它是否为新消息,在这种情况下,它会向发件人发送确认。收到ACK后,发件人会发送包含新内容的邮件。

在此模型中,无论何时发送消息,消息都可能丢失,发送两次或正常发送。

mtype = { MESSAGE, ACK };

chan sender2receiver = [2] of { mtype, bit, int };
chan receiver2sender = [2] of { mtype, bit, int };

inline unreliable_send(channel, type, tag, seqno) {
    bool loss = false;
    bool duplicate = true;
    if
        :: channel!type(tag, seqno);
            if
                :: channel!type(tag, seqno);
                :: duplicate = false;
            fi
        :: loss = true;
    fi
}

active proctype Sender () {
    bit in_bit, out_bit;
    int seq_no;

    do
        :: unreliable_send(sender2receiver, MESSAGE, out_bit, seq_no) ->
            receiver2sender?ACK(in_bit, 0);
            if
                :: in_bit == out_bit ->
                    out_bit = 1 - out_bit;
                    seq_no++;
                :: else ->
                    skip;
            fi;
    od;
}

active proctype Receiver () {
    bit in_bit, old_bit;
    int seq_no;

    do
        :: sender2receiver?MESSAGE(in_bit, seq_no) ->
            if
                :: in_bit != old_bit ->
                    printf("received: %d\n", seq_no);
                    old_bit = in_bit;
                :: else ->
                    skip;
            fi;
            unreliable_send(receiver2sender, ACK, in_bit, 0);
    od;
}

在之前的模型中,丢失数据包会导致死锁

为了解决这个问题,讲师讨论了几种方法。

其中一种方法是让发件人无情,以便它一直发送消息,直到它有效地传递给接收器及其相应的发件人正确接收了ack。的即:

active proctype Sender () {
    bit in_bit, out_bit;
    int seq_no;

    // solution 1: keep sending a message till an ack is received
    do
        :: unreliable_send(sender2receiver, MESSAGE, out_bit, seq_no);
        :: receiver2sender?ACK(in_bit, 0);
            if
                :: in_bit == out_bit ->
                    out_bit = 1 - out_bit;
                    seq_no++;
                :: else ->
                    skip;
            fi;
    od;
}

虽然在实践中这种方法似乎有效,这意味着模拟模型不再导致僵局,讲师警告我们,它仍然会通过正式验证的步骤,因为至少退出一个不幸的执行路径,在某些时候所有消息都会丢失,无论多少人坚持再次发送相同的消息。

讲师邀请我们思考如何使用Spin找到提议方法失败的执行跟踪之一。由于我们尚未涵盖LTL模型检查,因此解决方案应基于assert标签

1 个答案:

答案 0 :(得分:0)

该协议背后的理念是发件人接收器不断交换新邮件

可以通过进度标签来验证提议的解决方案是否有效地遵守了此规范。

  

进度标签用于定义正确性声明。进步   label表示标记的全局状态必须是的要求   经常在任何无限的系统执行中无限访问。任何   验证者可以将违反此要求的行为报告为   非进展周期。

在这个模型中,进度状态是我们确定消息被正确发送和确认的状态,因此我们的进度状态就是我们意识到这一事实之后的状态。

因此,我们按如下方式修改发件人代码,并在progress:之后添加in_bit == out_bit ->标签:

active proctype Sender () {
    bit in_bit, out_bit;
    int seq_no;

    // solution 1: keep sending a message till an ack is received
    do
        :: unreliable_send(sender2receiver, MESSAGE, out_bit, seq_no);
        :: receiver2sender?ACK(in_bit, 0);
            if
                :: in_bit == out_bit ->
progress:
                    out_bit = 1 - out_bit;
                    seq_no++;
                :: else ->
                    skip;
            fi;
    od;
}

然后我们可以正式验证模型如下:

~$ spin -a -DNP test.pml
~$ gcc -DNP pan.c -o run 
~$ ./run -l -bfs

或者,使用以下单行:

~$ spin -search -l test.pml

正如所料,spin找到非进度周期

pan:1: non-progress cycle (at depth 16)
pan: wrote test.pml.trail

(Spin Version 6.4.7 -- 19 August 2017)
Warning: Search not completed
    + Partial Order Reduction

Full statespace search for:
    never claim             + (:np_:)
    assertion violations    + (if within scope of claim)
    non-progress cycles     + (fairness disabled)
    invalid end states      - (disabled by never claim)

State-vector 88 byte, depth reached 41, errors: 1
       21 states, stored
        0 states, matched
       21 transitions (= stored+matched)
        0 atomic steps
hash conflicts:         0 (resolved)

Stats on memory usage (in Megabytes):
    0.002   equivalent memory usage for states (stored*(State-vector + overhead))
    0.287   actual memory usage for states
  128.000   memory used for hash table (-w24)
    0.534   memory used for DFS stack (-m10000)
  128.730   total actual memory usage



pan: elapsed time 0 seconds

我们现在可以模拟 反例并显示打印违规执行曲目:

~$ spin -p -l -g -t test.pml
starting claim 2
spin: couldn't find claim 2 (ignored)
using statement merging
  2:    proc  0 (Sender:1) test.pml:8 (state 1) [loss = 0]
        Sender(0):loss = 0
  2:    proc  0 (Sender:1) test.pml:9 (state 2) [duplicate = 1]
        Sender(0):duplicate = 1
        Sender(0):loss = 0
  4:    proc  0 (Sender:1) test.pml:10 (state 3)    [sender2receiver!2,out_bit,seq_no]
        queue 1 (sender2receiver): [MESSAGE,0,0]
  6:    proc  1 (Receiver:1) test.pml:44 (state 1)  [sender2receiver?MESSAGE,in_bit,seq_no]
        queue 1 (sender2receiver): 
        Receiver(1):seq_no = 0
        Receiver(1):in_bit = 0
  8:    proc  1 (Receiver:1) test.pml:49 (state 5)  [else]
        queue 1 (sender2receiver): 
 10:    proc  1 (Receiver:1) test.pml:50 (state 6)  [(1)]
        queue 1 (sender2receiver): 
 12:    proc  1 (Receiver:1) test.pml:8 (state 9)   [loss = 0]
        queue 1 (sender2receiver): 
        Receiver(1):loss = 0
 12:    proc  1 (Receiver:1) test.pml:9 (state 10)  [duplicate = 1]
        queue 1 (sender2receiver): 
        Receiver(1):duplicate = 1
        Receiver(1):loss = 0
 14:    proc  1 (Receiver:1) test.pml:10 (state 11) [receiver2sender!1,in_bit,0]
        queue 1 (sender2receiver): 
        queue 2 (receiver2sender): [ACK,0,0]
 16:    proc  1 (Receiver:1) test.pml:12 (state 12) [receiver2sender!1,in_bit,0]
        queue 1 (sender2receiver): 
        queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
  <<<<<START OF CYCLE>>>>>
 18:    proc  0 (Sender:1) test.pml:12 (state 4)    [sender2receiver!2,out_bit,seq_no]
        queue 1 (sender2receiver): [MESSAGE,0,0]
        queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
 20:    proc  1 (Receiver:1) test.pml:44 (state 1)  [sender2receiver?MESSAGE,in_bit,seq_no]
        queue 1 (sender2receiver): 
        queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
        Receiver(1):seq_no = 0
        Receiver(1):in_bit = 0
 22:    proc  1 (Receiver:1) test.pml:49 (state 5)  [else]
        queue 1 (sender2receiver): 
        queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
 24:    proc  1 (Receiver:1) test.pml:50 (state 6)  [(1)]
        queue 1 (sender2receiver): 
        queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
 26:    proc  1 (Receiver:1) test.pml:8 (state 9)   [loss = 0]
        queue 1 (sender2receiver): 
        queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
        Receiver(1):loss = 0
 26:    proc  1 (Receiver:1) test.pml:9 (state 10)  [duplicate = 1]
        queue 1 (sender2receiver): 
        queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
        Receiver(1):duplicate = 1
        Receiver(1):loss = 0
 28:    proc  1 (Receiver:1) test.pml:15 (state 16) [loss = 1]
        queue 1 (sender2receiver): 
        queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
        Receiver(1):loss = 1
 30:    proc  0 (Sender:1) test.pml:8 (state 1) [loss = 0]
        queue 1 (sender2receiver): 
        queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
        Sender(0):loss = 0
 30:    proc  0 (Sender:1) test.pml:9 (state 2) [duplicate = 1]
        queue 1 (sender2receiver): 
        queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
        Sender(0):duplicate = 1
        Sender(0):loss = 0
 32:    proc  0 (Sender:1) test.pml:10 (state 3)    [sender2receiver!2,out_bit,seq_no]
        queue 1 (sender2receiver): [MESSAGE,0,0]
        queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
 34:    proc  1 (Receiver:1) test.pml:44 (state 1)  [sender2receiver?MESSAGE,in_bit,seq_no]
        queue 1 (sender2receiver): 
        queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
        Receiver(1):seq_no = 0
        Receiver(1):in_bit = 0
 36:    proc  1 (Receiver:1) test.pml:49 (state 5)  [else]
        queue 1 (sender2receiver): 
        queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
 38:    proc  1 (Receiver:1) test.pml:50 (state 6)  [(1)]
        queue 1 (sender2receiver): 
        queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
 40:    proc  1 (Receiver:1) test.pml:8 (state 9)   [loss = 0]
        queue 1 (sender2receiver): 
        queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
        Receiver(1):loss = 0
 40:    proc  1 (Receiver:1) test.pml:9 (state 10)  [duplicate = 1]
        queue 1 (sender2receiver): 
        queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
        Receiver(1):duplicate = 1
        Receiver(1):loss = 0
 42:    proc  1 (Receiver:1) test.pml:15 (state 16) [loss = 1]
        queue 1 (sender2receiver): 
        queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
        Receiver(1):loss = 1
spin: trail ends after 42 steps
#processes: 2
        queue 1 (sender2receiver): 
        queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
 42:    proc  1 (Receiver:1) test.pml:43 (state 20)
        Receiver(1):loss = 1
 42:    proc  0 (Sender:1) test.pml:11 (state 6)
2 processes created