set ignoreTypes = false.

set verboseRules = true.

free target:bitstring [private].
free dummy:bitstring.

fun pubs(bitstring):bitstring.
fun password(bitstring):bitstring [private].
fun SignAUTH(bitstring,bitstring):bitstring.

reduc forall Sk:bitstring,M:bitstring; VerifyAUTH(pubs(Sk),M,SignAUTH(Sk,M)) = true.

reduc forall Sk:bitstring,M:bitstring; getMessageAUTH(pubs(Sk),SignAUTH(Sk,M)) = M.

free cp:channel.
free cellB:channel [private].
free cEpoch:channel [private].
free lockV:channel [private].
free lockS:channel [private].

table tab(bitstring).

const ALICE:bitstring.

event Requested(bitstring,nat).
event Accepted(bitstring,nat).
event Verified(bitstring,nat).
event Rejected(bitstring).

set preciseActions = true.

event Increase.

(*set nounifIgnoreAFewTimes = auto.*)
(*set nounifIgnoreNtimes = 2.*)

type stamp.

free id:stamp [private].

event Counter(channel,stamp,stamp,nat).
event Not_in_tab(nat,bitstring).
event In_tab(nat,bitstring).
event VCell_bitstring(channel,bitstring).
event UAction_bitstring(stamp,bitstring).

event EpochLockV(nat,nat).
event EpochLockS(nat,nat).
event RecordFirst(nat,bitstring).
event RequestedPlus(bitstring,nat).

nounif i:nat; mess(lockV, *i) .
nounif i:nat; mess(lockS, i).

axiom i:nat,i1:nat,st:stamp,st1:stamp,st2:stamp,x:bitstring,x1:bitstring,d:channel;
  event(UAction_bitstring(st2,x1)) && event(UAction_bitstring(st2,x)) ==> x1 = x;
  event(VCell_bitstring(d,(i,x1))) && event(VCell_bitstring(d,(i,x))) ==> x1 = x;
  event(In_tab(i,x)) && event(Not_in_tab(i1,x)) ==> i > i1;
  event(Counter(d,st2,st,i1)) && event(Counter(d,st2,st,i)) ==> i1 = i;
  event(Counter(d,st2,st,i1)) && event(Counter(d,st2,st1,i1)) ==> st = st1.

axiom st:stamp, i, j, i',j' : nat;
  event(EpochLockV(i,j)) && event(EpochLockV(i,j')) ==> j = j' [induction].

axiom st:stamp, i, j, i',j' : nat;
  event(EpochLockV(i,j)) && event(EpochLockV(i,j')) ==> (i <= i' || j >= j') && (i >= i' || j <= j') [induction].

axiom st:stamp, i, j, i',j' : nat;
  event(EpochLockS(i,j)) && event(EpochLockS(i,j')) ==> (i <= i' || j >= j') && (i >= i' || j <= j') [induction].

(*lemma x:bitstring, st:stamp, i, j, i',j' : nat;
  event(Accepted(x,i)) ==>
    (x = new n1[epochV = j] && i = j) ||
    (x = new n2[epochV = j] && i = j) [induction].
*)
(*lemma x:bitstring, st:stamp, i, j, i',j' : nat;
  event(RequestedPlus(x,i)) ==>
    (x = new n1[xLock = j] && i = j) ||
    (x = new n2[xLock = j] && i = j) [induction].
*)

set verboseLemmas = true.

axiom i,i',j,j':nat, x,x':bitstring;
  event(VCell_bitstring(cellB,(i,x))) && event(VCell_bitstring(cellB,(i',x'))) ==>
    (i = 0 && x = dummy) || (i' = 0 && x' = dummy) ||
    (
      (x = new n1[xLock = j] || x = new n2[xLock = j]) &&
      (x' = new n1[xLock = j'] || x' = new n2[xLock = j']) &&
      (i <= i' || j > j') && (i >= i' || j < j')
    ) [induction].

query epoch1:nat,epoch2:nat,n:bitstring,n':bitstring,i:nat,i1:nat,st:stamp,st1:stamp,st2:stamp,x:bitstring,x1:bitstring,d:channel;
  event(Verified(n,epoch1)) && event(Rejected(n)) ==>
        event(Requested(n',epoch2)) && n' <> n && epoch1 <= epoch2 [induction].

process
  (
    event VCell_bitstring(cellB,(0,dummy));
    out(cellB,(0,dummy));
    0
  ) | (
    out(lockV,0);
    0
  ) | (
    !(
    in(lockV,x:nat);
    out(lockV,x);
    0
    )  ) | (
    out(lockS,0);
    0
  ) | (
    !(
    in(lockS,x:nat);
    out(lockS,x);
    0
    )  ) | (
    out(cEpoch,0);
    0
  ) | (
    !(
    in(cEpoch,x:nat);
    out(cEpoch,x);
    0
    )  ) | (
    !(
    in(lockV,xLock:nat);
    new st[]:stamp;
    event Counter(lockV,id,st,xLock);
    in(cEpoch,epochV:nat);
    event EpochLockV(xLock,epochV);
    out(cEpoch,epochV);
    new n1:bitstring;
    event RequestedPlus(n1,xLock);
    event Requested(n1,epochV);
    out(cp,SignAUTH(password(ALICE),(n1,epochV)));
    in(cellB,(i:nat,= n1)); (* The first nat variable has been added due to the cell parameter *)
    event VCell_bitstring(cellB,(i,n1));
    event Verified(n1,epochV);
    out(cellB,(i,n1));
    out(lockV,xLock + 1);
    0
    )  ) | (
    !(
    in(lockV,xLock:nat);
    new st[]:stamp;
    event Counter(lockV,id,st,xLock);
    in(cEpoch,epochV:nat);
    event EpochLockV(xLock,epochV);
    out(cEpoch,epochV);
    new n2:bitstring;
    event RequestedPlus(n2,xLock);
    event Requested(n2,epochV);
    out(cp,SignAUTH(password(ALICE),(n2,epochV)));
    out(lockV,xLock + 1);
    0
    )  ) | (
    !(
    in(lockS,xLock:nat);
    new st[]:stamp;
    event Counter(lockS,id,st,xLock);
    in(cEpoch,epochS:nat);
    event EpochLockS(xLock,epochS);
    out(cEpoch,epochS);
    in(cp,x:bitstring);
    let (xn:bitstring,= epochS) = getMessageAUTH(pubs(password(ALICE)),x) in
    event Accepted(xn,epochS);
    get tab(= pubs(password(ALICE))) in
      new st1[]:stamp;
      event UAction_bitstring(st1,pubs(password(ALICE)));
      event In_tab(xLock,pubs(password(ALICE)));
      in(cellB,(i:nat,xnOld:bitstring)); (* The first nat variable has been added due to the cell parameter *)
      event VCell_bitstring(cellB,(i,xnOld));
      new st2[]:stamp;
      if xnOld = xn
      then
        out(cellB,(i,xn));
        out(lockS,xLock + 1);
        0
      else
        event VCell_bitstring(cellB,(i + 1,xn));
        event Rejected(xnOld);
        out(cp,target);
        out(cellB,(i + 1,xn));
        out(lockS,xLock + 1);
        0
    else
      event Not_in_tab(xLock,pubs(password(ALICE)));
      event In_tab(xLock + 1,pubs(password(ALICE)));
      insert tab(pubs(password(ALICE)));
      in(cellB,(i:nat,xnOld:bitstring)); (* The first nat variable has been added due to the cell parameter *)
      event RecordFirst(i,xnOld);
      event VCell_bitstring(cellB,(i,xnOld));
      new st1[]:stamp;
      event VCell_bitstring(cellB,(i + 1,xn));
      out(cellB,(i + 1,xn));
      out(lockS,xLock + 1);
      0
    )  ) | (
    !(
    in(cEpoch,epochM:nat);
    new st[]:stamp;
    event Counter(cEpoch,id,st,epochM);
    event Increase();
    out(cEpoch,epochM + 1);
    0
    )  ) | (
    0
  )
