Open prpr2770 opened 2 years ago
While making the following assumptions:
udp
requires definition of a single node
type, that gets mapped to a process in the OS. interface
as being defined by the actions, but to first capture the send
/recv
functionality. And then build the action definitions
around the derived structure. left-players
and the ODD-Numbered Nodes shall be right-players
. (src:node, dest:node)
. We obtain the following errors:
$ ivy_check trace=true ping_pong_trans_v4.ivy
...
The following program assertions are treated as guarantees:
in action node.next when called from refined_protocol.net.recv,refined_protocol.timer.timeout,refined_protocol.timer.timeout:
/home/user/.local/lib/python2.7/site-packages/ivy/include/1.7/order.ivy: line 92: guarantee ... FAIL
searching for a small model... done
two numerals assigned same value!: 2:node = 0:node
two numerals assigned same value!: 2:node = 0:node
two numerals assigned same value!: 2:node = 0:node
[
refined_protocol.net.sent(msg_ping,0) = true
refined_protocol.net.sent(msg_pong,0) = true
node.succ(0,0) = false
0:node < 0 = false
2:node = 0
0:node = 0
pid(0) = 0
0:id < 0 = false
0:node * 0 = 0
abstract_protocol.sent(msg_ping,0) = true
abstract_protocol.sent(msg_pong,0) = true
node.max = 0
refined_protocol.ball(0) = false
abstract_protocol.protocolSide(0,0,left) = false
abstract_protocol.protocolSide(0,0,right) = false
abstract_protocol.protocolSide(0,0,none) = false
]
fail call refined_protocol.net.recv
{
[
fml:v = msg_pong
fml:dst = 0
]
/home/user/.local/lib/python2.7/site-packages/ivy/include/1.7/udp.ivy: line 194: assume refined_protocol.net.sent(fml:v,fml:dst)
ping_pong_trans_v4.ivy: line 213: refined_protocol.ball(fml:dst) := true
[
refined_protocol.ball(0) = true
loc:0 = 0
]
ping_pong_trans_v4.ivy: line 219: call loc:0 := node.next(fml:dst)
{
[
fml:x = 0
]
/home/user/.local/lib/python2.7/site-packages/ivy/include/1.7/order.ivy: line 92: assert exists S. S:node > fml:x
}
}
$ ivy_check ping_pong_trans_v4.ivy
...
error: failed checks: 4
We derive the following definition for the ping-pong
example.
#lang ivy1.7
# Attempt at combining the `left-node` and `right-node` into a single `node` description. Also, combining their functionalities.
# LEFT-PLAYERS are EVEN-NUMBERED.
include order
include timeout
include udp
isolate id = {
type this
specification{
instantiate totally_ordered(this)
}
implementation{
interpret this -> bv[8]
}
}
instance node : iterable # from library.order
object node = { ... # include the desription extracted from the definition of `iterable`
action get_next(x:this) returns (y:this)
action get_prev(x:this) returns (y:this)
specification{
# `get_next` has the property that:
# 1. either we wrap around ( i.e. the output is least element and the input is the greatest) [(y <= X & X <= x)]
# 2. or, the output is the successor of the input ( i.e the output is greater than input and that there's no other element in between them. ) [(x<y & ~(x <Z & Z<y)]
after get_next{
# default: (x<y & ~(x <Z & Z<y))
# wrap-around: (y <= X & X <= x)
ensure (y <= X & X <= x) | (x<y & ~(x <Z & Z<y))
}
after get_prev{
# wrap-around: (x <= X & X <= y)
# default: (y<x & ~(y <Z & Z<x))
ensure (x <= X & X <= y) | (y<x & ~(y <Z & Z<x))
}
}
# NOTE: we describe the property as an `ensure` : This suggests that the onus is on the implementation of this module to guarantee that this property holds
# Therefore, we need to write an appropriate implementation.
implementation{
implement get_next{
y:= 0 if x = node.max else x.next;
}
implement get_prev{
y:= node.max if x = 0 else x.prev;
}
}
}
parameter pid(N:node) : id
axiom [pid_injective] pid(N) = pid(M) <-> N = M
type side_t = {left, right, none}
type packet={msg_ping, msg_pong}
isolate abstract_protocol ={
# -------------------------------------
# INTERFACE:
# -------------------------------------
# HOW TO REPRESENT STATE - OF WHICH AGENT's side it is.
relation protocolSide(X:node, Y:node, Z:side_t)
action send(src:node, dest:node, p:packet) # node-n sends pid-p to the next-node in the ring.
action ping(src:node, dest:node)
action pong(src:node, dest:node)
# -------------------------------------
# SPECIFICATION:
# -------------------------------------
specification{
relation sent(P:packet, N:node)
after init{
sent(P,N) := false;
protocolSide(X,Y,none):= true;
# EVEN LEFT-PLAYERS
#protocolSide(X, X.next,left):= ( (X/2)*2 = X )
#require forall X:node. protocolSide(X, M,left) = ( (X/2)*2 = X ) & ( (M <= P & P <= X) | (X<M & ~(X <Q & Q<M)) )
require forall X:node. protocolSide(X, M,left) = ( (X/2)*2 = X ) & ( M = X + 1 )
}
after send{
sent(p, dest) := true;
}
before ping {
assert protocolSide(src, dest,left) = true;
protocolSide(src, dest,left) := false;
protocolSide(src, dest, right) := true;
}
# When `pong` occurs, the ball must be on the right. It moves to the left.
before pong {
assert protocolSide(dest, src, right) = true;
protocolSide( dest, src, right) := false;
protocolSide(dest, src, left) := true;
}
private{
# Conditions NOT IN FAU
#invariant sent(msg_ping, X) -> ( (X/2)*2 ~= X )
#invariant sent(msg_pong, X) -> ( (X/2)*2 = X )
}
}
} with node, id, pid_injective
isolate refined_protocol = {
# -------------------------------------
# INTERFACE:
# -------------------------------------
# THere's no interface. At each timer-expiry, the LEFT-NODE sends the PING-message if it has its turn set.
specification{
relation ball(S:node)
after init(self:node) {
ball(Y) := false;
ball(self) := true;
}
}
# -------------------------------------
# IMPLEMENTATION: SEQUENTIAL REACTIVE PROGRAM
# -------------------------------------
implementation{
#instantiate net: udp_simple2(node.leftnode, packet) # Networking Service: `address_type`: node, `packet_type`: packet
instantiate net: udp_simple2(node, packet)
# PARAMETERIZED collection of instances of the `timeout_sec` service:
instantiate timer(N:node) : timeout_sec # There is ONE instance of `timeout_sec` for each node
implement timer.timeout(self:node){
if ball(self){
if ((self/2) * 2 ~= self){ # Odd Numbers are Right-Players
call abstract_protocol.send(self, self.prev, msg_pong);
# PONG
call net.send(self, self.prev, msg_pong);
}else{ # Even Numbers are Left-Players
call abstract_protocol.send(self, self.next, msg_ping);
# PING
call net.send(self, self.next, msg_ping);
};
ball(self) := false;
};
}
implement net.recv(self:node, v:packet){
ball(self) := true;
if (v=msg_pong & (self/2) * 2 = self){
# LEFT-PLAYER(EVEN) RECEIVES PONG
call abstract_protocol.pong( self.next, self);
};
if (v=msg_ping & (self/2) * 2 ~= self){
# RIGHT-PLAYER(ODD) RECEIVES PING
call abstract_protocol.ping( self.prev, self);
};
}
}
private{
# [Prop 3] The state of the implementation should be mapped to corresponding states of the abstract-protocol state.
invariant net.sent(V,N) -> abstract_protocol.sent(V,N)
}
} with abstract_protocol, node, id, pid_injective
extract code(n:node) = refined_protocol(n), pid(n), node, id
We adopted a simpler strategy to get the UDP implementation working. However, it generates the following error:
$ ivy_check ping_pong_udp_sendrecv.ivy
...
Initialization must establish the invariant
player.id := Yfml:self:side_t
ping_pong_udp_sendrecv.ivy: line 59: error: multiply assigned: player.id
May I have some guidance on what causes the multiple-assignment? I'd really appreciate some help with this issue.
#lang ivy1.7
include udp
#include tcp
type side_t = {left,right}
type msg_t = {ping, pong}
isolate intf = {
# actions are parameterized by the receiver.
action ping(dst:side_t)
action pong(dst:side_t)
specification {
individual side : side_t
after init {
side := left
}
before ping {
require side = left;
side := right
}
before pong {
require side = right;
side := left
}
}
}
isolate player = {
action hit(self:side_t)
action objectify(self:side_t)
specification{
individual ball : bool
individual id: side_t
after init(self:side_t){
#call objectify(self);
id := self;
if (id=left){
ball := true;
}else{
ball := false;
};
}
after objectify(self:side_t){
if (self=left){
id := left;
ball := true;
}else{
id := right;
ball := false;
};
}
private{
invariant ball -> intf.side = left
}
}
implementation{
instance trans : udp_simple2(side_t, msg_t)
implement intf.pong(self:side_t) {
ball := true
}
implement intf.ping(self:side_t) {
ball := true
}
implement trans.recv(self:side_t, v:msg_t){
if (v = ping & ball){
#call intf.ping(left, dst);
call intf.ping(self);
};
if (v = pong & ball){
call intf.pong(self);
};
}
implement hit(self:side_t) {
if ball {
if(id = left){
#call intf.ping;
call trans.send(id, right, ping);
ball := false
}else{
#call intf.pong;
call trans.send(id, left, pong);
ball := false;
};
}
}
}
} with intf, side_t
export player.hit
We got the following description of the ping-pong
using udp.send-recv
to compile correctly. However, when we attempt to generate the executable, we obtain the following error:
$ ivyc ping_pong_udp_sendrecv_v2_b.ivy
ping_pong_udp_sendrecv_v2_b.ivy: line 55: error: cannot strip isolate parameters from ext:player.hit
Modification of the code to the following gist generates the following error:
$ ivyc ping_pong_udp_sendrecv_v2_b_1.ivy
ping_pong_udp_sendrecv_v2_b_1.ivy: line 79: error: mixin player.intf_pong[implement31] has wrong number of input parameters for intf.pong
The code that ivy_check OK
is provided below:
#lang ivy1.8
include udp
type side_t = {left,right}
type msg_t = {ping, pong}
isolate intf = {
action ping(dest:side_t)
action pong(dest:side_t)
specification {
individual side : side_t
after init {
side := left
}
before ping {
require side = left;
side := right
}
before pong {
require side = right;
side := left
}
}
}
isolate player = {
individual ball : bool
individual id: side_t
after init{
if *{
id := left;
ball := true;
}else{
id := right;
ball := false;
};
}
action hit = {
if ball {
if(id = left){
#call intf.ping;
ball := false;
call trans.send(id, right, ping);
}else{
#call intf.pong;
ball := false;
call trans.send(id, left, pong);
};
}
}
implementation{
instance trans : udp_simple2(side_t, msg_t)
implement intf.pong {
#require id=left;
ball := true;
}
implement intf.ping {
#require id=right;
ball := true;
}
implement trans.recv(self:side_t, v:msg_t){
#require ~ball;
if (v = ping & ~ball & id=left & id=self){
call intf.ping(self);
};
if (v = pong & ~ball& id=right & id=self){
call intf.pong(self);
};
}
}
private{
# 1:
invariant ball & id = left -> intf.side = left
# 2:
invariant ball & id = right -> intf.side = right
# 3: constraints on the system messages
invariant ~trans.sent(ping,left) & ~trans.sent(pong,right)
}
} with intf, side_t
export player.hit
extract code(n:side_t) = player(n), intf, side_t
Compiled using IVy 1.7
I'm attempting to re-implement the PING-PONG tutorial, with the networking module, so that the actions
ping
andpong
may be translated into messages sent over the network. The correctness of theping-pong
protocol, should be checked for correctness, in consideration of the network dynamics.source:Specification Tutorial
I have attempted to implement this, using the following codebase. However, it generates the following error:
Is there an alternative/easier representation that's possible within IVy? Or is the code-extraction within IVy designed only for symmetric protocols?