modelator 0.4.2

A framework and tools for model-based testing.
Documentation
------------------------------ MODULE Indices ------------------------------
\* This is the model of Substrate Frame Indices from 
\* https://github.com/paritytech/substrate/tree/master/frame/indices 
\* at this commit: https://git.io/Ju5Jv
\*
\* An index is a short form of an address.
\* This module handles allocation of indices for a newly created accounts.
\*
\* This TLA+ model is created for demostrative purposes of MBT capabilities.
\* s2021 Andrey Kuprianov, Informal Systems

EXTENDS Integers, FiniteSets

CONSTANTS 
  \* @typeAlias: ADDR = Int;
  \* @typeAlias: INDEX = Int;
  \* @typeAlias: BALANCE = Int;
  \* @typeAlias: INDEX_ENTRY = [ addr: ADDR, deposit: Int, perm: Bool ];
  \* @typeAlias: INDICES = INDEX -> INDEX_ENTRY;
  \* @typeAlias: RESERVES = ADDR -> BALANCE;
  \* @typeAlias: ACTION = [type: Str, who: ADDR, new: ADDR, index: INDEX, freeze: Bool];
  \* How many users and indices are there? 
  \* @type: Int;
  NUM_USERS,
  \* @type: Int;
  NUM_INDICES

VARIABLES
 \* @type: INDICES;
  indices,
  \* @type: RESERVES;
  reserved,
  \* @type: ACTION;
  action,
  \* @type: Str;
  actionOutcome

Indices == 1..NUM_INDICES
Addresses == 0..NUM_USERS

\* A non-address
None == -1
\* A root address
Root == 0


\* Possible outcomes

\* Operation successful
OK == "OK"
\* The index was not already assigned.
NotAssigned == "NotAssigned"
\* The index is assigned to another account.
NotOwner == "NotOwner"
\* Only root can do this.
NotRoot == "NotRoot"
\* The index was not available.
InUse == "InUse"
\* The source and destination accounts are identical.
NotTransfer == "NotTransfer"
\* The index is permanent and may not be freed/changed.
Permanent == "Permanent"


ClaimOutcome(who, index) ==
  IF indices[index].who /= None
    THEN InUse
  ELSE OK

\* @type: (ADDR, INDEX) => Bool;
Claim(who, index) ==
  /\ indices' = [indices EXCEPT ![index] = 
       [who |-> who, deposit |-> 1, perm |-> FALSE] ]
  /\ reserved' = [reserved EXCEPT ![who] = @ + 1]


\* @type: (ADDR, ADDR, INDEX) => Str;
TransferOutcome(who, new, index) ==
  IF who = new
    THEN NotTransfer
  ELSE IF indices[index].who = None
    THEN NotAssigned
  ELSE IF indices[index].perm 
    THEN Permanent
  ELSE IF indices[index].who /= who
    THEN NotOwner
  ELSE 
    OK

\* @type: (ADDR, ADDR, INDEX) => Bool;
Transfer(who, new, index)==
  LET amount == indices[index].deposit IN
  /\ indices' = [indices EXCEPT ![index] = [who |-> new, deposit |-> amount, perm |-> FALSE]]
  /\ reserved' = [reserved EXCEPT ![who] = @ - amount, ![new] = @ + amount]


\* @type: (ADDR, INDEX) => Str;
FreeOutcome(who, index) ==
  IF indices[index].who = None
    THEN NotAssigned
  ELSE IF indices[index].perm 
    THEN Permanent
  ELSE IF indices[index].who /= who
    THEN NotOwner
  ELSE 
    OK

\* @type: (ADDR, INDEX) => Bool;
Free(who, index)==
  /\ indices' = [indices EXCEPT ![index] = [who |-> None, deposit |-> 0, perm |-> FALSE]]
  /\ reserved' = [reserved EXCEPT ![who] = @ - indices[index].deposit]


\* @type: (ADDR, ADDR, INDEX, Bool) => Str;
ForceTransferOutcome(who, new, index, freeze) ==
  IF who /= Root
    THEN NotRoot
  ELSE 
    OK

\* @type: (ADDR, ADDR, INDEX, Bool) => Bool;
ForceTransfer(who, new, index, freeze)==
  LET entry == indices[index] IN
    /\ indices' = [indices EXCEPT ![index] = [who |-> new, deposit |-> 0, perm |-> freeze]]
    /\ IF entry.who /= None THEN
         reserved' = [reserved EXCEPT ![who] = @ - indices[index].deposit]
       ELSE
         UNCHANGED reserved

\* @type: (ADDR, INDEX) => Str;
FreezeOutcome(who, index) ==
  IF indices[index].who = None
    THEN NotAssigned
  ELSE IF indices[index].perm
    THEN Permanent
  ELSE IF indices[index].who /= who
    THEN NotOwner
  ELSE OK

\* @type: (ADDR, INDEX) => Bool;
Freeze(who, index) ==
  /\ indices' = [indices EXCEPT ![index] = [who |-> who, deposit |-> 0, perm |-> TRUE]]
  /\ reserved' = [reserved EXCEPT ![who] = @ - indices[index].deposit]


\* Start with no occupied indices, and no reserved coins
Init == 
  /\ indices = [ i \in Indices |-> [ who |-> None, deposit |-> 0, perm |-> FALSE ] ]
  /\ reserved = [ a \in Addresses |-> 0 ]
  /\ action = [ type |-> "None" ]
  /\ actionOutcome = "None"


=============================================================================