diff --git a/_CoqProject b/_CoqProject
index 4489ccb2876733d2156d6e1c85882966758f62ea..496c210bafd16545c1169b4bf22789ac8fcb2f9a 100644
--- a/_CoqProject
+++ b/_CoqProject
@@ -61,3 +61,4 @@ multi_actris/channel/proto.v
 multi_actris/channel/channel.v
 multi_actris/channel/proofmode.v
 multi_actris/examples/basics.v
+multi_actris/examples/leader_election.v
diff --git a/multi_actris/channel/channel.v b/multi_actris/channel/channel.v
index 89937a2550979a790b45293561e19769fcebf966..11c498bcd9e20d10594e75f0d081d2e0c8376ae0 100644
--- a/multi_actris/channel/channel.v
+++ b/multi_actris/channel/channel.v
@@ -93,23 +93,21 @@ Definition tok `{!chanG Σ} (γ : gname) : iProp Σ := own γ (Excl ()).
 
 Definition chan_inv `{!heapGS Σ, !chanG Σ} γ γE γt i j (l:loc) : iProp Σ :=
   (l ↦ NONEV ∗ tok γt)%I ∨
-  (∃ v m, l ↦ SOMEV v ∗
+  (∃ v m p, l ↦ SOMEV v ∗
             iProto_own γ i (<(Send, j)> m)%proto ∗
-            (∃ p, iMsg_car m v (Next p) ∗ own γE (●E (Next p)))) ∨
+            iMsg_car m v (Next p) ∗ own γE (●E (Next p))) ∨
   (∃ p, l ↦ NONEV ∗
           iProto_own γ i p ∗ own γE (●E (Next p))).
 
 Definition iProto_pointsto_def `{!heapGS Σ, !chanG Σ}
     (c : val) (p : iProto Σ) : iProp Σ :=
-  ∃ γ γE1 (l:loc) (i:nat) (n:nat) p',
+  ∃ γ (γEs : list gname) (l:loc) (i:nat) (n:nat) p',
     ⌜ c = (#l,#n,#i)%V ⌝ ∗
-    inv (nroot.@"ctx") (iProto_ctx γ (set_seq 0 n)) ∗
-    ([∗list] j ↦ _ ∈ replicate n (),
-       ∃ γE2 γt1 γt2,
-         inv (nroot.@"p") (chan_inv γ γE1 γt1 i j (l +ₗ (pos n i j))) ∗
-         inv (nroot.@"p") (chan_inv γ γE2 γt2 j i (l +ₗ (pos n j i)))) ∗
+    inv (nroot.@"ctx") (iProto_ctx γ n) ∗
+    (∀ i j, ⌜i < n ⌝ -∗ ⌜j < n⌝ -∗
+      ∃ γt, inv (nroot.@"p") (chan_inv γ (γEs !!! i) γt i j (l +ₗ (pos n i j)))) ∗
     ▷ (p' ⊑ p) ∗
-    own γE1 (●E (Next p')) ∗ own γE1 (◯E (Next p')) ∗
+    own (γEs !!! i) (●E (Next p')) ∗ own (γEs !!! i) (◯E (Next p')) ∗
     iProto_own γ i p'.
 Definition iProto_pointsto_aux : seal (@iProto_pointsto_def). by eexists. Qed.
 Definition iProto_pointsto := iProto_pointsto_aux.(unseal).
@@ -121,19 +119,17 @@ Notation "c ↣ p" := (iProto_pointsto c p)
   (at level 20, format "c  ↣  p").
 
 Definition chan_pool `{!heapGS Σ, !chanG Σ}
-    (cs : val) (ps : gmap nat (iProto Σ)) : iProp Σ :=
-  ∃ γ (γEs : list gname) (l:loc) (n:nat),
-    ⌜cs = (#l,#n)%V⌝ ∗ ⌜∀ i, is_Some (ps !! i) → i < n⌝ ∗
-    inv (nroot.@"ctx") (iProto_ctx γ (set_seq 0 n)) ∗
+    (cs : val) (i':nat) (ps : list (iProto Σ)) : iProp Σ :=
+  ∃ γ (γEs : list gname) (n:nat) (l:loc),
+    ⌜cs = (#l,#n)%V⌝ ∗ ⌜(i' + length ps = n)%nat⌝ ∗
+    inv (nroot.@"ctx") (iProto_ctx γ n) ∗
+    (∀ i j, ⌜i < n ⌝ -∗ ⌜j < n⌝ -∗
+      ∃ γt, inv (nroot.@"p") (chan_inv γ (γEs !!! i) γt i j (l +ₗ (pos n i j)))) ∗
     [∗ list] i ↦ _ ∈ replicate n (),
-      (∀ p, ⌜ps !! i = Some p⌝ -∗
+      (∀ p, ⌜i' <= i⌝ -∗ ⌜ps !! (i - i') = Some p⌝ -∗
             own (γEs !!! i) (●E (Next p)) ∗
             own (γEs !!! i) (◯E (Next p)) ∗
-            iProto_own γ i p) ∗
-      [∗ list] j ↦ _ ∈ replicate n (),
-        ∃ γt1 γt2,
-        inv (nroot.@"p") (chan_inv γ (γEs !!! i) γt1 i j (l +ₗ (pos n i j))) ∗
-        inv (nroot.@"p") (chan_inv γ (γEs !!! j) γt2 j i (l +ₗ (pos n j i))).
+            iProto_own γ i p).
 
 Section channel.
   Context `{!heapGS Σ, !chanG Σ}.
@@ -203,63 +199,53 @@ Section channel.
   Qed.
 
   (** ** Specifications of [send] and [recv] *)
-  Lemma new_chan_spec (n:nat) (ps:gmap nat (iProto Σ)) :
-    0 < n →
-    dom ps = set_seq 0 n →
+  Lemma new_chan_spec (ps:list (iProto Σ)) :
+    0 < length ps →
     {{{ iProto_consistent ps }}}
-      new_chan #n
-    {{{ cs, RET cs; chan_pool cs ps }}}.
+      new_chan #(length ps)
+    {{{ cs, RET cs; chan_pool cs 0 ps }}}.
   Proof.
-    iIntros (Hle Hdom Φ) "Hps HΦ". wp_lam.
+    iIntros (Hle Φ) "Hps HΦ". wp_lam.
     wp_smart_apply wp_allocN; [lia|done|].
     iIntros (l) "[Hl _]".
     iMod (iProto_init with "Hps") as (γ) "[Hps Hps']".
     wp_pures. iApply "HΦ".
     iAssert (|==> ∃ (γEs : list gname),
-                ⌜length γEs = n⌝ ∗
-                [∗ list] i ↦ _ ∈ replicate n (),
+                ⌜length γEs = length ps⌝ ∗
+                [∗ list] i ↦ _ ∈ replicate (length ps) (),
                   own (γEs !!! i) (●E (Next (ps !!! i))) ∗
                   own (γEs !!! i) (◯E (Next (ps !!! i))) ∗
                   iProto_own γ i (ps !!! i))%I with "[Hps']" as "H".
     { clear Hle.
-      iInduction n as [|n] "IHn" forall (ps Hdom).
+      iInduction ps as [|p ps] "IHn" using rev_ind.
       { iExists []. iModIntro. simpl. done. }
-      assert (is_Some (ps !! n)) as [p HSome].
-      { apply elem_of_dom. rewrite Hdom. apply elem_of_set_seq. lia. }
-      iDestruct (big_sepM_delete _ _ n with "Hps'") as "[Hp Hps']"; [done|].
+      iDestruct "Hps'" as "[Hps' Hp]".
       iMod (own_alloc (●E (Next p) ⋅ ◯E (Next p))) as (γE) "[Hauth Hfrag]".
       { apply excl_auth_valid. }
-      iMod ("IHn" with "[] Hps'") as (γEs Hlen) "H".
-      { iPureIntro.
-        rewrite dom_delete_L Hdom.
-        replace (S n) with (n + 1) by lia.
-        rewrite set_seq_add_L /= right_id_L difference_union_distr_l_L
-          difference_diag_L right_id_L.
-        assert (n ∉ (set_seq 0 n:gset _)); [|set_solver].
-        intros Hin%elem_of_set_seq. lia. }
+      iMod ("IHn" with "Hps'") as (γEs Hlen) "H".
       iModIntro. iExists (γEs++[γE]).
-      replace (S n) with (n + 1) by lia.
-      rewrite replicate_add big_sepL_app app_length Hlen.
-      iSplit; [done|]=> /=.
+      rewrite !app_length Hlen.
+      iSplit; [iPureIntro=>/=;lia|]=> /=.
+      rewrite replicate_add.
       iSplitL "H".
       { iApply (big_sepL_impl with "H").
         iIntros "!>" (i ? HSome') "(Hauth & Hfrag & Hown)".
-        assert (i < n) as Hle.
+        assert (i < length ps) as Hle.
         { by apply lookup_replicate_1 in HSome' as [??]. }
-        assert (delete n ps !!! i = ps !!! i) as Heq'.
-        { apply lookup_total_delete_ne. lia. }
-        rewrite Heq'. iFrame.
-        rewrite lookup_total_app_l; [|lia]. iFrame. }
+        rewrite !lookup_total_app_l; [|lia..]. iFrame. }
       rewrite replicate_length Nat.add_0_r.
-      rewrite list_lookup_total_middle; [|done].
-      rewrite lookup_total_alt HSome. by iFrame. }
+      simpl. rewrite right_id_L.
+      rewrite !lookup_total_app_r; [|lia..]. rewrite !Hlen.
+      rewrite Nat.sub_diag. simpl. iFrame.
+      iDestruct "Hp" as "[$ _]". }
     iMod "H" as (γEs Hlen) "H".
+    set n := length ps.
     iAssert (|={⊤}=>
-        [∗ list] i ↦ _ ∈ replicate n (),
-          [∗ list] j ↦ _ ∈ replicate n (),
+        [∗ list] i ↦ _ ∈ replicate (length ps) (),
+          [∗ list] j ↦ _ ∈ replicate (length ps) (),
             ∃ γt,
             inv (nroot.@"p") (chan_inv γ (γEs !!! i) γt i j
-                                       (l +â‚— (pos n i j))))%I with "[Hl]" as "IH".
+                                       (l +â‚— (pos (length ps) i j))))%I with "[Hl]" as "IH".
     { replace (Z.to_nat (Z.of_nat n * Z.of_nat n)) with (n*n) by lia.
       iDestruct (array_to_matrix with "Hl") as "Hl".
       iApply big_sepL_fupd.
@@ -273,72 +259,77 @@ Section channel.
     iMod "IH" as "#IH".
     iMod (inv_alloc with "Hps") as "#IHp".
     iExists _,_,_,_.
-    iModIntro. iSplit; [done|].
+    iModIntro.
+    iSplit.
+    { iPureIntro. done. }
     iSplit.
-    { iPureIntro. intros i HSome.
-      apply elem_of_dom in HSome.
-      rewrite Hdom in HSome.
-      apply elem_of_set_seq in HSome. lia. }
-    rewrite Hdom. iFrame "IHp".
+    { done. }
+    iFrame "IHp".
+    iSplit.
+    { iIntros (i j Hi Hj).
+      rewrite (big_sepL_lookup _ _ i); [|by rewrite lookup_replicate].
+      rewrite (big_sepL_lookup _ _ j); [|by rewrite lookup_replicate].
+      iApply "IH". }
     iApply (big_sepL_impl with "H").
     iIntros "!>" (i ? HSome') "(Hauth & Hfrag & Hown)".
-    iSplitL.
-    { iIntros (p HSome'').
-      rewrite lookup_total_alt. rewrite HSome''.
-      iFrame. }
-    iApply big_sepL_intro.
-    iIntros "!>" (j ? HSome'').
-    assert (i < n) as Hle'.
-    { apply lookup_replicate in HSome' as [_ Hle']. done. }
-    assert (j < n) as Hle''.
-    { apply lookup_replicate in HSome'' as [_ Hle'']. done. }
-    iDestruct (big_sepL_lookup _ _ i () with "IH") as "IH'";
-      [by rewrite lookup_replicate|].
-    iDestruct (big_sepL_lookup _ _ j () with "IH'") as "IH''";
-      [by rewrite lookup_replicate|].
-    iDestruct (big_sepL_lookup _ _ j () with "IH") as "H";
-      [by rewrite lookup_replicate|].
-    iDestruct (big_sepL_lookup _ _ i () with "H") as "H'";
-      [by rewrite lookup_replicate|].
-    iDestruct "IH''" as (γ1) "?".
-    iDestruct "H'" as (γ2) "?".
-    iExists _, _. iFrame "#".
+    iIntros (p Hle' HSome'').
+    iFrame. rewrite right_id_L in HSome''.
+    rewrite (list_lookup_total_alt ps).
+    rewrite HSome''. simpl. iFrame.
   Qed.
 
-  Lemma get_chan_spec cs (i:nat) ps p :
-    ps !! i = Some p →
-    {{{ chan_pool cs ps }}}
+  Lemma get_chan_spec cs i ps p :
+    {{{ chan_pool cs i (p::ps) }}}
       get_chan cs #i
-    {{{ c, RET c; c ↣ p ∗ chan_pool cs (delete i ps) }}}.
+    {{{ c, RET c; c ↣ p ∗ chan_pool cs (i+1) ps }}}.
   Proof.
-    iIntros (HSome Φ) "Hcs HΦ".
-    iDestruct "Hcs" as (γp γEs l n -> Hle) "[#IHp Hl]".
-    wp_lam. wp_pures.
-    assert (i < n); [by apply Hle|].
-    iDestruct (big_sepL_delete _ _ i () with "Hl") as "[[Hi #IHs] H]";
-      [by apply lookup_replicate|].
-    iDestruct ("Hi" with "[//]") as "(Hauth & Hown & Hp)".
+    iIntros (Φ) "Hcs HΦ".
+    iDestruct "Hcs" as (γp γEs n l -> <-) "(#IHp & #IHl & Hl)".
+    wp_lam. wp_pures. 
+    rewrite replicate_add. simpl.
+    iDestruct "Hl" as "[Hl1 [Hi Hl3]]". 
+    iDestruct ("Hi" with "[] []") as "(Hauth & Hown & Hp)".
+    { rewrite right_id_L. rewrite replicate_length. iPureIntro. lia. }
+    { rewrite right_id_L. rewrite replicate_length.
+      rewrite Nat.sub_diag. simpl. done. }
     iModIntro.
     iApply "HΦ".
-    iSplitR "H".
+    iSplitR "Hl1 Hl3".
     { rewrite iProto_pointsto_eq. iExists _, _, _, _, _, _.
-      iSplit; [done|]. iFrame "#∗". iSplit; [|iNext; done].
-      iApply (big_sepL_impl with "IHs").
-      iIntros "!>" (???). iDestruct 1 as (γt1 γt2) "[??]".
-      iExists _,_,_. iFrame. }
-    iExists _, _, _, _. iSplit; [done|].
+      iSplit; [done|].
+      rewrite replicate_length. rewrite right_id_L.
+      iFrame. iFrame "#∗". iNext. done. }
+    iExists γp, γEs, _, _. iSplit; [done|].
     iSplit.
-    { iPureIntro. intros i' HSome'. apply Hle.
-      assert (i ≠ i').
-      { intros ->. rewrite lookup_delete in HSome'. by inversion HSome'. }
-      by rewrite lookup_delete_ne in HSome'. }
+    { iPureIntro. lia. }
+    iFrame. iFrame "#∗".
+    rewrite replicate_add.
+    simpl.
+    iSplitL "Hl1".
+    { iApply (big_sepL_impl with "Hl1").
+      iIntros "!>" (i' ? HSome'').
+      assert (i' < i).
+      { rewrite lookup_replicate in HSome''. lia. }
+      iIntros "H" (p' Hle'). lia. }
+    simpl.
     iFrame "#∗".
-    iApply (big_sepL_impl with "H").
+    iSplitR.
+    { iIntros (p' Hle'). rewrite right_id_L in Hle'.
+      rewrite replicate_length in Hle'. lia. }
+    iApply (big_sepL_impl with "Hl3").
     iIntros "!>" (i' ? HSome'').
-    case_decide.
-    { simplify_eq. iFrame "#".
-      iIntros "_" (p' Hin). simplify_eq. by rewrite lookup_delete in Hin. }
-    rewrite lookup_delete_ne; [|done]. by eauto.
+    assert (i' < length ps).
+    { rewrite lookup_replicate in HSome''. lia. }
+    iIntros "H" (p' Hle' HSome).
+    iApply "H".
+    { iPureIntro. lia. }
+    iPureIntro.
+    rewrite replicate_length.
+    rewrite replicate_length in HSome.
+    replace (i + S i' - i) with (S i') by lia.
+    simpl.
+    replace (i + S i' - (i+1)) with (i') in HSome by lia.
+    done.
   Qed.
 
   Lemma vpos_spec (n i j : nat) :
@@ -359,44 +350,42 @@ Section channel.
     iDestruct "Hc" as
       (γ γE l i n p' ->) "(#IH & #Hls & Hle & H● & H◯ & Hown)".
     wp_bind (Fst _).
-    iInv "IH" as "HI".
+    iInv "IH" as "Hctx".
     iDestruct (iProto_le_msg_inv_r with "Hle") as (m') "#Heq".
     iRewrite "Heq" in "Hown".
+    iAssert (▷ (⌜i < n⌝ ∗ iProto_own γ i (<(Send, j)> m') ∗
+                iProto_ctx γ n))%I with "[Hctx Hown]"
+      as "[Hi [Hown Hctx]]".
+    { iNext. iDestruct (iProto_ctx_agree with "Hctx Hown") as %Hi.
+      iFrame. done. }
     iAssert (▷ (▷ ⌜j < n⌝ ∗ iProto_own γ i (<(Send, j)> m') ∗
-                iProto_ctx γ (set_seq 0 n)))%I with "[HI Hown]"
-      as "[HI [Hown Hctx]]".
-    { iNext. iDestruct (iProto_target with "HI Hown") as "[Hin [$ $]]".
-      iFrame. iNext. iDestruct "Hin" as %Hin. by set_solver. }
+                iProto_ctx γ n))%I with "[Hctx Hown]"
+      as "[Hj [Hown Hctx]]".
+    { iNext. iDestruct (iProto_target with "Hctx Hown") as "[Hin [$ $]]".
+      iFrame. }
     iRewrite -"Heq" in "Hown". wp_pures. iModIntro. iFrame.
     wp_smart_apply (vpos_spec); [done|]; iIntros "_".
-    iDestruct "HI" as %Hle.
-    iDestruct (big_sepL_lookup_acc with "Hls") as "[Hj _]";
-      [by apply lookup_replicate_2|].
-    iDestruct "Hj" as (γE' γt γt') "#[IHl1 IHl2]".
+    iDestruct "Hi" as %Hi.
+    iDestruct "Hj" as %Hj.
+    iDestruct ("Hls" $! i j with "[//] [//]") as (γt) "IHl1". 
     wp_pures. wp_bind (Store _ _).
     iInv "IHl1" as "HIp".
     iDestruct "HIp" as "[HIp|HIp]"; last first.
     { iDestruct "HIp" as "[HIp|HIp]".
-      - iDestruct "HIp" as (? m) "(>Hl & Hown' & HIp)".
+      - iDestruct "HIp" as (? m p'') "(>Hl & Hown' & HIp)".
         wp_store.
-        rewrite /iProto_own.
-        iDestruct "Hown" as (p'') "[Hle' Hown]".
-        iDestruct "Hown'" as (p''') "[Hle'' Hown']".
-        iDestruct (own_prot_excl with "Hown Hown'") as "H". done.
+        iDestruct (iProto_own_excl with "Hown Hown'") as %[].
       - iDestruct "HIp" as (p'') "(>Hl' & Hown' & HIp)".
         wp_store.
-        rewrite /iProto_own.
-        iDestruct "Hown" as (p''') "[Hle' Hown]".
-        iDestruct "Hown'" as (p'''') "[Hle'' Hown']".
-        iDestruct (own_prot_excl with "Hown Hown'") as "H". done. }
+        iDestruct (iProto_own_excl with "Hown Hown'") as %[]. }
     iDestruct "HIp" as "[>Hl' Htok]".
     wp_store.
     iMod (own_update_2 with "H● H◯") as "[H● H◯]"; [by apply excl_auth_update|].
     iModIntro.
     iSplitL "Hl' H● Hown Hle".
-    { iRight. iLeft. iIntros "!>". iExists _, _. iFrame.
+    { iRight. iLeft. iIntros "!>". iExists _, _, _. iFrame.
       iSplitL "Hown Hle"; [by iApply (iProto_own_le with "Hown Hle")|].
-      iExists _. iFrame. by rewrite iMsg_base_eq. }
+      by rewrite iMsg_base_eq. }
     wp_pures.
     iLöb as "HL".
     wp_lam.
@@ -406,10 +395,10 @@ Section channel.
     { iDestruct "HIp" as ">[Hl' Htok']".
       iDestruct (own_valid_2 with "Htok Htok'") as %[]. }
     iDestruct "HIp" as "[HIp|HIp]".
-    - iDestruct "HIp" as (? m) "(>Hl' & Hown & HIp)".
+    - iDestruct "HIp" as (? m p'') "(>Hl' & Hown & HIp)".
       wp_load. iModIntro.
       iSplitL "Hl' Hown HIp".
-      { iRight. iLeft. iExists _, _. iFrame. }
+      { iRight. iLeft. iExists _, _,_. iFrame. }
       wp_pures. iApply ("HL" with "HΦ Htok H◯").
     - iDestruct "HIp" as (p'') "(>Hl' & Hown & H●)".
       wp_load.
@@ -452,19 +441,23 @@ Section channel.
     iDestruct "Hc" as
       (γ γE l i n p' ->) "(#IH & #Hls & Hle & H● & H◯ & Hown)".
     do 6 wp_pure _. wp_bind (Fst _). wp_pure _.
-    iInv "IH" as "HI".
+    iInv "IH" as "Hctx".
     iDestruct (iProto_le_msg_inv_r with "Hle") as (m') "#Heq".
     iRewrite "Heq" in "Hown".
+    iAssert (▷ (⌜i < n⌝ ∗ iProto_own γ i (<(Recv, j)> m') ∗
+                iProto_ctx γ n))%I with "[Hctx Hown]"
+      as "[Hi [Hown Hctx]]".
+    { iNext. iDestruct (iProto_ctx_agree with "Hctx Hown") as %Hi.
+      iFrame. done. }
     iAssert (▷ (▷ ⌜j < n⌝ ∗ iProto_own γ i (<(Recv, j)> m') ∗
-                iProto_ctx γ (set_seq 0 n)))%I with "[HI Hown]" as "[HI [Hown Hctx]]".
-    { iNext. iDestruct (iProto_target with "HI Hown") as "[Hin [$$]]".
-      iFrame. iNext. iDestruct "Hin" as %Hin. by set_solver. }
+                iProto_ctx γ n))%I with "[Hctx Hown]" as "[Hj [Hown Hctx]]".
+    { iNext. iDestruct (iProto_target with "Hctx Hown") as "[Hin [$$]]".
+      iFrame. }
     iRewrite -"Heq" in "Hown". wp_pures. iModIntro. iFrame.
     wp_smart_apply (vpos_spec); [done|]; iIntros "_".
-    iDestruct "HI" as %Hle.
-    iDestruct (big_sepL_lookup_acc with "Hls") as "[Hj _]";
-      [by apply lookup_replicate_2|].
-    iDestruct "Hj" as (γE' γt γt') "#[IHl1 IHl2]".
+    iDestruct "Hi" as %Hi.
+    iDestruct "Hj" as %Hj.
+    iDestruct ("Hls" $! j i with "[//] [//]") as (γt) "IHl2".
     wp_pures.
     wp_bind (Xchg _ _).
     iInv "IHl2" as "HIp".
@@ -483,8 +476,7 @@ Section channel.
       { iRight. iRight. iExists _. iFrame. }
       wp_pures. iApply ("HL" with "[H● H◯ Hown Hle] HΦ").
       iExists _, _, _, _, _, _. iSplit; [done|]. iFrame "#∗". }
-    iDestruct "HIp" as (w m) "(>Hl' & Hown' & HIp)".
-    iDestruct "HIp" as (p'') "[Hm Hp']".
+    iDestruct "HIp" as (w m p'') "(>Hl' & Hown' & Hm & Hp')".
     iInv "IH" as "Hctx".
     wp_xchg.
     iDestruct (iProto_own_le with "Hown Hle") as "Hown".
diff --git a/multi_actris/channel/proofmode.v b/multi_actris/channel/proofmode.v
index 07c044920f750bd14e942c5043f7cc2e6ba20891..5f9d7b8fb105122ee7dc64e208c6ecb50395aef1 100644
--- a/multi_actris/channel/proofmode.v
+++ b/multi_actris/channel/proofmode.v
@@ -205,42 +205,14 @@ Tactic Notation "wp_recv_core" tactic3(tac_intros) "as" tactic3(tac) :=
   | _ => fail "wp_recv: not a 'wp'"
   end.
 
+
 Tactic Notation "wp_recv" "as" constr(pat) :=
   wp_recv_core (idtac) as (fun H => iDestructHyp H as pat).
-
 Tactic Notation "wp_recv" "(" simple_intropattern_list(xs) ")" "as" constr(pat) :=
   wp_recv_core (intros xs) as (fun H => iDestructHyp H as pat).
-Tactic Notation "wp_recv" "(" simple_intropattern_list(xs) ")" "as" "(" simple_intropattern(x1) ")"
-    constr(pat) :=
-  wp_recv_core (intros xs) as (fun H => iDestructHyp H as ( x1 ) pat).
-Tactic Notation "wp_recv" "(" simple_intropattern_list(xs) ")" "as" "(" simple_intropattern(x1)
-    simple_intropattern(x2) ")" constr(pat) :=
-  wp_recv_core (intros xs) as (fun H => iDestructHyp H as ( x1 x2 ) pat).
-Tactic Notation "wp_recv" "(" simple_intropattern_list(xs) ")" "as" "(" simple_intropattern(x1)
-    simple_intropattern(x2) simple_intropattern(x3) ")" constr(pat) :=
-  wp_recv_core (intros xs) as (fun H => iDestructHyp H as ( x1 x2 x3 ) pat).
-Tactic Notation "wp_recv" "(" simple_intropattern_list(xs) ")" "as" "(" simple_intropattern(x1)
-    simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) ")"
-    constr(pat) :=
-  wp_recv_core (intros xs) as (fun H => iDestructHyp H as ( x1 x2 x3 x4 ) pat).
-Tactic Notation "wp_recv" "(" simple_intropattern_list(xs) ")" "as" "(" simple_intropattern(x1)
-    simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4)
-    simple_intropattern(x5) ")" constr(pat) :=
-  wp_recv_core (intros xs) as (fun H => iDestructHyp H as ( x1 x2 x3 x4 x5 ) pat).
-Tactic Notation "wp_recv" "(" simple_intropattern_list(xs) ")" "as" "(" simple_intropattern(x1)
-    simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4)
-    simple_intropattern(x5) simple_intropattern(x6) ")" constr(pat) :=
-  wp_recv_core (intros xs) as (fun H => iDestructHyp H as ( x1 x2 x3 x4 x5 x6 ) pat).
-Tactic Notation "wp_recv" "(" simple_intropattern_list(xs) ")" "as" "(" simple_intropattern(x1)
-    simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4)
-    simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) ")"
-    constr(pat) :=
-  wp_recv_core (intros xs) as (fun H => iDestructHyp H as ( x1 x2 x3 x4 x5 x6 x7 ) pat).
-Tactic Notation "wp_recv" "(" simple_intropattern_list(xs) ")" "as" "(" simple_intropattern(x1)
-    simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4)
-    simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7)
-    simple_intropattern(x8) ")" constr(pat) :=
-  wp_recv_core (intros xs) as (fun H => iDestructHyp H as ( x1 x2 x3 x4 x5 x6 x7 x8 ) pat).
+Tactic Notation "wp_recv" "(" simple_intropattern_list(xs) ")" "as"
+    "(" ne_simple_intropattern_list(ys) ")" constr(pat) :=
+  wp_recv_core (intros xs) as (fun H => _iDestructHyp H ys pat).
     
 Lemma tac_wp_send `{!chanG Σ, !heapGS Σ} {TT : tele} Δ neg i js K (n:nat) w c v p m tv tP tp Φ :
   w = #n →
@@ -318,33 +290,10 @@ Tactic Notation "wp_send_core" tactic3(tac_exist) "with" constr(pat) :=
 
 Tactic Notation "wp_send" "with" constr(pat) :=
   wp_send_core (idtac) with pat.
-Tactic Notation "wp_send" "(" uconstr(x1) ")" "with" constr(pat) :=
-  wp_send_core (eexists x1) with pat.
-Tactic Notation "wp_send" "(" uconstr(x1) uconstr(x2) ")" "with" constr(pat) :=
-  wp_send_core (eexists x1; eexists x2) with pat.
-Tactic Notation "wp_send" "(" uconstr(x1) uconstr(x2) uconstr(x3) ")"
-    "with" constr(pat) :=
-  wp_send_core (eexists x1; eexists x2; eexists x3) with pat.
-Tactic Notation "wp_send" "(" uconstr(x1) uconstr(x2) uconstr(x3) uconstr(x4) ")"
-    "with" constr(pat) :=
-  wp_send_core (eexists x1; eexists x2; eexists x3; eexists x4) with pat.
-Tactic Notation "wp_send" "(" uconstr(x1) uconstr(x2) uconstr(x3) uconstr(x4)
-    uconstr(x5) ")" "with" constr(pat) :=
-  wp_send_core (eexists x1; eexists x2; eexists x3; eexists x4; eexists x5) with pat.
-Tactic Notation "wp_send" "(" uconstr(x1) uconstr(x2) uconstr(x3) uconstr(x4) ")"
-    uconstr(x5) uconstr(x6) ")" "with" constr(pat) :=
-  wp_send_core (eexists x1; eexists x2; eexists x3; eexists x4; eexists x5;
-                eexists x6) with pat.
-Tactic Notation "wp_send" "(" uconstr(x1) uconstr(x2) uconstr(x3) uconstr(x4) ")"
-    uconstr(x5) uconstr(x6) uconstr(x7) ")" "with" constr(pat) :=
-  wp_send_core (eexists x1; eexists x2; eexists x3; eexists x4; eexists x5;
-                eexists x6; eexists x7) with pat.
-Tactic Notation "wp_send" "(" uconstr(x1) uconstr(x2) uconstr(x3) uconstr(x4) ")"
-    uconstr(x5) uconstr(x6) uconstr(x7) uconstr(x8) ")" "with" constr(pat) :=
-  wp_send_core (eexists x1; eexists x2; eexists x3; eexists x4; eexists x5;
-                eexists x6; eexists x7; eexists x8) with pat.
-
-Lemma iProto_consistent_equiv_proof {Σ} (ps : gmap nat (iProto Σ)) :
+Tactic Notation "wp_send" "(" ne_uconstr_list(xs) ")" "with" constr(pat) :=
+  wp_send_core (ltac1_list_iter ltac:(fun x => eexists x) xs) with pat.
+
+Lemma iProto_consistent_equiv_proof {Σ} (ps : list (iProto Σ)) :
   (∀ i j, valid_target ps i j) ∗
   (∀ i j m1 m2,
      (ps !!! i ≡ (<(Send, j)> m1)%proto) -∗
@@ -396,56 +345,31 @@ Tactic Notation "iProto_consistent_take_step_step" :=
   let m1 := fresh in
   let m2 := fresh in
   iIntros (i j m1 m2) "#Hm1 #Hm2";
-  repeat (destruct i as [|i];
-          [repeat (rewrite lookup_total_insert_ne; [|lia]);
-           try (by rewrite lookup_total_empty iProto_end_message_equivI);
-           try (rewrite lookup_total_insert;
-                try (by rewrite iProto_end_message_equivI);
-                iDestruct (iProto_message_equivI with "Hm1")
-                  as "[%Heq1 Hm1']";simplify_eq)|
-            repeat (rewrite lookup_total_insert_ne; [|lia]);
-            try (by rewrite lookup_total_empty iProto_end_message_equivI)]);
-  repeat (rewrite lookup_total_insert_ne; [|lia]);
-  try rewrite lookup_total_empty;
-  try (by iProto_end_message_equivI);
-  rewrite lookup_total_insert;
+  repeat (destruct i as [|i]=> /=;
+          [try (by rewrite iProto_end_message_equivI);
+           iDestruct (iProto_message_equivI with "Hm1")
+                  as "[%Heq1 Hm1']";simplify_eq=> /=|
+            try (by rewrite iProto_end_message_equivI)]);
+  try (by rewrite iProto_end_message_equivI);
   iDestruct (iProto_message_equivI with "Hm2")
-    as "[%Heq2 Hm2']";simplify_eq;
+    as "[%Heq2 Hm2']";simplify_eq=> /=;
   try (iClear "Hm1' Hm2'";
        iExists _,_,_,_,_,_,_,_,_,_;
        iSplitL "Hm1"; [iFrame "#"|];
        iSplitL "Hm2"; [iFrame "#"|];
        iSplit; [iPureIntro; tc_solve|];
        iSplit; [iPureIntro; tc_solve|];
-       simpl; iClear "Hm1 Hm2"; clear m1 m2);
-  try (repeat (rewrite (insert_commute _ _ i); [|done]);
-  rewrite insert_insert;
-  repeat (rewrite (insert_commute _ _ j); [|done]);
-  rewrite insert_insert).
+       simpl; iClear "Hm1 Hm2"; clear m1 m2).
 
 Tactic Notation "iProto_consistent_take_step_target" :=
   let i := fresh in
   iIntros (i j a m); rewrite /valid_target;
-            iIntros "#Hm";
-  repeat (destruct i as [|i];
-          [repeat (rewrite lookup_total_insert_ne; [|lia]);
-           try (by rewrite lookup_total_empty iProto_end_message_equivI);
-           try (rewrite lookup_total_insert;
-                try (by rewrite iProto_end_message_equivI);
-                iDestruct (iProto_message_equivI with "Hm1")
-                  as "[%Heq1 Hm1']";simplify_eq)|
-            repeat (rewrite lookup_total_insert_ne; [|lia]);
-            try (by rewrite lookup_total_empty iProto_end_message_equivI)]);
-  repeat (rewrite lookup_total_insert_ne; [|lia]);
-  try rewrite lookup_total_empty;
-  try (by iProto_end_message_equivI);
-  rewrite lookup_total_insert;
-  iDestruct (iProto_message_equivI with "Hm")
-    as "[%Heq Hm']";simplify_eq;
-  repeat (try rewrite lookup_empty;
-          try rewrite lookup_insert;
-          rewrite lookup_insert_ne; [|lia]);
-    try rewrite lookup_insert; try done.
+            iIntros "#Hm1";
+  repeat (destruct i as [|i]=> /=;
+          [try (by rewrite iProto_end_message_equivI);
+           by iDestruct (iProto_message_equivI with "Hm1")
+                    as "[%Heq1 _]" ; simplify_eq=> /=|
+           try (by rewrite iProto_end_message_equivI)]).
 
 Tactic Notation "iProto_consistent_take_step" :=
   try iNext;
@@ -453,13 +377,30 @@ Tactic Notation "iProto_consistent_take_step" :=
   iSplitR; [iProto_consistent_take_step_target|
              iProto_consistent_take_step_step].
 
-Tactic Notation "clean_map" constr(i) :=
-  iEval (repeat (rewrite (insert_commute _ _ i); [|done]));
-  rewrite (insert_insert _ i).
-
 Tactic Notation "iProto_consistent_resolve_step" :=
   repeat iIntros (?); repeat iIntros "?";
   repeat iExists _; repeat (iSplit; [done|]); try iFrame.
 
 Tactic Notation "iProto_consistent_take_steps" :=
   repeat (iProto_consistent_take_step; iProto_consistent_resolve_step).
+
+Tactic Notation "wp_get_chan" "(" simple_intropattern(c) ")" constr(pat) :=
+  wp_smart_apply (get_chan_spec with "[$]"); iIntros (c) "[_TMP ?]";
+  iRevert "_TMP"; iIntros pat.
+
+Ltac ltac1_list_iter2 tac l1 l2 :=
+  let go := ltac2:(tac l1 l2 |- List.iter2 (ltac1:(tac x y |- tac x y) tac)
+                                          (of_ltac1_list l1) (of_ltac1_list l2)) in
+  go tac l1 l2.
+
+Tactic Notation "wp_new_chan" constr(prot) "as"
+       "(" simple_intropattern_list(xs) ")" constr_list(pats) :=
+  wp_smart_apply (new_chan_spec prot);
+    [set_solver| |iIntros (?) "?"];
+    [|ltac1_list_iter2 ltac:(fun x y => wp_get_chan (x) y) xs pats].
+
+Tactic Notation "wp_new_chan" constr(prot) "with" constr(lem) "as"
+       "(" simple_intropattern_list(xs) ")" constr_list(pats) :=
+  wp_smart_apply (new_chan_spec prot);
+    [set_solver|by iApply lem|iIntros (?) "?"];
+    ltac1_list_iter2 ltac:(fun x y => wp_get_chan (x) y) xs pats.
diff --git a/multi_actris/channel/proto.v b/multi_actris/channel/proto.v
index dbb1b16e265bc573133ee6d5180461709e85f43b..aa0826a901447c221041396c7a8b895ac871a5e1 100644
--- a/multi_actris/channel/proto.v
+++ b/multi_actris/channel/proto.v
@@ -173,22 +173,6 @@ Notation "< a @.. x1 .. xn > m" := (iProto_message a (∃.. x1, .. (∃.. xn, m)
   (at level 200, a at level 10, x1 closed binder, xn closed binder, m at level 200,
    format "< a  @..  x1  ..  xn >  m") : proto_scope.
 
-Notation "<!> m" := (<Send> m) (at level 200, m at level 200) : proto_scope.
-Notation "<! x1 .. xn > m" := (<!> ∃ x1, .. (∃ xn, m) ..)
-  (at level 200, x1 closed binder, xn closed binder, m at level 200,
-   format "<!  x1  ..  xn >  m") : proto_scope.
-Notation "<!.. x1 .. xn > m" := (<!> ∃.. x1, .. (∃.. xn, m) ..)
-  (at level 200, x1 closed binder, xn closed binder, m at level 200,
-   format "<!..  x1  ..  xn >  m") : proto_scope.
-
-Notation "<?> m" := (<Recv> m) (at level 200, m at level 200) : proto_scope.
-Notation "<? x1 .. xn > m" := (<?> ∃ x1, .. (∃ xn, m) ..)
-  (at level 200, x1 closed binder, xn closed binder, m at level 200,
-   format "<?  x1  ..  xn >  m") : proto_scope.
-Notation "<?.. x1 .. xn > m" := (<?> ∃.. x1, .. (∃.. xn, m) ..)
-  (at level 200, x1 closed binder, xn closed binder, m at level 200,
-   format "<?..  x1  ..  xn >  m") : proto_scope.
-
 Class MsgTele {Σ V} {TT : tele} (m : iMsg Σ V)
     (tv : TT -t> V) (tP : TT -t> iProp Σ) (tp : TT -t> iProto Σ V) :=
   msg_tele : m ≡ (∃.. x, MSG tele_app tv x {{ tele_app tP x }}; tele_app tp x)%msg.
@@ -512,30 +496,30 @@ End proto.
 
 Global Instance iProto_inhabited {Σ V} : Inhabited (iProto Σ V) := populate END.
 
-Definition can_step {Σ V} (rec : gmap nat (iProto Σ V) → iProp Σ)
-           (ps : gmap nat (iProto Σ V)) (i j : nat) : iProp Σ :=
+Definition can_step {Σ V} (rec : list (iProto Σ V) → iProp Σ)
+           (ps : list (iProto Σ V)) (i j : nat) : iProp Σ :=
   ∀ m1 m2,
-    (ps !!! i ≡ <(Send, j)> m1) -∗
-    (ps !!! j ≡ <(Recv, i)> m2) -∗
+    (ps !!! i ≡ (<(Send, j)> m1)) -∗
+    (ps !!! j ≡ (<(Recv, i)> m2)) -∗
     ∀ v p1, iMsg_car m1 v (Next p1) -∗
             ∃ p2, iMsg_car m2 v (Next p2) ∗
                   â–· (rec (<[i:=p1]>(<[j:=p2]>ps))).
 
-Definition valid_target {Σ V} (ps : gmap nat (iProto Σ V)) (i j : nat) : iProp Σ :=
-  ∀ a m, (ps !!! i ≡ <(a, j)> m) -∗ ⌜is_Some (ps !! j)⌝.
+Definition valid_target {Σ V} (ps : list (iProto Σ V)) (i j : nat) : iProp Σ :=
+  ∀ a m, (ps !!! i ≡ (<(a, j)> m)) -∗ ⌜is_Some (ps !! j)⌝.
 
-Definition iProto_consistent_pre {Σ V} (rec : gmap nat (iProto Σ V) → iProp Σ)
-  (ps : gmap nat (iProto Σ V)) : iProp Σ :=
+Definition iProto_consistent_pre {Σ V} (rec : list (iProto Σ V) → iProp Σ)
+  (ps : list (iProto Σ V)) : iProp Σ :=
   (∀ i j, valid_target ps i j) ∗ (∀ i j, can_step rec ps i j).
 
 Global Instance iProto_consistent_pre_ne {Σ V}
-       (rec : gmapO natO (iProto Σ V) -n> iPropO Σ) :
+       (rec : listO (iProto Σ V) -n> iPropO Σ) :
   NonExpansive (iProto_consistent_pre rec).
 Proof. rewrite /iProto_consistent_pre /can_step /valid_target. solve_proper. Qed.
 
 Program Definition iProto_consistent_pre' {Σ V}
-  (rec : gmapO natO (iProto Σ V) -n> iPropO Σ) :
-  gmapO natO (iProto Σ V) -n> iPropO Σ :=
+  (rec : listO (iProto Σ V) -n> iPropO Σ) :
+  listO (iProto Σ V) -n> iPropO Σ :=
   λne ps, iProto_consistent_pre (λ ps, rec ps) ps.
 
 Local Instance iProto_consistent_pre_contractive {Σ V} : Contractive (@iProto_consistent_pre' Σ V).
@@ -544,7 +528,7 @@ Proof.
   solve_contractive.
 Qed.
 
-Definition iProto_consistent {Σ V} (ps : gmap nat (iProto Σ V)) : iProp Σ :=
+Definition iProto_consistent {Σ V} (ps : list (iProto Σ V)) : iProp Σ :=
   fixpoint iProto_consistent_pre' ps.
 
 Arguments iProto_consistent {_ _} _%proto.
@@ -555,7 +539,7 @@ Proof. solve_proper. Qed.
 Global Instance iProto_consistent_proper {Σ V} : Proper ((≡) ==> (⊣⊢)) (@iProto_consistent Σ V).
 Proof. solve_proper. Qed.
 
-Lemma iProto_consistent_unfold {Σ V} (ps : gmap nat (iProto Σ V)) :
+Lemma iProto_consistent_unfold {Σ V} (ps : list (iProto Σ V)) :
   iProto_consistent ps ≡ (iProto_consistent_pre iProto_consistent) ps.
 Proof.
   apply: (fixpoint_unfold iProto_consistent_pre').
@@ -615,12 +599,12 @@ Definition iProto_own_frag `{!protoG Σ V} (γ : gname)
   own γ (gmap_view_frag i (DfracOwn 1) (Excl' (Next p))).
 
 Definition iProto_own_auth `{!protoG Σ V} (γ : gname)
-    (ps : gmap nat (iProto Σ V)) : iProp Σ :=
-  own γ (gmap_view_auth (DfracOwn 1) (((λ p, Excl' (Next p)) <$> ps) : gmap _ _)).
+    (ps : list (iProto Σ V)) : iProp Σ :=
+  own γ (gmap_view_auth (DfracOwn 1) ((λ p, Excl' (Next p)) <$> map_seq 0 ps)).
 
 Definition iProto_ctx `{protoG Σ V}
-    (γ : gname) (ps_dom : gset nat) : iProp Σ :=
-  ∃ ps, ⌜dom ps = ps_dom⌝ ∗ iProto_own_auth γ ps ∗ ▷ iProto_consistent ps.
+    (γ : gname) (ps_len : nat) : iProp Σ :=
+  ∃ ps, ⌜length ps = ps_len⌝ ∗ iProto_own_auth γ ps ∗ ▷ iProto_consistent ps.
 
 (** * The connective for ownership of channel ends *)
 Definition iProto_own `{!protoG Σ V}
@@ -796,44 +780,191 @@ Section proto.
       iDestruct "Hp2" as (Heq) "Hp2". simplify_eq.
       iDestruct "H" as (->) "H". by iExists _.
   Qed.
+  
+  Lemma iProto_le_send i m1 m2 :
+    (∀ v p2', iMsg_car m2 v (Next p2') -∗ ∃ p1',
+      ▷ (p1' ⊑ p2') ∗ iMsg_car m1 v (Next p1')) -∗
+    (<(Send,i)> m1) ⊑ (<(Send,i)> m2).
+  Proof.
+    iIntros "Hle". rewrite iProto_le_unfold.
+    iRight. iExists (Send, i), (Send, i), m1, m2. by eauto.
+  Qed.
+
+  Lemma iProto_le_recv i m1 m2 :
+    (∀ v p1', iMsg_car m1 v (Next p1') -∗ ∃ p2',
+      ▷ (p1' ⊑ p2') ∗ iMsg_car m2 v (Next p2')) -∗
+    (<(Recv,i)> m1) ⊑ (<(Recv,i)> m2).
+  Proof.
+    iIntros "Hle". rewrite iProto_le_unfold.
+    iRight. iExists (Recv, i), (Recv, i), m1, m2. by eauto.
+  Qed.
+
+  Lemma iProto_le_base a v P p1 p2 :
+    ▷ (p1 ⊑ p2) -∗
+    (<a> MSG v {{ P }}; p1) ⊑ (<a> MSG v {{ P }}; p2).
+  Proof.
+    rewrite iMsg_base_eq. iIntros "H". destruct a as [[]].
+    - iApply iProto_le_send. iIntros (v' p') "(->&Hp&$)".
+      iExists p1. iSplit; [|by auto]. iIntros "!>". by iRewrite -"Hp".
+    - iApply iProto_le_recv. iIntros (v' p') "(->&Hp&$)".
+      iExists p2. iSplit; [|by auto]. iIntros "!>". by iRewrite -"Hp".
+  Qed.
+
+  Lemma iProto_le_trans p1 p2 p3 : p1 ⊑ p2 -∗ p2 ⊑ p3 -∗ p1 ⊑ p3.
+  Proof.
+    iIntros "H1 H2". iLöb as "IH" forall (p1 p2 p3).
+    destruct (iProto_case p3) as [->|([]&i&m3&->)].
+    - iDestruct (iProto_le_end_inv_r with "H2") as "H2". by iRewrite "H2" in "H1".
+    - iDestruct (iProto_le_send_inv with "H2") as (m2) "[Hp2 H2]".
+      iRewrite "Hp2" in "H1"; clear p2.
+      iDestruct (iProto_le_send_inv with "H1") as (m1) "[Hp1 H1]".
+      iRewrite "Hp1"; clear p1.
+      iApply iProto_le_send. iIntros (v p3') "Hm3".
+      iDestruct ("H2" with "Hm3") as (p2') "[Hle Hm2]".
+      iDestruct ("H1" with "Hm2") as (p1') "[Hle' Hm1]".
+      iExists p1'. iIntros "{$Hm1} !>". by iApply ("IH" with "Hle'").
+    - iDestruct (iProto_le_recv_inv_r with "H2") as (m2) "[Hp2 H3]".
+      iRewrite "Hp2" in "H1".
+      iDestruct (iProto_le_recv_inv_r with "H1") as (m1) "[Hp1 H2]".
+      iRewrite "Hp1". iApply iProto_le_recv. iIntros (v p1') "Hm1".
+      iDestruct ("H2" with "Hm1") as (p2') "[Hle Hm2]".
+      iDestruct ("H3" with "Hm2") as (p3') "[Hle' Hm3]".
+      iExists p3'. iIntros "{$Hm3} !>". by iApply ("IH" with "Hle").
+  Qed.
+
+  Lemma iProto_le_refl p : ⊢ p ⊑ p.
+  Proof.
+    iLöb as "IH" forall (p). destruct (iProto_case p) as [->|([]&i&m&->)].
+    - iApply iProto_le_end.
+    - iApply iProto_le_send. auto 10 with iFrame.
+    - iApply iProto_le_recv. auto 10 with iFrame.
+  Qed.
+
+
+  Global Instance iProto_own_frag_ne γ s : NonExpansive (iProto_own_frag γ s).
+  Proof. solve_proper. Qed.
+
+  Lemma iProto_own_auth_agree γ ps i p :
+    iProto_own_auth γ ps -∗ iProto_own_frag γ i p -∗ ▷ (ps !! i ≡ Some p).
+  Proof.    
+    iIntros "H● H◯".
+    iDestruct (own_valid_2 with "H● H◯") as "H✓".
+    rewrite gmap_view_both_validI.
+    iDestruct "H✓" as "[_ [H1 H2]]".
+    rewrite lookup_fmap.
+    simpl.
+    rewrite lookup_map_seq_0.    
+    destruct (ps !! i) eqn:Heqn; last first.
+    { rewrite Heqn. rewrite !option_equivI. done. }
+    rewrite Heqn.
+    simpl. rewrite !option_equivI excl_equivI. by iNext.
+  Qed.
+
+  Lemma iProto_own_auth_agree_Some γ ps i p :
+    iProto_own_auth γ ps -∗ iProto_own_frag γ i p -∗ ⌜is_Some (ps !! i)⌝.
+  Proof.
+    iIntros "H● H◯".
+    iDestruct (own_valid_2 with "H● H◯") as "H✓".
+    rewrite gmap_view_both_validI.
+    iDestruct "H✓" as "[_ [H1 H2]]".
+    rewrite lookup_fmap.
+    simpl.
+    rewrite lookup_map_seq_0.    
+    destruct (ps !! i) eqn:Heqn; last first.
+    { rewrite Heqn. rewrite !option_equivI. done. }
+    rewrite Heqn.
+    simpl. rewrite !option_equivI excl_equivI. done. 
+  Qed.
+
+  Lemma iProto_own_auth_update γ ps i p p' :
+    iProto_own_auth γ ps -∗ iProto_own_frag γ i p ==∗
+    iProto_own_auth γ (<[i := p']>ps) ∗ iProto_own_frag γ i p'.
+  Proof.
+    iIntros "H● H◯".
+    iDestruct (iProto_own_auth_agree_Some with "H● H◯") as %HSome.
+    iMod (own_update_2 with "H● H◯") as "[H1 H2]"; [|iModIntro].
+    { eapply (gmap_view_replace _ _ _ (Excl' (Next p'))). done. }
+    iFrame. rewrite -fmap_insert.
+    rewrite /iProto_own_auth. 
+    rewrite insert_map_seq_0; [done|].
+    by apply lookup_lt_is_Some_1.
+  Qed.
+  
+  Lemma iProto_own_auth_alloc ps :
+    ⊢ |==> ∃ γ, iProto_own_auth γ ps ∗ [∗ list] i ↦p ∈ ps, iProto_own γ i p.
+  Proof.
+    iMod (own_alloc (gmap_view_auth (DfracOwn 1) ∅)) as (γ) "Hauth".
+    { apply gmap_view_auth_valid. }
+    iExists γ.
+    iInduction ps as [|p ps] "IH" using rev_ind.
+    { iModIntro. iFrame. done. }
+    iMod ("IH" with "Hauth") as "[Hauth Hfrags]".
+    iFrame "Hfrags".
+    iMod (own_update with "Hauth") as "[Hauth Hfrag]".
+    { apply (gmap_view_alloc _ (length ps) (DfracOwn 1) (Excl' (Next p))); [|done|done].
+      rewrite fmap_map_seq.
+      rewrite lookup_map_seq_0.
+      apply lookup_ge_None_2. rewrite fmap_length. done. }
+    simpl.
+    iModIntro. 
+    rewrite right_id_L. 
+    rewrite -fmap_insert. iFrame.
+    iSplitL "Hauth".
+    - rewrite /iProto_own_auth.
+      rewrite map_seq_snoc. simpl. done.
+    - iSplit; [|done].
+      iExists _. iFrame. by iApply iProto_le_refl.
+  Qed.
+
+  Lemma list_lookup_Some_le (ps : list $ iProto Σ V) (i : nat) (p1 : iProto Σ V) :
+    ⊢@{iProp Σ} ps !! i ≡ Some p1 -∗ ⌜i < length ps⌝.
+  Proof.
+    iIntros "HSome".
+    rewrite option_equivI.
+    destruct (ps !! i) eqn:Heqn; [|done].
+    iPureIntro.
+    by apply lookup_lt_is_Some_1.
+  Qed.
 
   Lemma valid_target_le ps i p1 p2 :
     (∀ i' j', valid_target ps i' j') -∗
-    ps !!! i ≡ p1 -∗
+    ps !! i ≡ Some p1 -∗
     p1 ⊑ p2 -∗
     (∀ i' j', valid_target (<[i := p2]>ps) i' j') ∗ p1 ⊑ p2.
   Proof.
     iIntros "Hprot #HSome Hle".
+    iDestruct (list_lookup_Some_le with "HSome") as %Hi.
     pose proof (iProto_case p1) as [Hend|Hmsg].
     { rewrite Hend. iDestruct (iProto_le_end_inv_l with "Hle") as "#H".
       iFrame "Hle".
       iIntros (i' j' a m) "Hm".
       destruct (decide (i = j')) as [->|Hneqj].
-      { by rewrite lookup_insert. }
-      rewrite lookup_insert_ne; [|done].
+      { rewrite list_lookup_insert; [done|]. done. }
+      rewrite (list_lookup_insert_ne _ i j'); [|done].
       destruct (decide (i = i')) as [->|Hneqi].
-      { rewrite lookup_total_insert. iRewrite "H" in "Hm".
+      { rewrite list_lookup_total_insert; [|done]. iRewrite "H" in "Hm".
         by iDestruct (iProto_end_message_equivI with "Hm") as "Hm". }
-      rewrite lookup_total_insert_ne; [|done].
+      rewrite list_lookup_total_insert_ne; [|done].
       by iApply "Hprot". }
     destruct Hmsg as (t & n & m & Hmsg).
     setoid_rewrite Hmsg.
     iDestruct (iProto_le_msg_inv_l with "Hle") as (m2) "#Heq". iFrame "Hle".
     iIntros (i' j' a m') "Hm".
     destruct (decide (i = j')) as [->|Hneqj].
-    { rewrite lookup_insert. done. }
-    rewrite lookup_insert_ne; [|done].
+    { by rewrite list_lookup_insert. }
+    rewrite (list_lookup_insert_ne _ i j'); [|done].
     destruct (decide (i = i')) as [->|Hneqi].
-    { rewrite lookup_total_insert. iRewrite "Heq" in "Hm".
+    { rewrite list_lookup_total_insert; [|done]. iRewrite "Heq" in "Hm".
       iDestruct (iProto_message_equivI with "Hm") as (Heq) "Hm".
-      simplify_eq. by iApply "Hprot". }
-    rewrite lookup_total_insert_ne; [|done].
+      simplify_eq. iApply ("Hprot" $! i'). 
+      rewrite list_lookup_total_alt. iRewrite "HSome". done. }
+    rewrite list_lookup_total_insert_ne; [|done].
     by iApply "Hprot".
   Qed.
 
   Lemma iProto_consistent_le ps i p1 p2 :
     iProto_consistent ps -∗
-    ps !!! i ≡ p1 -∗
+    ps !! i ≡ Some p1 -∗
     p1 ⊑ p2 -∗
     iProto_consistent (<[i := p2]>ps).
   Proof.
@@ -841,19 +972,21 @@ Section proto.
     iRevert "HSome".
     iLöb as "IH" forall (p1 p2 ps).
     iIntros "#HSome".
+    iDestruct (list_lookup_Some_le with "HSome") as %Hi.
     rewrite !iProto_consistent_unfold.
     iDestruct "Hprot" as "(Htar & Hprot)".
     iDestruct (valid_target_le with "Htar HSome Hle") as "[Htar Hle]".
     iFrame.
     iIntros (i' j' m1 m2) "#Hm1 #Hm2".
     destruct (decide (i = i')) as [<-|Hneq].
-    { rewrite lookup_total_insert.
+    { rewrite list_lookup_total_insert; [|done].
       pose proof (iProto_case p2) as [Hend|Hmsg].
-      { setoid_rewrite Hend. rewrite iProto_end_message_equivI. done. }
+      { setoid_rewrite Hend.
+        rewrite !option_equivI. rewrite iProto_end_message_equivI. done. }
       destruct Hmsg as (a&?&m&Hmsg).
       setoid_rewrite Hmsg.
       destruct a; last first.
-      { rewrite iProto_message_equivI.
+      { rewrite !option_equivI. rewrite iProto_message_equivI.
         iDestruct "Hm1" as "[%Htag Hm1]". done. }
       rewrite iProto_message_equivI.
       iDestruct "Hm1" as "[%Htag Hm1]".
@@ -865,29 +998,35 @@ Section proto.
       iDestruct "Hle" as (m') "[#Heq H]".
       iDestruct ("H" with "Hm1'") as (p') "[Hle H]".
       destruct (decide (i = j')) as [<-|Hneq].
-      { rewrite lookup_total_insert. rewrite iProto_message_equivI.
+      { rewrite list_lookup_total_insert; [|done].
+        rewrite iProto_message_equivI.
         iDestruct "Hm2" as "[%Heq _]". done. }
       iDestruct ("Hprot" $!i j' with "[] [] H") as "Hprot".
-      { iRewrite -"Heq". rewrite !lookup_total_alt. iRewrite "HSome". done. }
-      { rewrite lookup_total_insert_ne; [|done]. done. }
+      { iRewrite -"Heq". iEval (rewrite list_lookup_total_alt). 
+        iRewrite "HSome". done. }
+      { rewrite list_lookup_total_insert_ne; [|done]. done. }
       iDestruct "Hprot" as (p'') "[Hm Hprot]".
       iExists p''. iFrame.
       iNext.
       iDestruct ("IH" with "Hprot Hle [HSome]") as "HI".
-      { by rewrite lookup_total_insert. }
+      { rewrite list_lookup_insert; [done|].
+        by rewrite insert_length. }
       iClear "IH Hm1 Hm2 Heq".
-      rewrite insert_insert.
-      rewrite (insert_commute _ j' i); [|done].
-      rewrite insert_insert. done. }
-    rewrite lookup_total_insert_ne; [|done].
+      rewrite list_insert_insert.
+      rewrite (list_insert_commute _ j' i); [|done].
+      rewrite list_insert_insert. done. }
+    rewrite list_lookup_total_insert_ne; [|done].
     destruct (decide (i = j')) as [<-|Hneq'].
-    { rewrite lookup_total_insert.
+    { rewrite list_lookup_total_insert; [|done].
       pose proof (iProto_case p2) as [Hend|Hmsg].
-      { setoid_rewrite Hend. rewrite iProto_end_message_equivI. done. }
+      { setoid_rewrite Hend.
+        rewrite !option_equivI.
+        rewrite iProto_end_message_equivI. done. }
       destruct Hmsg as (a&?&m&Hmsg).
       setoid_rewrite Hmsg.
       destruct a.
-      { rewrite iProto_message_equivI.
+      { rewrite !option_equivI.
+        rewrite iProto_message_equivI.
         iDestruct "Hm2" as "[%Htag Hm2]". done. }
       rewrite iProto_message_equivI.
       iDestruct "Hm2" as "[%Htag Hm2]".
@@ -897,90 +1036,31 @@ Section proto.
       iDestruct "Hle" as (m') "[#Heq Hle]".
       iDestruct ("Hprot" $!i' with "[] [] Hm1'") as "Hprot".
       { done. }
-      { rewrite !lookup_total_alt. iRewrite "HSome". done. }
+      { iEval (rewrite list_lookup_total_alt). iRewrite "HSome". done. }
       iDestruct ("Hprot") as (p') "[Hm1' Hprot]".
       iDestruct ("Hle" with "Hm1'") as (p2') "[Hle Hm']".
       iSpecialize ("Hm2" $! v (Next p2')).
       iExists p2'.
       iRewrite -"Hm2". iFrame.
       iDestruct ("IH" with "Hprot Hle []") as "HI".
-      { iPureIntro. rewrite lookup_total_insert_ne; [|done].
-        rewrite lookup_total_insert. done. }
-      rewrite insert_commute; [|done].
-      rewrite !insert_insert. done. }
-    rewrite lookup_total_insert_ne; [|done].
+      { iPureIntro. rewrite list_lookup_insert_ne; [|done].
+        by rewrite list_lookup_insert. }
+      rewrite list_insert_commute; [|done].
+      rewrite !list_insert_insert. done. }
+    rewrite list_lookup_total_insert_ne; [|done].
     iIntros (v p) "Hm1'".
     iDestruct ("Hprot" $!i' j' with "[//] [//] Hm1'") as "Hprot".
     iDestruct "Hprot" as (p') "[Hm2' Hprot]".
     iExists p'. iFrame.
     iNext.
-    rewrite (insert_commute _ j' i); [|done].
-    rewrite (insert_commute _ i' i); [|done].
+    rewrite (list_insert_commute _ j' i); [|done].
+    rewrite (list_insert_commute _ i' i); [|done].
     iApply ("IH" with "Hprot Hle []").
-    rewrite lookup_total_insert_ne; [|done].
-    rewrite lookup_total_insert_ne; [|done].
+    rewrite list_lookup_insert_ne; [|done].
+    rewrite list_lookup_insert_ne; [|done].
     done.
   Qed.
 
-  Lemma iProto_le_send i m1 m2 :
-    (∀ v p2', iMsg_car m2 v (Next p2') -∗ ∃ p1',
-      ▷ (p1' ⊑ p2') ∗ iMsg_car m1 v (Next p1')) -∗
-    (<(Send,i)> m1) ⊑ (<(Send,i)> m2).
-  Proof.
-    iIntros "Hle". rewrite iProto_le_unfold.
-    iRight. iExists (Send, i), (Send, i), m1, m2. by eauto.
-  Qed.
-
-  Lemma iProto_le_recv i m1 m2 :
-    (∀ v p1', iMsg_car m1 v (Next p1') -∗ ∃ p2',
-      ▷ (p1' ⊑ p2') ∗ iMsg_car m2 v (Next p2')) -∗
-    (<(Recv,i)> m1) ⊑ (<(Recv,i)> m2).
-  Proof.
-    iIntros "Hle". rewrite iProto_le_unfold.
-    iRight. iExists (Recv, i), (Recv, i), m1, m2. by eauto.
-  Qed.
-
-  Lemma iProto_le_base a v P p1 p2 :
-    ▷ (p1 ⊑ p2) -∗
-    (<a> MSG v {{ P }}; p1) ⊑ (<a> MSG v {{ P }}; p2).
-  Proof.
-    rewrite iMsg_base_eq. iIntros "H". destruct a as [[]].
-    - iApply iProto_le_send. iIntros (v' p') "(->&Hp&$)".
-      iExists p1. iSplit; [|by auto]. iIntros "!>". by iRewrite -"Hp".
-    - iApply iProto_le_recv. iIntros (v' p') "(->&Hp&$)".
-      iExists p2. iSplit; [|by auto]. iIntros "!>". by iRewrite -"Hp".
-  Qed.
-
-  Lemma iProto_le_trans p1 p2 p3 : p1 ⊑ p2 -∗ p2 ⊑ p3 -∗ p1 ⊑ p3.
-  Proof.
-    iIntros "H1 H2". iLöb as "IH" forall (p1 p2 p3).
-    destruct (iProto_case p3) as [->|([]&i&m3&->)].
-    - iDestruct (iProto_le_end_inv_r with "H2") as "H2". by iRewrite "H2" in "H1".
-    - iDestruct (iProto_le_send_inv with "H2") as (m2) "[Hp2 H2]".
-      iRewrite "Hp2" in "H1"; clear p2.
-      iDestruct (iProto_le_send_inv with "H1") as (m1) "[Hp1 H1]".
-      iRewrite "Hp1"; clear p1.
-      iApply iProto_le_send. iIntros (v p3') "Hm3".
-      iDestruct ("H2" with "Hm3") as (p2') "[Hle Hm2]".
-      iDestruct ("H1" with "Hm2") as (p1') "[Hle' Hm1]".
-      iExists p1'. iIntros "{$Hm1} !>". by iApply ("IH" with "Hle'").
-    - iDestruct (iProto_le_recv_inv_r with "H2") as (m2) "[Hp2 H3]".
-      iRewrite "Hp2" in "H1".
-      iDestruct (iProto_le_recv_inv_r with "H1") as (m1) "[Hp1 H2]".
-      iRewrite "Hp1". iApply iProto_le_recv. iIntros (v p1') "Hm1".
-      iDestruct ("H2" with "Hm1") as (p2') "[Hle Hm2]".
-      iDestruct ("H3" with "Hm2") as (p3') "[Hle' Hm3]".
-      iExists p3'. iIntros "{$Hm3} !>". by iApply ("IH" with "Hle").
-  Qed.
-
-  Lemma iProto_le_refl p : ⊢ p ⊑ p.
-  Proof.
-    iLöb as "IH" forall (p). destruct (iProto_case p) as [->|([]&i&m&->)].
-    - iApply iProto_le_end.
-    - iApply iProto_le_send. auto 10 with iFrame.
-    - iApply iProto_le_recv. auto 10 with iFrame.
-  Qed.
-
   Lemma iProto_le_dual p1 p2 : p2 ⊑ p1 -∗ iProto_dual p1 ⊑ iProto_dual p2.
   Proof.
     iIntros "H". iLöb as "IH" forall (p1 p2).
@@ -1130,16 +1210,18 @@ Section proto.
 
   Lemma iProto_consistent_target ps m a i j :
     iProto_consistent ps -∗
-    ps !!! i ≡ (<(a, j)> m) -∗
+    ps !! i ≡ Some (<(a, j)> m) -∗
     ⌜is_Some (ps !! j)⌝.
   Proof.
-    rewrite iProto_consistent_unfold. iDestruct 1 as "[Htar _]". iApply "Htar".
+    rewrite iProto_consistent_unfold. iDestruct 1 as "[Htar _]".
+    iIntros "H". iApply ("Htar" $! i).
+    rewrite list_lookup_total_alt. iRewrite "H". done.
   Qed.
 
   Lemma iProto_consistent_step ps m1 m2 i j v p1 :
     iProto_consistent ps -∗
-    ps !!! i ≡ (<(Send, j)> m1) -∗
-    ps !!! j ≡ (<(Recv, i)> m2) -∗
+    ps !! i ≡ Some (<(Send, j)> m1) -∗
+    ps !! j ≡ Some (<(Recv, i)> m2) -∗
     iMsg_car m1 v (Next p1) -∗
     ∃ p2, iMsg_car m2 v (Next p2) ∗
           â–· iProto_consistent (<[i := p1]>(<[j := p2]>ps)).
@@ -1147,70 +1229,51 @@ Section proto.
     iIntros "Hprot #Hi #Hj Hm1".
     rewrite iProto_consistent_unfold /iProto_consistent_pre.
     iDestruct "Hprot" as "[_ Hprot]".
-    iDestruct ("Hprot" with "Hi Hj Hm1") as (p2) "[Hm2 Hprot]".
+    iDestruct ("Hprot" $! i j with "[Hi] [Hj] Hm1") as (p2) "[Hm2 Hprot]".
+    { rewrite list_lookup_total_alt. iRewrite "Hi". done. }
+    { rewrite list_lookup_total_alt. iRewrite "Hj". done. }
     iExists p2. iFrame.
   Qed.
 
-  Global Instance iProto_own_frag_ne γ s : NonExpansive (iProto_own_frag γ s).
-  Proof. solve_proper. Qed.
-
-  Lemma iProto_own_auth_agree γ ps i p :
-    iProto_own_auth γ ps -∗ iProto_own_frag γ i p -∗ ▷ (ps !!! i ≡ p).
-  Proof.
-    iIntros "H● H◯".
-    iDestruct (own_valid_2 with "H● H◯") as "H✓".
-    rewrite gmap_view_both_validI.
-    iDestruct "H✓" as "[_ [H1 H2]]".
-    rewrite lookup_total_alt lookup_fmap.
-    destruct (ps !! i); last first.
-    { by rewrite !option_equivI. }
-    simpl. rewrite !option_equivI excl_equivI. by iNext.
-  Qed.
-
-  Lemma iProto_own_auth_update γ ps i p p' :
-    iProto_own_auth γ ps -∗ iProto_own_frag γ i p ==∗
-    iProto_own_auth γ (<[i := p']>ps) ∗ iProto_own_frag γ i p'.
+  Lemma iProto_own_le γ s p1 p2 :
+    iProto_own γ s p1 -∗ ▷ (p1 ⊑ p2) -∗ iProto_own γ s p2.
   Proof.
-    iIntros "H● H◯".
-    iMod (own_update_2 with "H● H◯") as "[H1 H2]"; [|iModIntro].
-    { eapply (gmap_view_replace _ _ _ (Excl' (Next p'))). done. }
-    iFrame. rewrite -fmap_insert. done.
+    iDestruct 1 as (p1') "[Hle H]". iIntros "Hle'".
+    iExists p1'. iFrame "H". by iApply (iProto_le_trans with "Hle").
   Qed.
 
-  Lemma iProto_own_auth_alloc ps :
-    ⊢ |==> ∃ γ, iProto_own_auth γ ps ∗ [∗ map] i ↦p ∈ ps, iProto_own γ i p.
+  Lemma iProto_own_excl γ i (p1 p2 : iProto Σ V) :
+    iProto_own γ i p1 -∗ iProto_own γ i p2 -∗ False.
   Proof.
-    iMod (own_alloc (gmap_view_auth (DfracOwn 1) ∅)) as (γ) "Hauth".
-    { apply gmap_view_auth_valid. }
-    iExists γ.
-    iInduction ps as [|i p ps Hin] "IH" using map_ind.
-    { iModIntro. iFrame. by iApply big_sepM_empty. }
-    iMod ("IH" with "Hauth") as "[Hauth Hfrags]".
-    rewrite big_sepM_insert; [|done]. iFrame "Hfrags".
-    iMod (own_update with "Hauth") as "[Hauth Hfrag]".
-    { apply (gmap_view_alloc _ i (DfracOwn 1) (Excl' (Next p))); [|done|done].
-      by rewrite lookup_fmap Hin. }
-    iModIntro. rewrite -fmap_insert. iFrame.
-    iExists _. iFrame. iApply iProto_le_refl.
+    rewrite /iProto_own.
+    iDestruct 1 as (p1') "[_ Hp1]".
+    iDestruct 1 as (p2') "[_ Hp2]".
+    iDestruct (own_prot_excl with "Hp1 Hp2") as %[].
   Qed.
 
-  Lemma iProto_own_le γ s p1 p2 :
-    iProto_own γ s p1 -∗ ▷ (p1 ⊑ p2) -∗ iProto_own γ s p2.
-  Proof.
-    iDestruct 1 as (p1') "[Hle H]". iIntros "Hle'".
-    iExists p1'. iFrame "H". by iApply (iProto_le_trans with "Hle").
+  Lemma iProto_ctx_agree γ n i p :
+    iProto_ctx γ n -∗
+    iProto_own γ i p -∗
+    ⌜i < n⌝.
+  Proof. 
+      iIntros "Hctx Hown".
+      rewrite /iProto_ctx /iProto_own.
+      iDestruct "Hctx" as (ps <-) "[Hauth Hps]".
+      iDestruct "Hown" as (p') "[Hle Hown]".
+      iDestruct (iProto_own_auth_agree_Some with "Hauth Hown") as %HSome.
+      iPureIntro.
+      by apply lookup_lt_is_Some_1.
   Qed.
 
   Lemma iProto_init ps :
     ▷ iProto_consistent ps -∗
-    |==> ∃ γ, iProto_ctx γ (dom ps) ∗ [∗ map] i ↦p ∈ ps, iProto_own γ i p.
+    |==> ∃ γ, iProto_ctx γ (length ps) ∗ [∗ list] i ↦p ∈ ps, iProto_own γ i p.
   Proof.
     iIntros "Hconsistent".
     iMod iProto_own_auth_alloc as (γ) "[Hauth Hfrags]".
     iExists γ. iFrame. iExists _. by iFrame.
   Qed.
 
-
   Lemma iProto_step γ ps_dom i j m1 m2 p1 v :
     iProto_ctx γ ps_dom -∗
     iProto_own γ i (<(Send, j)> m1) -∗
@@ -1220,53 +1283,53 @@ Section proto.
             iProto_own γ i p1 ∗ iProto_own γ j p2.
   Proof.
     iIntros "Hctx Hi Hj Hm".
+    iDestruct (iProto_ctx_agree with "Hctx Hi") as %Hi.
+    iDestruct (iProto_ctx_agree with "Hctx Hj") as %Hij.
     iDestruct "Hi" as (pi) "[Hile Hi]".
     iDestruct "Hj" as (pj) "[Hjle Hj]".
     iDestruct "Hctx" as (ps Hdom) "[Hauth Hconsistent]".
     iDestruct (iProto_own_auth_agree with "Hauth Hi") as "#Hpi".
     iDestruct (iProto_own_auth_agree with "Hauth Hj") as "#Hpj".
     iDestruct (own_prot_idx with "Hi Hj") as %Hneq.
-    iAssert (▷ (<[i:=<(Send, j)> m1]>ps !!! j ≡ pj))%I as "Hpj'".
-    { by rewrite lookup_total_insert_ne. }
+    iAssert (▷ (<[i:=<(Send, j)> m1]>ps !! j ≡ Some pj))%I as "Hpj'".
+    { by rewrite list_lookup_insert_ne. }
     iAssert (▷ (⌜is_Some (ps !! i)⌝ ∗ (pi ⊑ (<(Send, j)> m1))))%I with "[Hile]"
       as "[Hi' Hile]".
     { iNext. iDestruct (iProto_le_msg_inv_r with "Hile") as (m) "#Heq".
-      iFrame. iRewrite "Heq" in "Hpi". rewrite lookup_total_alt.
-      destruct (ps !! i); [done|].
-      iDestruct (iProto_end_message_equivI with "Hpi") as "[]". }
+      iFrame. iRewrite "Heq" in "Hpi". destruct (ps !! i); [done|].
+      by rewrite option_equivI. }
     iAssert (▷ (⌜is_Some (ps !! j)⌝ ∗ (pj ⊑ (<(Recv, i)> m2))))%I with "[Hjle]"
       as "[Hj' Hjle]".
     { iNext. iDestruct (iProto_le_msg_inv_r with "Hjle") as (m) "#Heq".
-      iFrame. iRewrite "Heq" in "Hpj". rewrite !lookup_total_alt.
-      destruct (ps !! j); [done|].
-      iDestruct (iProto_end_message_equivI with "Hpj") as "[]". }
+      iFrame. iRewrite "Heq" in "Hpj".
+      destruct (ps !! j); [done|]. by rewrite !option_equivI. }
     iDestruct (iProto_consistent_le with "Hconsistent Hpi Hile") as "Hconsistent".
     iDestruct (iProto_consistent_le with "Hconsistent Hpj' Hjle") as "Hconsistent".
     iDestruct (iProto_consistent_step _ _ _ i j with "Hconsistent [] [] [Hm //]") as
       (p2) "[Hm2 Hconsistent]".
-    { rewrite lookup_total_insert_ne; [|done].
-      rewrite lookup_total_insert_ne; [|done].
-      by rewrite lookup_total_insert. }
-    { rewrite lookup_total_insert_ne; [|done].
-      by rewrite lookup_total_insert. }
+    { rewrite list_lookup_insert_ne; [|done].
+      rewrite list_lookup_insert_ne; [|done].
+      rewrite list_lookup_insert; [done|]. lia. }
+    { rewrite list_lookup_insert_ne; [|done].
+      rewrite list_lookup_insert; [done|]. rewrite insert_length. lia. }
     iMod (iProto_own_auth_update _ _ _ _ p2 with "Hauth Hj") as "[Hauth Hj]".
     iMod (iProto_own_auth_update _ _ _ _ p1 with "Hauth Hi") as "[Hauth Hi]".
     iIntros "!>!>". iExists p2. iFrame "Hm2".
-    iDestruct "Hi'" as %Hi. iDestruct "Hj'" as %Hj.
+    iDestruct "Hi'" as %Hi'. iDestruct "Hj'" as %Hj'.
     iSplitL "Hconsistent Hauth".
     { iExists (<[i:=p1]> (<[j:=p2]> ps)).
       iSplit.
-      { rewrite !dom_insert_lookup_L; [done..|by rewrite lookup_insert_ne]. }
-      iFrame. rewrite insert_insert.
-      rewrite insert_commute; [|done]. rewrite insert_insert.
-      by rewrite insert_commute; [|done]. }
+      { iPureIntro. rewrite !insert_length. done. } 
+      iFrame. rewrite list_insert_insert.
+      rewrite list_insert_commute; [|done]. rewrite list_insert_insert.
+      by rewrite list_insert_commute; [|done]. }
     iSplitL "Hi"; iExists _; iFrame; iApply iProto_le_refl.
   Qed.
 
   Lemma iProto_target γ ps_dom i a j m :
     iProto_ctx γ ps_dom -∗
     iProto_own γ i (<(a, j)> m) -∗
-    ▷ (⌜j ∈ ps_dom⌝) ∗ iProto_ctx γ ps_dom ∗ iProto_own γ i (<(a, j)> m).
+    ▷ (⌜j < ps_dom⌝) ∗ iProto_ctx γ ps_dom ∗ iProto_own γ i (<(a, j)> m).
   Proof.
     iIntros "Hctx Hown".
     rewrite /iProto_ctx /iProto_own.
@@ -1280,7 +1343,7 @@ Section proto.
       iDestruct (iProto_consistent_target with "Hps Hi") as "#$". by iFrame. }
     iSplitL "HSome".
     { iNext. iDestruct "HSome" as %Heq.
-      iPureIntro. simplify_eq. by apply elem_of_dom. }
+      iPureIntro. simplify_eq. by apply lookup_lt_is_Some_1. }
     iSplitL "Hauth Hps"; iExists _; by iFrame.
   Qed.
 
diff --git a/multi_actris/channel/proto_alt.v b/multi_actris/channel/proto_alt.v
new file mode 100644
index 0000000000000000000000000000000000000000..8e27dbf061d0f1612e55ca9c01fa5fd45deaae76
--- /dev/null
+++ b/multi_actris/channel/proto_alt.v
@@ -0,0 +1,1417 @@
+(** This file defines the core of the Actris logic: It defines dependent
+separation protocols and the various operations on it like dual, append, and
+branching.
+
+Dependent separation protocols [iProto] are defined by instantiating the
+parameterized version in [proto_model] with the type of propositions [iProp] of Iris.
+We define ways of constructing instances of the instantiated type via two
+constructors:
+- [iProto_end], which is identical to [proto_end].
+- [iProto_message], which takes an [action] and an [iMsg]. The type [iMsg] is a
+  sequence of binders [iMsg_exist], terminated by the payload constructed with
+  [iMsg_base] based on arguments [v], [P] and [prot], which are the value, the
+  carried proposition and the [iProto] tail, respectively.
+
+For convenience sake, we provide the following notations:
+- [END], which is simply [iProto_end].
+- [∃ x, m], which is [iMsg_exist] with argument [m].
+- [MSG v {{ P }}; prot], which is [iMsg_Base] with arguments [v], [P] and [prot].
+- [<a> m], which is [iProto_message] with arguments [a] and [m].
+
+We also include custom notation to more easily construct complete constructions:
+- [<a x1 .. xn> m], which is [<a> ∃ x1, .. ∃ xn, m].
+- [<a x1 .. xn> MSG v; {{ P }}; prot], which constructs a full protocol.
+
+Futhermore, we define the following operations:
+- [iProto_dual], which turns all [Send] of a protocol into [Recv] and vice-versa.
+- [iProto_app], which appends two protocols.
+
+In addition we define the subprotocol relation [iProto_le] [⊑], which generalises
+the following inductive definition for asynchronous subtyping on session types:
+
+                 p1 <: p2           p1 <: p2          p1 <: !B.p3    ?A.p3 <: p2
+----------   ----------------   ----------------     ----------------------------
+end <: end    !A.p1 <: !A.p2     ?A.p1 <: ?A.p2             ?A.p1 <: !B.p2
+
+Example:
+
+!R <: !R  ?Q <: ?Q      ?Q <: ?Q
+-------------------  --------------
+?Q.!R <: !R.?Q       ?P.?Q <: ?P.?Q
+------------------------------------
+   ?P.?Q.!R <: !R.?P.?Q
+
+Lastly, relevant type classes instances are defined for each of the above
+notions, such as contractiveness and non-expansiveness, after which the
+specifications of the message-passing primitives are defined in terms of the
+protocol connectives. *)
+From iris.algebra Require Import gmap excl_auth gmap_view.
+From iris.proofmode Require Import proofmode.
+From iris.base_logic Require Export lib.iprop.
+From iris.base_logic Require Import lib.own.
+From iris.program_logic Require Import language.
+From multi_actris.channel Require Import proto_model.
+Set Default Proof Using "Type".
+Export action.
+
+(** * Setup of Iris's cameras *)
+Class protoG Σ V :=
+  protoG_authG ::
+    inG Σ (gmap_viewR natO
+      (optionUR (exclR (laterO (proto (leibnizO V) (iPropO Σ) (iPropO Σ)))))).
+
+Definition protoΣ V := #[
+  GFunctor ((gmap_viewRF natO (optionRF (exclRF (laterOF (protoOF (leibnizO V) idOF idOF))))))
+].
+Global Instance subG_chanΣ {Σ V} : subG (protoΣ V) Σ → protoG Σ V.
+Proof. solve_inG. Qed.
+
+(** * Types *)
+Definition iProto Σ V := proto V (iPropO Σ) (iPropO Σ).
+Declare Scope proto_scope.
+Delimit Scope proto_scope with proto.
+Bind Scope proto_scope with iProto.
+Local Open Scope proto.
+
+(** * Messages *)
+Section iMsg.
+  Set Primitive Projections.
+  Record iMsg Σ V := IMsg { iMsg_car : V → laterO (iProto Σ V) -n> iPropO Σ }.
+End iMsg.
+Arguments IMsg {_ _} _.
+Arguments iMsg_car {_ _} _.
+
+Declare Scope msg_scope.
+Delimit Scope msg_scope with msg.
+Bind Scope msg_scope with iMsg.
+Global Instance iMsg_inhabited {Σ V} : Inhabited (iMsg Σ V) := populate (IMsg inhabitant).
+
+Section imsg_ofe.
+  Context {Σ : gFunctors} {V : Type}.
+
+  Instance iMsg_equiv : Equiv (iMsg Σ V) := λ m1 m2,
+    ∀ w p, iMsg_car m1 w p ≡ iMsg_car m2 w p.
+  Instance iMsg_dist : Dist (iMsg Σ V) := λ n m1 m2,
+    ∀ w p, iMsg_car m1 w p ≡{n}≡ iMsg_car m2 w p.
+
+  Lemma iMsg_ofe_mixin : OfeMixin (iMsg Σ V).
+  Proof. by apply (iso_ofe_mixin (iMsg_car : _ → V -d> _ -n> _)). Qed.
+  Canonical Structure iMsgO := Ofe (iMsg Σ V) iMsg_ofe_mixin.
+
+  Global Instance iMsg_cofe : Cofe iMsgO.
+  Proof. by apply (iso_cofe (IMsg : (V -d> _ -n> _) → _) iMsg_car). Qed.
+End imsg_ofe.
+
+Program Definition iMsg_base_def {Σ V}
+    (v : V) (P : iProp Σ) (p : iProto Σ V) : iMsg Σ V :=
+  IMsg (λ v', λne p', ⌜ v = v' ⌝ ∗ Next p ≡ p' ∗ P)%I.
+Next Obligation. solve_proper. Qed.
+Definition iMsg_base_aux : seal (@iMsg_base_def). by eexists. Qed.
+Definition iMsg_base := iMsg_base_aux.(unseal).
+Definition iMsg_base_eq : @iMsg_base = @iMsg_base_def := iMsg_base_aux.(seal_eq).
+Arguments iMsg_base {_ _} _%V _%I _%proto.
+Global Instance: Params (@iMsg_base) 3 := {}.
+
+Program Definition iMsg_exist_def {Σ V A} (m : A → iMsg Σ V) : iMsg Σ V :=
+  IMsg (λ v', λne p', ∃ x, iMsg_car (m x) v' p')%I.
+Next Obligation. solve_proper. Qed.
+Definition iMsg_exist_aux : seal (@iMsg_exist_def). by eexists. Qed.
+Definition iMsg_exist := iMsg_exist_aux.(unseal).
+Definition iMsg_exist_eq : @iMsg_exist = @iMsg_exist_def := iMsg_exist_aux.(seal_eq).
+Arguments iMsg_exist {_ _ _} _%msg.
+Global Instance: Params (@iMsg_exist) 3 := {}.
+
+Definition iMsg_texist {Σ V} {TT : tele} (m : TT → iMsg Σ V) : iMsg Σ V :=
+  tele_fold (@iMsg_exist Σ V) (λ x, x) (tele_bind m).
+Arguments iMsg_texist {_ _ !_} _%msg /.
+
+Notation "'MSG' v {{ P } } ; p" := (iMsg_base v P p)
+  (at level 200, v at level 20, right associativity,
+   format "MSG  v  {{  P  } } ;  p") : msg_scope.
+Notation "'MSG' v ; p" := (iMsg_base v True p)
+  (at level 200, v at level 20, right associativity,
+   format "MSG  v ;  p") : msg_scope.
+Notation "∃ x .. y , m" :=
+  (iMsg_exist (λ x, .. (iMsg_exist (λ y, m)) ..)%msg) : msg_scope.
+Notation "'∃..' x .. y , m" :=
+  (iMsg_texist (λ x, .. (iMsg_texist (λ y, m)) .. )%msg)
+  (at level 200, x binder, y binder, right associativity,
+   format "∃..  x  ..  y ,  m") : msg_scope.
+
+Lemma iMsg_texist_exist {Σ V} {TT : tele} w lp (m : TT → iMsg Σ V) :
+  iMsg_car (∃.. x, m x)%msg w lp ⊣⊢ (∃.. x, iMsg_car (m x) w lp).
+Proof.
+  rewrite /iMsg_texist iMsg_exist_eq.
+  induction TT as [|T TT IH]; simpl; [done|]. f_equiv=> x. apply IH.
+Qed.
+
+(** * Operators *)
+Definition iProto_end_def {Σ V} : iProto Σ V := proto_end.
+Definition iProto_end_aux : seal (@iProto_end_def). by eexists. Qed.
+Definition iProto_end := iProto_end_aux.(unseal).
+Definition iProto_end_eq : @iProto_end = @iProto_end_def := iProto_end_aux.(seal_eq).
+Arguments iProto_end {_ _}.
+
+Definition iProto_message_def {Σ V} (a : action) (m : iMsg Σ V) : iProto Σ V :=
+  proto_message a (iMsg_car m).
+Definition iProto_message_aux : seal (@iProto_message_def). by eexists. Qed.
+Definition iProto_message := iProto_message_aux.(unseal).
+Definition iProto_message_eq :
+  @iProto_message = @iProto_message_def := iProto_message_aux.(seal_eq).
+Arguments iProto_message {_ _} _ _%msg.
+Global Instance: Params (@iProto_message) 3 := {}.
+
+Notation "'END'" := iProto_end : proto_scope.
+
+Notation "< a > m" := (iProto_message a m)
+  (at level 200, a at level 10, m at level 200,
+   format "< a >  m") : proto_scope.
+Notation "< a @ x1 .. xn > m" := (iProto_message a (∃ x1, .. (∃ xn, m) ..))
+  (at level 200, a at level 10, x1 closed binder, xn closed binder, m at level 200,
+   format "< a  @  x1  ..  xn >  m") : proto_scope.
+Notation "< a @.. x1 .. xn > m" := (iProto_message a (∃.. x1, .. (∃.. xn, m) ..))
+  (at level 200, a at level 10, x1 closed binder, xn closed binder, m at level 200,
+   format "< a  @..  x1  ..  xn >  m") : proto_scope.
+
+Class MsgTele {Σ V} {TT : tele} (m : iMsg Σ V)
+    (tv : TT -t> V) (tP : TT -t> iProp Σ) (tp : TT -t> iProto Σ V) :=
+  msg_tele : m ≡ (∃.. x, MSG tele_app tv x {{ tele_app tP x }}; tele_app tp x)%msg.
+Global Hint Mode MsgTele ! ! - ! - - - : typeclass_instances.
+
+(** * Operations *)
+Program Definition iMsg_map {Σ V}
+    (rec : iProto Σ V → iProto Σ V) (m : iMsg Σ V) : iMsg Σ V :=
+  IMsg (λ v, λne p1', ∃ p1, iMsg_car m v (Next p1) ∗ p1' ≡ Next (rec p1))%I.
+Next Obligation. solve_proper. Qed.
+
+Program Definition iProto_map_app_aux {Σ V}
+    (f : action → action) (p2 : iProto Σ V)
+    (rec : iProto Σ V -n> iProto Σ V) : iProto Σ V -n> iProto Σ V := λne p,
+  proto_elim p2 (λ a m,
+    proto_message (f a) (iMsg_car (iMsg_map rec (IMsg m)))) p.
+Next Obligation.
+  intros Σ V f p2 rec n p1 p1' Hp. apply proto_elim_ne=> // a m1 m2 Hm.
+  apply proto_message_ne=> v p' /=. by repeat f_equiv.
+Qed.
+
+Global Instance iProto_map_app_aux_contractive {Σ V} f (p2 : iProto Σ V) :
+  Contractive (iProto_map_app_aux f p2).
+Proof.
+  intros n rec1 rec2 Hrec p1; simpl. apply proto_elim_ne=> // a m1 m2 Hm.
+  apply proto_message_ne=> v p' /=. by repeat (f_contractive || f_equiv).
+Qed.
+
+Definition iProto_map_app {Σ V} (f : action → action)
+    (p2 : iProto Σ V) : iProto Σ V -n> iProto Σ V :=
+  fixpoint (iProto_map_app_aux f p2).
+
+Definition iProto_app_def {Σ V} (p1 p2 : iProto Σ V) : iProto Σ V :=
+  iProto_map_app id p2 p1.
+Definition iProto_app_aux : seal (@iProto_app_def). Proof. by eexists. Qed.
+Definition iProto_app := iProto_app_aux.(unseal).
+Definition iProto_app_eq : @iProto_app = @iProto_app_def := iProto_app_aux.(seal_eq).
+Arguments iProto_app {_ _} _%proto _%proto.
+Global Instance: Params (@iProto_app) 2 := {}.
+Infix "<++>" := iProto_app (at level 60) : proto_scope.
+Notation "m <++> p" := (iMsg_map (flip iProto_app p) m) : msg_scope.
+
+Definition iProto_dual_def {Σ V} (p : iProto Σ V) : iProto Σ V :=
+  iProto_map_app action_dual proto_end p.
+Definition iProto_dual_aux : seal (@iProto_dual_def). Proof. by eexists. Qed.
+Definition iProto_dual := iProto_dual_aux.(unseal).
+Definition iProto_dual_eq :
+  @iProto_dual = @iProto_dual_def := iProto_dual_aux.(seal_eq).
+Arguments iProto_dual {_ _} _%proto.
+Global Instance: Params (@iProto_dual) 2 := {}.
+Notation iMsg_dual := (iMsg_map iProto_dual).
+
+Definition iProto_dual_if {Σ V} (d : bool) (p : iProto Σ V) : iProto Σ V :=
+  if d then iProto_dual p else p.
+Arguments iProto_dual_if {_ _} _ _%proto.
+Global Instance: Params (@iProto_dual_if) 3 := {}.
+
+(** * Proofs *)
+Section proto.
+  Context `{!protoG Σ V}.
+  Implicit Types v : V.
+  Implicit Types p pl pr : iProto Σ V.
+  Implicit Types m : iMsg Σ V.
+
+  (** ** Equality *)
+  Lemma iProto_case p : p ≡ END ∨ ∃ t n m, p ≡ <(t,n)> m.
+  Proof.
+    rewrite iProto_message_eq iProto_end_eq.
+    destruct (proto_case p) as [|([a n]&m&?)]; [by left|right].
+    by exists a, n, (IMsg m).
+  Qed.
+  Lemma iProto_message_equivI `{!BiInternalEq SPROP} a1 a2 m1 m2 :
+    (<a1> m1) ≡ (<a2> m2) ⊣⊢@{SPROP} ⌜ a1 = a2 ⌝ ∧
+      (∀ v lp, iMsg_car m1 v lp ≡ iMsg_car m2 v lp).
+  Proof. rewrite iProto_message_eq. apply proto_message_equivI. Qed.
+
+  Lemma iProto_message_end_equivI `{!BiInternalEq SPROP} a m :
+    (<a> m) ≡ END ⊢@{SPROP} False.
+  Proof. rewrite iProto_message_eq iProto_end_eq. apply proto_message_end_equivI. Qed.
+  Lemma iProto_end_message_equivI `{!BiInternalEq SPROP} a m :
+    END ≡ (<a> m) ⊢@{SPROP} False.
+  Proof. by rewrite internal_eq_sym iProto_message_end_equivI. Qed.
+
+  (** ** Non-expansiveness of operators *)
+  Global Instance iMsg_car_proper :
+    Proper (iMsg_equiv ==> (=) ==> (≡) ==> (≡)) (iMsg_car (Σ:=Σ) (V:=V)).
+  Proof.
+    intros m1 m2 meq v1 v2 veq p1 p2 peq. rewrite meq.
+    f_equiv; [ by f_equiv | done ].
+  Qed.
+  Global Instance iMsg_car_ne n :
+    Proper (iMsg_dist n ==> (=) ==> (dist n) ==> (dist n)) (iMsg_car (Σ:=Σ) (V:=V)).
+  Proof.
+    intros m1 m2 meq v1 v2 veq p1 p2 peq. rewrite meq.
+    f_equiv; [ by f_equiv | done ].
+  Qed.
+
+  Global Instance iMsg_contractive v n :
+    Proper (dist n ==> dist_later n ==> dist n) (iMsg_base (Σ:=Σ) (V:=V) v).
+  Proof. rewrite iMsg_base_eq=> P1 P2 HP p1 p2 Hp w q /=. solve_contractive. Qed.
+  Global Instance iMsg_ne v : NonExpansive2 (iMsg_base (Σ:=Σ) (V:=V) v).
+  Proof. rewrite iMsg_base_eq=> P1 P2 HP p1 p2 Hp w q /=. solve_proper. Qed.
+  Global Instance iMsg_proper v :
+    Proper ((≡) ==> (≡) ==> (≡)) (iMsg_base (Σ:=Σ) (V:=V) v).
+  Proof. apply (ne_proper_2 _). Qed.
+
+  Global Instance iMsg_exist_ne A n :
+    Proper (pointwise_relation _ (dist n) ==> (dist n)) (@iMsg_exist Σ V A).
+  Proof. rewrite iMsg_exist_eq=> m1 m2 Hm v p /=. f_equiv=> x. apply Hm. Qed.
+  Global Instance iMsg_exist_proper A :
+    Proper (pointwise_relation _ (≡) ==> (≡)) (@iMsg_exist Σ V A).
+  Proof. rewrite iMsg_exist_eq=> m1 m2 Hm v p /=. f_equiv=> x. apply Hm. Qed.
+
+  Global Instance msg_tele_base (v:V) (P : iProp Σ) (p : iProto Σ V) :
+    MsgTele (TT:=TeleO) (MSG v {{ P }}; p) v P p.
+  Proof. done. Qed.
+  Global Instance msg_tele_exist {A} {TT : A → tele} (m : A → iMsg Σ V) tv tP tp :
+  (∀ x, MsgTele (TT:=TT x) (m x) (tv x) (tP x) (tp x)) →
+  MsgTele (TT:=TeleS TT) (∃ x, m x) tv tP tp.
+  Proof. intros Hm. rewrite /MsgTele /=. f_equiv=> x. apply Hm. Qed.
+
+  Global Instance iProto_message_ne a :
+    NonExpansive (iProto_message (Σ:=Σ) (V:=V) a).
+  Proof. rewrite iProto_message_eq. solve_proper. Qed.
+  Global Instance iProto_message_proper a :
+    Proper ((≡) ==> (≡)) (iProto_message (Σ:=Σ) (V:=V) a).
+  Proof. apply (ne_proper _). Qed.
+
+  Lemma iProto_message_equiv {TT1 TT2 : tele} a1 a2
+        (m1 m2 : iMsg Σ V)
+        (v1 : TT1 -t> V) (v2 : TT2 -t> V)
+        (P1 : TT1 -t> iProp Σ) (P2 : TT2 -t> iProp Σ)
+        (prot1 : TT1 -t> iProto Σ V) (prot2 : TT2 -t> iProto Σ V) :
+    MsgTele m1 v1 P1 prot1 →
+    MsgTele m2 v2 P2 prot2 →
+    ⌜ a1 = a2 ⌝ -∗
+    (■ ∀.. (xs1 : TT1), tele_app P1 xs1 -∗
+       ∃.. (xs2 : TT2), ⌜tele_app v1 xs1 = tele_app v2 xs2⌝ ∗
+                        ▷ (tele_app prot1 xs1 ≡ tele_app prot2 xs2) ∗
+                        tele_app P2 xs2) -∗
+    (■ ∀.. (xs2 : TT2), tele_app P2 xs2 -∗
+       ∃.. (xs1 : TT1), ⌜tele_app v1 xs1 = tele_app v2 xs2⌝ ∗
+                        ▷ (tele_app prot1 xs1 ≡ tele_app prot2 xs2) ∗
+                        tele_app P1 xs1) -∗
+      (<a1> m1) ≡ (<a2> m2).
+  Proof.
+    iIntros (Hm1 Hm2 Heq) "#Heq1 #Heq2".
+    unfold MsgTele in Hm1. rewrite Hm1. clear Hm1.
+    unfold MsgTele in Hm2. rewrite Hm2. clear Hm2.
+    rewrite iProto_message_eq proto_message_equivI.
+    iSplit; [ done | ].
+    iIntros (v p').
+    do 2 rewrite iMsg_texist_exist.
+    rewrite iMsg_base_eq /=.
+    iApply prop_ext.
+    iIntros "!>". iSplit.
+    - iDestruct 1 as (xs1 Hveq1) "[Hrec1 HP1]".
+      iDestruct ("Heq1" with "HP1") as (xs2 Hveq2) "[Hrec2 HP2]".
+      iExists xs2. rewrite -Hveq1 Hveq2.
+      iSplitR; [ done | ]. iSplitR "HP2"; [ | done ].
+      iRewrite -"Hrec1". iApply later_equivI. iIntros "!>". by iRewrite "Hrec2".
+    - iDestruct 1 as (xs2 Hveq2) "[Hrec2 HP2]".
+      iDestruct ("Heq2" with "HP2") as (xs1 Hveq1) "[Hrec1 HP1]".
+      iExists xs1. rewrite -Hveq2 Hveq1.
+      iSplitR; [ done | ]. iSplitR "HP1"; [ | done ].
+      iRewrite -"Hrec2". iApply later_equivI. iIntros "!>". by iRewrite "Hrec1".
+  Qed.
+
+  (** Helpers *)
+  Lemma iMsg_map_base f v P p :
+    NonExpansive f →
+    iMsg_map f (MSG v {{ P }}; p) ≡ (MSG v {{ P }}; f p)%msg.
+  Proof.
+    rewrite iMsg_base_eq. intros ? v' p'; simpl. iSplit.
+    - iDestruct 1 as (p'') "[(->&Hp&$) Hp']". iSplit; [done|].
+      iRewrite "Hp'". iIntros "!>". by iRewrite "Hp".
+    - iIntros "(->&Hp'&$)". iExists p. iRewrite -"Hp'". auto.
+  Qed.
+  Lemma iMsg_map_exist {A} f (m : A → iMsg Σ V) :
+    iMsg_map f (∃ x, m x) ≡ (∃ x, iMsg_map f (m x))%msg.
+  Proof.
+    rewrite iMsg_exist_eq. intros v' p'; simpl. iSplit.
+    - iDestruct 1 as (p'') "[H Hp']". iDestruct "H" as (x) "H"; auto.
+    - iDestruct 1 as (x p'') "[Hm Hp']". auto.
+  Qed.
+
+  (** ** Dual *)
+  Global Instance iProto_dual_ne : NonExpansive (@iProto_dual Σ V).
+  Proof. rewrite iProto_dual_eq. solve_proper. Qed.
+  Global Instance iProto_dual_proper : Proper ((≡) ==> (≡)) (@iProto_dual Σ V).
+  Proof. apply (ne_proper _). Qed.
+  Global Instance iProto_dual_if_ne d : NonExpansive (@iProto_dual_if Σ V d).
+  Proof. solve_proper. Qed.
+  Global Instance iProto_dual_if_proper d :
+    Proper ((≡) ==> (≡)) (@iProto_dual_if Σ V d).
+  Proof. apply (ne_proper _). Qed.
+
+  Lemma iProto_dual_end : iProto_dual (Σ:=Σ) (V:=V) END ≡ END.
+  Proof.
+    rewrite iProto_end_eq iProto_dual_eq /iProto_dual_def /iProto_map_app.
+    etrans; [apply (fixpoint_unfold (iProto_map_app_aux _ _))|]; simpl.
+    by rewrite proto_elim_end.
+  Qed.
+  Lemma iProto_dual_message a m :
+    iProto_dual (<a> m) ≡ <action_dual a> iMsg_dual m.
+  Proof.
+    rewrite iProto_message_eq iProto_dual_eq /iProto_dual_def /iProto_map_app.
+    etrans; [apply (fixpoint_unfold (iProto_map_app_aux _ _))|]; simpl.
+    rewrite /iProto_message_def. rewrite ->proto_elim_message; [done|].
+    intros a' m1 m2 Hm; f_equiv; solve_proper.
+  Qed.
+  Lemma iMsg_dual_base v P p :
+    iMsg_dual (MSG v {{ P }}; p) ≡ (MSG v {{ P }}; iProto_dual p)%msg.
+  Proof. apply iMsg_map_base, _. Qed.
+  Lemma iMsg_dual_exist {A} (m : A → iMsg Σ V) :
+    iMsg_dual (∃ x, m x) ≡ (∃ x, iMsg_dual (m x))%msg.
+  Proof. apply iMsg_map_exist. Qed.
+
+  Global Instance iProto_dual_involutive : Involutive (≡) (@iProto_dual Σ V).
+  Proof.
+    intros p. apply (uPred.internal_eq_soundness (M:=iResUR Σ)).
+    iLöb as "IH" forall (p). destruct (iProto_case p) as [->|(a&n&m&->)].
+    { by rewrite !iProto_dual_end. }
+    rewrite !iProto_dual_message involutive.
+    iApply iProto_message_equivI; iSplit; [done|]; iIntros (v p') "/=".
+    iApply prop_ext; iIntros "!>"; iSplit.
+    - iDestruct 1 as (pd) "[H Hp']". iRewrite "Hp'".
+      iDestruct "H" as (pdd) "[H #Hpd]".
+      iApply (internal_eq_rewrite); [|done]; iIntros "!>".
+      iRewrite "Hpd". by iRewrite ("IH" $! pdd).
+    - iIntros "H". destruct (Next_uninj p') as [p'' Hp']. iExists _.
+      rewrite Hp'. iSplitL; [by auto|]. iIntros "!>". by iRewrite ("IH" $! p'').
+  Qed.
+
+  (** ** Append *)
+  Global Instance iProto_app_end_l : LeftId (≡) END (@iProto_app Σ V).
+  Proof.
+    intros p. rewrite iProto_end_eq iProto_app_eq /iProto_app_def /iProto_map_app.
+    etrans; [apply (fixpoint_unfold (iProto_map_app_aux _ _))|]; simpl.
+    by rewrite proto_elim_end.
+  Qed.
+  Lemma iProto_app_message a m p2 : (<a> m) <++> p2 ≡ <a> m <++> p2.
+  Proof.
+    rewrite iProto_message_eq iProto_app_eq /iProto_app_def /iProto_map_app.
+    etrans; [apply (fixpoint_unfold (iProto_map_app_aux _ _))|]; simpl.
+    rewrite /iProto_message_def. rewrite ->proto_elim_message; [done|].
+    intros a' m1 m2 Hm. f_equiv; solve_proper.
+  Qed.
+
+  Global Instance iProto_app_ne : NonExpansive2 (@iProto_app Σ V).
+  Proof.
+    assert (∀ n, Proper (dist n ==> (=) ==> dist n) (@iProto_app Σ V)).
+    { intros n p1 p1' Hp1 p2 p2' <-. by rewrite iProto_app_eq /iProto_app_def Hp1. }
+    assert (Proper ((≡) ==> (=) ==> (≡)) (@iProto_app Σ V)).
+    { intros p1 p1' Hp1 p2 p2' <-. by rewrite iProto_app_eq /iProto_app_def Hp1. }
+    intros n p1 p1' Hp1 p2 p2' Hp2. rewrite Hp1. clear p1 Hp1.
+    revert p1'. induction (lt_wf n) as [n _ IH]; intros p1.
+    destruct (iProto_case p1) as [->|(a&i&m&->)].
+    { by rewrite !left_id. }
+    rewrite !iProto_app_message. f_equiv=> v p' /=. do 4 f_equiv.
+    f_contractive. apply IH; eauto using dist_le with lia.
+  Qed.
+  Global Instance iProto_app_proper : Proper ((≡) ==> (≡) ==> (≡)) (@iProto_app Σ V).
+  Proof. apply (ne_proper_2 _). Qed.
+
+  Lemma iMsg_app_base v P p1 p2 :
+    ((MSG v {{ P }}; p1) <++> p2)%msg ≡ (MSG v {{ P }}; p1 <++> p2)%msg.
+  Proof. apply: iMsg_map_base. Qed.
+  Lemma iMsg_app_exist {A} (m : A → iMsg Σ V) p2 :
+    ((∃ x, m x) <++> p2)%msg ≡ (∃ x, m x <++> p2)%msg.
+  Proof. apply iMsg_map_exist. Qed.
+
+  Global Instance iProto_app_end_r : RightId (≡) END (@iProto_app Σ V).
+  Proof.
+    intros p. apply (uPred.internal_eq_soundness (M:=iResUR Σ)).
+    iLöb as "IH" forall (p). destruct (iProto_case p) as [->|(a&i&m&->)].
+    { by rewrite left_id. }
+    rewrite iProto_app_message.
+    iApply iProto_message_equivI; iSplit; [done|]; iIntros (v p') "/=".
+    iApply prop_ext; iIntros "!>". iSplit.
+    - iDestruct 1 as (p1') "[H Hp']". iRewrite "Hp'".
+      iApply (internal_eq_rewrite); [|done]; iIntros "!>".
+      by iRewrite ("IH" $! p1').
+    - iIntros "H". destruct (Next_uninj p') as [p'' Hp']. iExists p''.
+      rewrite Hp'. iSplitL; [by auto|]. iIntros "!>". by iRewrite ("IH" $! p'').
+  Qed.
+  Global Instance iProto_app_assoc : Assoc (≡) (@iProto_app Σ V).
+  Proof.
+    intros p1 p2 p3. apply (uPred.internal_eq_soundness (M:=iResUR Σ)).
+    iLöb as "IH" forall (p1). destruct (iProto_case p1) as [->|(a&i&m&->)].
+    { by rewrite !left_id. }
+    rewrite !iProto_app_message.
+    iApply iProto_message_equivI; iSplit; [done|]; iIntros (v p123) "/=".
+    iApply prop_ext; iIntros "!>". iSplit.
+    - iDestruct 1 as (p1') "[H #Hp']".
+      iExists (p1' <++> p2). iSplitL; [by auto|].
+      iRewrite "Hp'". iIntros "!>". iApply "IH".
+    - iDestruct 1 as (p12) "[H #Hp123]". iDestruct "H" as (p1') "[H #Hp12]".
+      iExists p1'. iFrame "H". iRewrite "Hp123".
+      iIntros "!>". iRewrite "Hp12". by iRewrite ("IH" $! p1').
+  Qed.
+
+  Lemma iProto_dual_app p1 p2 :
+    iProto_dual (p1 <++> p2) ≡ iProto_dual p1 <++> iProto_dual p2.
+  Proof.
+    apply (uPred.internal_eq_soundness (M:=iResUR Σ)).
+    iLöb as "IH" forall (p1 p2). destruct (iProto_case p1) as [->|(a&i&m&->)].
+    { by rewrite iProto_dual_end !left_id. }
+    rewrite iProto_dual_message !iProto_app_message iProto_dual_message /=.
+    iApply iProto_message_equivI; iSplit; [done|]; iIntros (v p12) "/=".
+    iApply prop_ext; iIntros "!>". iSplit.
+    - iDestruct 1 as (p12d) "[H #Hp12]". iDestruct "H" as (p1') "[H #Hp12d]".
+      iExists (iProto_dual p1'). iSplitL; [by auto|].
+      iRewrite "Hp12". iIntros "!>". iRewrite "Hp12d". iApply "IH".
+    - iDestruct 1 as (p1') "[H #Hp12]". iDestruct "H" as (p1d) "[H #Hp1']".
+      iExists (p1d <++> p2). iSplitL; [by auto|].
+      iRewrite "Hp12". iIntros "!>". iRewrite "Hp1'". by iRewrite ("IH" $! p1d p2).
+  Qed.
+
+End proto.
+
+Global Instance iProto_inhabited {Σ V} : Inhabited (iProto Σ V) := populate END.
+
+Program Definition iProto_elim {Σ V A}
+    (x : A) (f : action → iMsg Σ V -> A) (p : iProto Σ V) : A :=
+  proto_elim x (λ a m, f a (IMsg (λ v, λne p, m v p)))%I p.
+Next Obligation. solve_proper. Qed.
+
+Lemma iProto_elim_message {Σ V} {A:ofe}
+     (x : A) (f : action → iMsg Σ V -> A) a m
+    `{Hf : ∀ a, Proper ((≡) ==> (≡)) (f a)} :
+  iProto_elim x f (iProto_message a m) ≡ f a m.
+ Proof.
+  rewrite /iProto_elim iProto_message_eq /iProto_message_def /=.
+   setoid_rewrite proto_elim_message.
+  { apply Hf. done. }
+  intros a' f1 f2 Hf'. apply Hf=> v p /=. apply Hf'.
+Qed.
+
+Lemma iProto_elim_message' {Σ V} {A:ofe}
+    (x : A) (f : action → iMsg Σ V -> A) a m 
+   `{Hf : ∀ a, Proper ((≡) ==> (≡)) (f a)} :
+   iProto_elim x f (iProto_message a m) ≡ f a m.
+Proof.
+  rewrite /iProto_elim.
+  rewrite iProto_message_eq /iProto_message_def. simpl.
+  setoid_rewrite proto_elim_message.
+  { f_equiv. done. }
+  intros a'.
+  intros f1 f2 Hf'. f_equiv. done.
+Qed.
+ 
+Definition nat_beq := Eval compute in Nat.eqb.
+
+Definition find_recv {Σ V} (i:nat) (j:nat) (ps : list (iProto Σ V)) :
+  option $ iMsg Σ V :=
+  iProto_elim None (λ a m,
+                      match a with
+                      | (Recv, i') => if nat_beq i i' then Some m else None
+                      | (Send, _) => None
+                      end) (ps !!! j).
+
+Fixpoint sync_pairs_aux {Σ V} (i : nat) (ps_full ps : list (iProto Σ V)) :
+  list (nat * nat * iMsg Σ V * iMsg Σ V) :=
+  match ps with
+  | [] => []
+  | p :: ps =>
+      iProto_elim (sync_pairs_aux (S i) ps_full ps)
+        (λ a mi, match a with
+                 | (Recv,_) => sync_pairs_aux (S i) ps_full ps
+                 | (Send,j) => match find_recv i j ps_full with
+                               | None => sync_pairs_aux (S i) ps_full ps
+                               | Some mj => (i,j,mi,mj) ::
+                                           sync_pairs_aux (S i) ps_full ps
+                              end
+                 end) p
+  end.
+
+Notation sync_pairs ps := (sync_pairs_aux 0 ps ps).
+
+Definition can_step {Σ V} (rec : list (iProto Σ V) → iProp Σ)
+           (ps : list (iProto Σ V)) : iProp Σ :=
+  [∧ list] '(i,j,m1,m2) ∈ sync_pairs ps,
+    ∀ v p1, iMsg_car m1 v (Next p1) -∗
+            ∃ p2, iMsg_car m2 v (Next p2) ∗
+                  â–· (rec (<[i:=p1]>(<[j:=p2]>ps))).
+
+From iris.heap_lang Require Import notation.
+
+Definition iProto_binary `{!heapGS Σ} : list (iProto Σ val) :=
+  [(<(Send, 1) @ (x:Z)> MSG #x ; END)%proto;
+   (<(Recv, 0) @ (x:Z)> MSG #x ; END)%proto].
+
+Lemma iProto_binary_consistent `{!heapGS Σ} :
+  ⊢ can_step (λ _, True) (@iProto_binary _ Σ heapGS).
+Proof. rewrite /iProto_binary /can_step /iProto_elim. simpl.
+       rewrite /find_recv. simpl.
+       Fail rewrite iProto_elim_message.
+       (* OBS: Break here *)
+
+
+Definition valid_target {Σ V} (ps : list (iProto Σ V)) (i j : nat) : iProp Σ :=
+  ∀ a m, (ps !!! i ≡ <(a, j)> m) -∗ ⌜is_Some (ps !! j)⌝.
+
+Definition iProto_consistent_pre {Σ V} (rec : list (iProto Σ V) → iProp Σ)
+  (ps : list (iProto Σ V)) : iProp Σ :=
+  (∀ i j, valid_target ps i j) ∗ (can_step rec ps).
+
+Global Instance iProto_consistent_pre_ne {Σ V}
+       (rec : listO (iProto Σ V) -n> iPropO Σ) :
+  NonExpansive (iProto_consistent_pre rec).
+Proof. rewrite /iProto_consistent_pre /can_step /valid_target. solve_proper. Qed.
+
+Program Definition iProto_consistent_pre' {Σ V}
+  (rec : listO (iProto Σ V) -n> iPropO Σ) :
+  listO (iProto Σ V) -n> iPropO Σ :=
+  λne ps, iProto_consistent_pre (λ ps, rec ps) ps.
+
+Local Instance iProto_consistent_pre_contractive {Σ V} : Contractive (@iProto_consistent_pre' Σ V).
+Proof.
+  rewrite /iProto_consistent_pre' /iProto_consistent_pre /can_step.
+  solve_contractive.
+Qed.
+
+Definition iProto_consistent {Σ V} (ps : list (iProto Σ V)) : iProp Σ :=
+  fixpoint iProto_consistent_pre' ps.
+
+Arguments iProto_consistent {_ _} _%proto.
+Global Instance: Params (@iProto_consistent) 1 := {}.
+
+Global Instance iProto_consistent_ne {Σ V} : NonExpansive (@iProto_consistent Σ V).
+Proof. solve_proper. Qed.
+Global Instance iProto_consistent_proper {Σ V} : Proper ((≡) ==> (⊣⊢)) (@iProto_consistent Σ V).
+Proof. solve_proper. Qed.
+
+Lemma iProto_consistent_unfold {Σ V} (ps : list (iProto Σ V)) :
+  iProto_consistent ps ≡ (iProto_consistent_pre iProto_consistent) ps.
+Proof.
+  apply: (fixpoint_unfold iProto_consistent_pre').
+Qed.
+
+(** * Protocol entailment *)
+Definition iProto_le_pre {Σ V}
+    (rec : iProto Σ V → iProto Σ V → iProp Σ) (p1 p2 : iProto Σ V) : iProp Σ :=
+  (p1 ≡ END ∗ p2 ≡ END) ∨
+  ∃ a1 a2 m1 m2,
+    (p1 ≡ <a1> m1) ∗ (p2 ≡ <a2> m2) ∗
+    match a1, a2 with
+    | (Recv,i), (Recv,j) => ⌜i = j⌝ ∗ ∀ v p1',
+       iMsg_car m1 v (Next p1') -∗ ∃ p2', ▷ rec p1' p2' ∗ iMsg_car m2 v (Next p2')
+    | (Send,i), (Send,j) => ⌜i = j⌝ ∗ ∀ v p2',
+       iMsg_car m2 v (Next p2') -∗ ∃ p1', ▷ rec p1' p2' ∗ iMsg_car m1 v (Next p1')
+    | _, _ => False
+    end.
+Global Instance iProto_le_pre_ne {Σ V} (rec : iProto Σ V → iProto Σ V → iProp Σ) :
+  NonExpansive2 (iProto_le_pre rec).
+Proof. solve_proper. Qed.
+
+Program Definition iProto_le_pre' {Σ V}
+    (rec : iProto Σ V -n> iProto Σ V -n> iPropO Σ) :
+    iProto Σ V -n> iProto Σ V -n> iPropO Σ := λne p1 p2,
+  iProto_le_pre (λ p1' p2', rec p1' p2') p1 p2.
+Solve Obligations with solve_proper.
+Local Instance iProto_le_pre_contractive {Σ V} : Contractive (@iProto_le_pre' Σ V).
+Proof.
+  intros n rec1 rec2 Hrec p1 p2. rewrite /iProto_le_pre' /iProto_le_pre /=.
+  by repeat (f_contractive || f_equiv).
+Qed.
+Definition iProto_le {Σ V} (p1 p2 : iProto Σ V) : iProp Σ :=
+  fixpoint iProto_le_pre' p1 p2.
+Arguments iProto_le {_ _} _%proto _%proto.
+Global Instance: Params (@iProto_le) 2 := {}.
+Notation "p ⊑ q" := (iProto_le p q) : bi_scope.
+
+Global Instance iProto_le_ne {Σ V} : NonExpansive2 (@iProto_le Σ V).
+Proof. solve_proper. Qed.
+Global Instance iProto_le_proper {Σ V} : Proper ((≡) ==> (≡) ==> (⊣⊢)) (@iProto_le Σ V).
+Proof. solve_proper. Qed.
+
+Record proto_name := ProtName { proto_names : gmap nat gname }.
+Global Instance proto_name_inhabited : Inhabited proto_name :=
+  populate (ProtName inhabitant).
+Global Instance proto_name_eq_dec : EqDecision proto_name.
+Proof. solve_decision. Qed.
+Global Instance proto_name_countable : Countable proto_name.
+Proof.
+ refine (inj_countable (λ '(ProtName γs), (γs))
+   (λ '(γs), Some (ProtName γs)) _); by intros [].
+Qed.
+
+Definition iProto_own_frag `{!protoG Σ V} (γ : gname)
+    (i : nat) (p : iProto Σ V) : iProp Σ :=
+  own γ (gmap_view_frag i (DfracOwn 1) (Excl' (Next p))).
+
+Definition iProto_own_auth `{!protoG Σ V} (γ : gname)
+    (ps : list (iProto Σ V)) : iProp Σ :=
+  own γ (gmap_view_auth (DfracOwn 1) (((λ p, Excl' (Next p)) <$>
+    (list_to_map (zip (seq 0 (length ps)) ps))) : gmap _ _)).
+
+Definition iProto_ctx `{protoG Σ V}
+    (γ : gname) (ps_len : nat) : iProp Σ :=
+  ∃ ps, ⌜length ps = ps_len⌝ ∗ iProto_own_auth γ ps ∗ ▷ iProto_consistent ps.
+
+(** * The connective for ownership of channel ends *)
+Definition iProto_own `{!protoG Σ V}
+    (γ : gname) (i : nat) (p : iProto Σ V) : iProp Σ :=
+  ∃ p', ▷ (p' ⊑ p) ∗ iProto_own_frag γ i p'.
+Arguments iProto_own {_ _ _} _ _ _%proto.
+Global Instance: Params (@iProto_own) 3 := {}.
+
+Global Instance iProto_own_frag_contractive `{protoG Σ V} γ i :
+  Contractive (iProto_own_frag γ i).
+Proof. solve_contractive. Qed.
+
+Global Instance iProto_own_contractive `{protoG Σ V} γ i :
+  Contractive (iProto_own γ i).
+Proof. solve_contractive. Qed.
+Global Instance iProto_own_ne `{protoG Σ V} γ s : NonExpansive (iProto_own γ s).
+Proof. solve_proper. Qed.
+Global Instance iProto_own_proper `{protoG Σ V} γ s :
+  Proper ((≡) ==> (≡)) (iProto_own γ s).
+Proof. apply (ne_proper _). Qed.
+
+(** * Proofs *)
+Section proto.
+  Context `{!protoG Σ V}.
+  Implicit Types v : V.
+  Implicit Types p pl pr : iProto Σ V.
+  Implicit Types m : iMsg Σ V.
+
+  Lemma own_prot_idx γ i j (p1 p2 : iProto Σ V) :
+    own γ (gmap_view_frag i (DfracOwn 1) (Excl' (Next p1))) -∗
+    own γ (gmap_view_frag j (DfracOwn 1) (Excl' (Next p2))) -∗
+    ⌜i ≠ j⌝.
+  Proof.
+    iIntros "Hown Hown'" (->).
+    iDestruct (own_valid_2 with "Hown Hown'") as "H".
+    rewrite uPred.cmra_valid_elim.
+    by iDestruct "H" as %[]%gmap_view_frag_op_validN.
+  Qed.
+
+  Lemma own_prot_excl γ i (p1 p2 : iProto Σ V) :
+    own γ (gmap_view_frag i (DfracOwn 1) (Excl' (Next p1))) -∗
+    own γ (gmap_view_frag i (DfracOwn 1) (Excl' (Next p2))) -∗
+    False.
+  Proof. iIntros "Hi Hj". by iDestruct (own_prot_idx with "Hi Hj") as %?. Qed.
+
+  (** ** Protocol entailment **)
+  Lemma iProto_le_unfold p1 p2 : iProto_le p1 p2 ≡ iProto_le_pre iProto_le p1 p2.
+  Proof. apply: (fixpoint_unfold iProto_le_pre'). Qed.
+
+  Lemma iProto_le_end : ⊢ END ⊑ (END : iProto Σ V).
+  Proof. rewrite iProto_le_unfold. iLeft. auto 10. Qed.
+
+  Lemma iProto_le_end_inv_r p : p ⊑ END -∗ (p ≡ END).
+  Proof.
+    rewrite iProto_le_unfold. iIntros "[[Hp _]|H] //".
+    iDestruct "H" as (a1 a2 m1 m2) "(_ & Heq & _)".
+    by iDestruct (iProto_end_message_equivI with "Heq") as %[].
+  Qed.
+
+  Lemma iProto_le_end_inv_l p : END ⊑ p -∗ (p ≡ END).
+  Proof.
+    rewrite iProto_le_unfold. iIntros "[[_ Hp]|H] //".
+    iDestruct "H" as (a1 a2 m1 m2) "(Heq & _ & _)".
+    iDestruct (iProto_end_message_equivI with "Heq") as %[].
+  Qed.
+
+  Lemma iProto_le_send_inv i p1 m2 :
+    p1 ⊑ (<(Send,i)> m2) -∗ ∃ m1,
+      (p1 ≡ <(Send,i)> m1) ∗
+      ∀ v p2', iMsg_car m2 v (Next p2') -∗
+               ∃ p1', ▷ (p1' ⊑ p2') ∗ iMsg_car m1 v (Next p1').
+  Proof.
+    rewrite iProto_le_unfold.
+    iIntros "[[_ Heq]|H]".
+    { by iDestruct (iProto_message_end_equivI with "Heq") as %[]. }
+    iDestruct "H" as (a1 a2 m1 m2') "(Hp1 & Hp2 & H)".
+    rewrite iProto_message_equivI. iDestruct "Hp2" as "[%Heq Hm2]".
+    simplify_eq.
+    destruct a1 as [[]]; [|done].
+    iDestruct "H" as (->) "H". iExists m1. iFrame "Hp1".
+    iIntros (v p2). iSpecialize ("Hm2" $! v (Next p2)). by iRewrite "Hm2".
+  Qed.
+
+  Lemma iProto_le_send_send_inv i m1 m2 v p2' :
+    (<(Send,i)> m1) ⊑ (<(Send,i)> m2) -∗
+    iMsg_car m2 v (Next p2') -∗ ∃ p1', ▷ (p1' ⊑ p2') ∗ iMsg_car m1 v (Next p1').
+  Proof.
+    iIntros "H Hm2". iDestruct (iProto_le_send_inv with "H") as (m1') "[Hm1 H]".
+    iDestruct (iProto_message_equivI with "Hm1") as (Heq) "Hm1".
+    iDestruct ("H" with "Hm2") as (p1') "[Hle Hm]".
+    iRewrite -("Hm1" $! v (Next p1')) in "Hm". auto with iFrame.
+  Qed.
+
+  Lemma iProto_le_recv_inv_l i m1 p2 :
+    (<(Recv,i)> m1) ⊑ p2 -∗ ∃ m2,
+      (p2 ≡ <(Recv,i)> m2) ∗
+      ∀ v p1', iMsg_car m1 v (Next p1') -∗
+               ∃ p2', ▷ (p1' ⊑ p2') ∗ iMsg_car m2 v (Next p2').
+  Proof.
+    rewrite iProto_le_unfold.
+    iIntros "[[Heq _]|H]".
+    { iDestruct (iProto_message_end_equivI with "Heq") as %[]. }
+    iDestruct "H" as (a1 a2 m1' m2) "(Hp1 & Hp2 & H)".
+    rewrite iProto_message_equivI. iDestruct "Hp1" as "[%Heq Hm1]".
+    simplify_eq.
+    destruct a2 as [[]]; [done|].
+    iDestruct "H" as (->) "H". iExists m2. iFrame "Hp2".
+    iIntros (v p1). iSpecialize ("Hm1" $! v (Next p1)). by iRewrite "Hm1".
+  Qed.
+
+  Lemma iProto_le_recv_inv_r i p1 m2 :
+    (p1 ⊑ <(Recv,i)> m2) -∗ ∃ m1,
+      (p1 ≡ <(Recv,i)> m1) ∗
+      ∀ v p1', iMsg_car m1 v (Next p1') -∗
+               ∃ p2', ▷ (p1' ⊑ p2') ∗ iMsg_car m2 v (Next p2').
+  Proof.
+    rewrite iProto_le_unfold.
+    iIntros "[[_ Heq]|H]".
+    { iDestruct (iProto_message_end_equivI with "Heq") as %[]. }
+    iDestruct "H" as (a1 a2 m1 m2') "(Hp1 & Hp2 & H)".
+    rewrite iProto_message_equivI.
+    iDestruct "Hp2" as "[%Heq Hm2]".
+    simplify_eq.
+    destruct a1 as [[]]; [done|].
+    iDestruct "H" as (->) "H".
+    iExists m1. iFrame.
+    iIntros (v p2).
+    iIntros "Hm1". iDestruct ("H" with "Hm1") as (p2') "[Hle H]".
+    iSpecialize ("Hm2" $! v (Next p2')).
+    iExists p2'. iFrame.
+    iRewrite "Hm2". iApply "H".
+  Qed.
+
+  Lemma iProto_le_recv_recv_inv i m1 m2 v p1' :
+    (<(Recv, i)> m1) ⊑ (<(Recv, i)> m2) -∗
+    iMsg_car m1 v (Next p1') -∗ ∃ p2', ▷ (p1' ⊑ p2') ∗ iMsg_car m2 v (Next p2').
+  Proof.
+    iIntros "H Hm2". iDestruct (iProto_le_recv_inv_r with "H") as (m1') "[Hm1 H]".
+    iApply "H". iDestruct (iProto_message_equivI with "Hm1") as (_) "Hm1".
+    by iRewrite -("Hm1" $! v (Next p1')).
+  Qed.
+
+  Lemma iProto_le_msg_inv_l i a m1 p2 :
+    (<(a,i)> m1) ⊑ p2 -∗ ∃ m2, p2 ≡ <(a,i)> m2.
+  Proof.
+    rewrite iProto_le_unfold /iProto_le_pre.
+    iIntros "[[Heq _]|H]".
+    { iDestruct (iProto_message_end_equivI with "Heq") as %[]. }
+    iDestruct "H" as (a1 a2 m1' m2) "(Hp1 & Hp2 & H)".
+    destruct a1 as [t1 ?], a2 as [t2 ?].
+    destruct t1,t2; [|done|done|].
+    - rewrite iProto_message_equivI.
+      iDestruct "Hp1" as (Heq) "Hp1". simplify_eq.
+      iDestruct "H" as (->) "H". by iExists _.
+    - rewrite iProto_message_equivI.
+      iDestruct "Hp1" as (Heq) "Hp1". simplify_eq.
+      iDestruct "H" as (->) "H". by iExists _.
+  Qed.
+
+  Lemma iProto_le_msg_inv_r j a p1 m2 :
+    (p1 ⊑ <(a,j)> m2) -∗ ∃ m1, p1 ≡ <(a,j)> m1.
+  Proof.
+    rewrite iProto_le_unfold /iProto_le_pre.
+    iIntros "[[_ Heq]|H]".
+    { iDestruct (iProto_message_end_equivI with "Heq") as %[]. }
+    iDestruct "H" as (a1 a2 m1 m2') "(Hp1 & Hp2 & H)".
+    destruct a1 as [t1 ?], a2 as [t2 ?].
+    destruct t1,t2; [|done|done|].
+    - rewrite iProto_message_equivI.
+      iDestruct "Hp2" as (Heq) "Hp2". simplify_eq.
+      iDestruct "H" as (->) "H". by iExists _.
+    - rewrite iProto_message_equivI.
+      iDestruct "Hp2" as (Heq) "Hp2". simplify_eq.
+      iDestruct "H" as (->) "H". by iExists _.
+  Qed.
+
+  Lemma valid_target_le ps i p1 p2 :
+    (∀ i' j', valid_target ps i' j') -∗
+    ps !!! i ≡ p1 -∗
+    p1 ⊑ p2 -∗
+    (∀ i' j', valid_target (<[i := p2]>ps) i' j') ∗ p1 ⊑ p2.
+  Proof. Admitted.
+  (*   iIntros "Hprot #HSome Hle". *)
+  (*   pose proof (iProto_case p1) as [Hend|Hmsg]. *)
+  (*   { rewrite Hend. iDestruct (iProto_le_end_inv_l with "Hle") as "#H". *)
+  (*     iFrame "Hle". *)
+  (*     iIntros (i' j' a m) "Hm". *)
+  (*     destruct (decide (i = j')) as [->|Hneqj]. *)
+  (*     { Search list_lookup_total insert. rewrite list_lookup_total_insert. ; [done|]. lia. done. } *)
+  (*     rewrite lookup_insert_ne; [|done]. *)
+  (*     destruct (decide (i = i')) as [->|Hneqi]. *)
+  (*     { rewrite lookup_total_insert. iRewrite "H" in "Hm". *)
+  (*       by iDestruct (iProto_end_message_equivI with "Hm") as "Hm". } *)
+  (*     rewrite lookup_total_insert_ne; [|done]. *)
+  (*     by iApply "Hprot". } *)
+  (*   destruct Hmsg as (t & n & m & Hmsg). *)
+  (*   setoid_rewrite Hmsg. *)
+  (*   iDestruct (iProto_le_msg_inv_l with "Hle") as (m2) "#Heq". iFrame "Hle". *)
+  (*   iIntros (i' j' a m') "Hm". *)
+  (*   destruct (decide (i = j')) as [->|Hneqj]. *)
+  (*   { rewrite lookup_insert. done. } *)
+  (*   rewrite lookup_insert_ne; [|done]. *)
+  (*   destruct (decide (i = i')) as [->|Hneqi]. *)
+  (*   { rewrite lookup_total_insert. iRewrite "Heq" in "Hm". *)
+  (*     iDestruct (iProto_message_equivI with "Hm") as (Heq) "Hm". *)
+  (*     simplify_eq. by iApply "Hprot". } *)
+  (*   rewrite lookup_total_insert_ne; [|done]. *)
+  (*   by iApply "Hprot". *)
+  (* Qed. *)
+
+  Lemma iProto_consistent_le ps i p1 p2 :
+    iProto_consistent ps -∗
+    ps !!! i ≡ p1 -∗
+    p1 ⊑ p2 -∗
+    iProto_consistent (<[i := p2]>ps).
+  Proof.
+    iIntros "Hprot #HSome Hle".
+    iRevert "HSome".
+    iLöb as "IH" forall (p1 p2 ps).
+    iIntros "#HSome".
+    rewrite !iProto_consistent_unfold.
+    iDestruct "Hprot" as "(Htar & Hprot)".
+    iDestruct (valid_target_le with "Htar HSome Hle") as "[Htar Hle]".
+    iFrame.
+    iIntros (i' j' m1 m2) "#Hm1 #Hm2".
+    destruct (decide (i = i')) as [<-|Hneq].
+    { rewrite list_lookup_total_insert; [|admit].
+      pose proof (iProto_case p2) as [Hend|Hmsg].
+      { setoid_rewrite Hend. rewrite iProto_end_message_equivI. done. }
+      destruct Hmsg as (a&?&m&Hmsg).
+      setoid_rewrite Hmsg.
+      destruct a; last first.
+      { rewrite iProto_message_equivI.
+        iDestruct "Hm1" as "[%Htag Hm1]". done. }
+      rewrite iProto_message_equivI.
+      iDestruct "Hm1" as "[%Htag Hm1]".
+      inversion Htag. simplify_eq.
+      iIntros (v p) "Hm1'".
+      iSpecialize ("Hm1" $! v (Next p)).
+      iDestruct (iProto_le_send_inv with "Hle") as "Hle".
+      iRewrite -"Hm1" in "Hm1'".
+      iDestruct "Hle" as (m') "[#Heq H]".
+      iDestruct ("H" with "Hm1'") as (p') "[Hle H]".
+      destruct (decide (i = j')) as [<-|Hneq].
+      { rewrite list_lookup_total_insert. rewrite iProto_message_equivI.
+        iDestruct "Hm2" as "[%Heq _]". done. admit. }
+      iDestruct ("Hprot" $!i j' with "[] [] H") as "Hprot".
+      { iRewrite -"Heq". rewrite !list_lookup_total_alt. iRewrite "HSome". done. }
+      { rewrite list_lookup_total_insert_ne; [|done]. done. }
+      iDestruct "Hprot" as (p'') "[Hm Hprot]".
+      iExists p''. iFrame.
+      iNext.
+      iDestruct ("IH" with "Hprot Hle [HSome]") as "HI".
+      { rewrite list_lookup_total_insert; [done|]. admit. }
+      iClear "IH Hm1 Hm2 Heq".
+      rewrite list_insert_insert.
+      rewrite (list_insert_commute _ j' i); [|done].
+      rewrite list_insert_insert. done. }
+    rewrite list_lookup_total_insert_ne; [|done].
+    destruct (decide (i = j')) as [<-|Hneq'].
+    { rewrite list_lookup_total_insert.
+      pose proof (iProto_case p2) as [Hend|Hmsg].
+      { setoid_rewrite Hend. rewrite iProto_end_message_equivI. done. }
+      destruct Hmsg as (a&?&m&Hmsg).
+      setoid_rewrite Hmsg.
+      destruct a.
+      { rewrite iProto_message_equivI.
+        iDestruct "Hm2" as "[%Htag Hm2]". done. }
+      rewrite iProto_message_equivI.
+      iDestruct "Hm2" as "[%Htag Hm2]".
+      inversion Htag. simplify_eq.
+      iIntros (v p) "Hm1'".
+      iDestruct (iProto_le_recv_inv_r with "Hle") as "Hle".
+      iDestruct "Hle" as (m') "[#Heq Hle]".
+      iDestruct ("Hprot" $!i' with "[] [] Hm1'") as "Hprot".
+      { done. }
+      { rewrite !list_lookup_total_alt. iRewrite "HSome". done. }
+      iDestruct ("Hprot") as (p') "[Hm1' Hprot]".
+      iDestruct ("Hle" with "Hm1'") as (p2') "[Hle Hm']".
+      iSpecialize ("Hm2" $! v (Next p2')).
+      iExists p2'.
+      iRewrite -"Hm2". iFrame.
+      iDestruct ("IH" with "Hprot Hle []") as "HI".
+      { iPureIntro. rewrite list_lookup_total_insert_ne; [|done].
+        rewrite list_lookup_total_insert. done. admit. }
+      rewrite list_insert_commute; [|done].
+      rewrite !list_insert_insert. done. admit. }
+    rewrite list_lookup_total_insert_ne; [|done].
+    iIntros (v p) "Hm1'".
+    iDestruct ("Hprot" $!i' j' with "[//] [//] Hm1'") as "Hprot".
+    iDestruct "Hprot" as (p') "[Hm2' Hprot]".
+    iExists p'. iFrame.
+    iNext.
+    rewrite (list_insert_commute _ j' i); [|done].
+    rewrite (list_insert_commute _ i' i); [|done].
+    iApply ("IH" with "Hprot Hle []").
+    rewrite list_lookup_total_insert_ne; [|done].
+    rewrite list_lookup_total_insert_ne; [|done].
+    done.
+  Admitted.
+  
+  Lemma iProto_le_send i m1 m2 :
+    (∀ v p2', iMsg_car m2 v (Next p2') -∗ ∃ p1',
+      ▷ (p1' ⊑ p2') ∗ iMsg_car m1 v (Next p1')) -∗
+    (<(Send,i)> m1) ⊑ (<(Send,i)> m2).
+  Proof.
+    iIntros "Hle". rewrite iProto_le_unfold.
+    iRight. iExists (Send, i), (Send, i), m1, m2. by eauto.
+  Qed.
+
+  Lemma iProto_le_recv i m1 m2 :
+    (∀ v p1', iMsg_car m1 v (Next p1') -∗ ∃ p2',
+      ▷ (p1' ⊑ p2') ∗ iMsg_car m2 v (Next p2')) -∗
+    (<(Recv,i)> m1) ⊑ (<(Recv,i)> m2).
+  Proof.
+    iIntros "Hle". rewrite iProto_le_unfold.
+    iRight. iExists (Recv, i), (Recv, i), m1, m2. by eauto.
+  Qed.
+
+  Lemma iProto_le_base a v P p1 p2 :
+    ▷ (p1 ⊑ p2) -∗
+    (<a> MSG v {{ P }}; p1) ⊑ (<a> MSG v {{ P }}; p2).
+  Proof.
+    rewrite iMsg_base_eq. iIntros "H". destruct a as [[]].
+    - iApply iProto_le_send. iIntros (v' p') "(->&Hp&$)".
+      iExists p1. iSplit; [|by auto]. iIntros "!>". by iRewrite -"Hp".
+    - iApply iProto_le_recv. iIntros (v' p') "(->&Hp&$)".
+      iExists p2. iSplit; [|by auto]. iIntros "!>". by iRewrite -"Hp".
+  Qed.
+
+  Lemma iProto_le_trans p1 p2 p3 : p1 ⊑ p2 -∗ p2 ⊑ p3 -∗ p1 ⊑ p3.
+  Proof.
+    iIntros "H1 H2". iLöb as "IH" forall (p1 p2 p3).
+    destruct (iProto_case p3) as [->|([]&i&m3&->)].
+    - iDestruct (iProto_le_end_inv_r with "H2") as "H2". by iRewrite "H2" in "H1".
+    - iDestruct (iProto_le_send_inv with "H2") as (m2) "[Hp2 H2]".
+      iRewrite "Hp2" in "H1"; clear p2.
+      iDestruct (iProto_le_send_inv with "H1") as (m1) "[Hp1 H1]".
+      iRewrite "Hp1"; clear p1.
+      iApply iProto_le_send. iIntros (v p3') "Hm3".
+      iDestruct ("H2" with "Hm3") as (p2') "[Hle Hm2]".
+      iDestruct ("H1" with "Hm2") as (p1') "[Hle' Hm1]".
+      iExists p1'. iIntros "{$Hm1} !>". by iApply ("IH" with "Hle'").
+    - iDestruct (iProto_le_recv_inv_r with "H2") as (m2) "[Hp2 H3]".
+      iRewrite "Hp2" in "H1".
+      iDestruct (iProto_le_recv_inv_r with "H1") as (m1) "[Hp1 H2]".
+      iRewrite "Hp1". iApply iProto_le_recv. iIntros (v p1') "Hm1".
+      iDestruct ("H2" with "Hm1") as (p2') "[Hle Hm2]".
+      iDestruct ("H3" with "Hm2") as (p3') "[Hle' Hm3]".
+      iExists p3'. iIntros "{$Hm3} !>". by iApply ("IH" with "Hle").
+  Qed.
+
+  Lemma iProto_le_refl p : ⊢ p ⊑ p.
+  Proof.
+    iLöb as "IH" forall (p). destruct (iProto_case p) as [->|([]&i&m&->)].
+    - iApply iProto_le_end.
+    - iApply iProto_le_send. auto 10 with iFrame.
+    - iApply iProto_le_recv. auto 10 with iFrame.
+  Qed.
+
+  Lemma iProto_le_dual p1 p2 : p2 ⊑ p1 -∗ iProto_dual p1 ⊑ iProto_dual p2.
+  Proof.
+    iIntros "H". iLöb as "IH" forall (p1 p2).
+    destruct (iProto_case p1) as [->|([]&i&m1&->)].
+    - iDestruct (iProto_le_end_inv_r with "H") as "H".
+      iRewrite "H". iApply iProto_le_refl.
+    - iDestruct (iProto_le_send_inv with "H") as (m2) "[Hp2 H]".
+      iRewrite "Hp2"; clear p2. iEval (rewrite !iProto_dual_message).
+      iApply iProto_le_recv. iIntros (v p1d).
+      iDestruct 1 as (p1') "[Hm1 #Hp1d]".
+      iDestruct ("H" with "Hm1") as (p2') "[H Hm2]".
+      iDestruct ("IH" with "H") as "H". iExists (iProto_dual p2').
+      iSplitL "H"; [iIntros "!>"; by iRewrite "Hp1d"|]. simpl; auto.
+    - iDestruct (iProto_le_recv_inv_r with "H") as (m2) "[Hp2 H]".
+      iRewrite "Hp2"; clear p2. iEval (rewrite !iProto_dual_message /=).
+      iApply iProto_le_send. iIntros (v p2d).
+      iDestruct 1 as (p2') "[Hm2 #Hp2d]".
+      iDestruct ("H" with "Hm2") as (p1') "[H Hm1]".
+      iDestruct ("IH" with "H") as "H". iExists (iProto_dual p1').
+      iSplitL "H"; [iIntros "!>"; by iRewrite "Hp2d"|]. simpl; auto.
+  Qed.
+
+  Lemma iProto_le_dual_l p1 p2 : iProto_dual p2 ⊑ p1 ⊢ iProto_dual p1 ⊑ p2.
+  Proof.
+    iIntros "H". iEval (rewrite -(involutive iProto_dual p2)).
+    by iApply iProto_le_dual.
+  Qed.
+  Lemma iProto_le_dual_r p1 p2 : p2 ⊑ iProto_dual p1 ⊢ p1 ⊑ iProto_dual p2.
+  Proof.
+    iIntros "H". iEval (rewrite -(involutive iProto_dual p1)).
+    by iApply iProto_le_dual.
+  Qed.
+
+  Lemma iProto_le_app p1 p2 p3 p4 :
+    p1 ⊑ p2 -∗ p3 ⊑ p4 -∗ p1 <++> p3 ⊑ p2 <++> p4.
+  Proof.
+    iIntros "H1 H2". iLöb as "IH" forall (p1 p2 p3 p4).
+    destruct (iProto_case p2) as [->|([]&i&m2&->)].
+    - iDestruct (iProto_le_end_inv_r with "H1") as "H1".
+      iRewrite "H1". by rewrite !left_id.
+    - iDestruct (iProto_le_send_inv with "H1") as (m1) "[Hp1 H1]".
+      iRewrite "Hp1"; clear p1. rewrite !iProto_app_message.
+      iApply iProto_le_send. iIntros (v p24).
+      iDestruct 1 as (p2') "[Hm2 #Hp24]".
+      iDestruct ("H1" with "Hm2") as (p1') "[H1 Hm1]".
+      iExists (p1' <++> p3). iSplitR "Hm1"; [|by simpl; eauto].
+      iIntros "!>". iRewrite "Hp24". by iApply ("IH" with "H1").
+    - iDestruct (iProto_le_recv_inv_r with "H1") as (m1) "[Hp1 H1]".
+      iRewrite "Hp1"; clear p1. rewrite !iProto_app_message.
+      iApply iProto_le_recv.
+      iIntros (v p13). iDestruct 1 as (p1') "[Hm1 #Hp13]".
+      iDestruct ("H1" with "Hm1") as (p2'') "[H1 Hm2]".
+      iExists (p2'' <++> p4). iSplitR "Hm2"; [|by simpl; eauto].
+      iIntros "!>". iRewrite "Hp13". by iApply ("IH" with "H1").
+  Qed.
+
+  Lemma iProto_le_payload_elim_l i m v P p :
+    (P -∗ (<(Recv,i)> MSG v; p) ⊑ (<(Recv,i)> m)) ⊢
+    (<(Recv,i)> MSG v {{ P }}; p) ⊑ <(Recv,i)> m.
+  Proof.
+    rewrite iMsg_base_eq. iIntros "H".
+    iApply iProto_le_recv. iIntros (v' p') "(->&Hp&HP)".
+    iApply (iProto_le_recv_recv_inv with "(H HP)"); simpl; auto.
+  Qed.
+  Lemma iProto_le_payload_elim_r i m v P p :
+    (P -∗ (<(Send, i)> m) ⊑ (<(Send, i)> MSG v; p)) ⊢
+    (<(Send,i)> m) ⊑ (<(Send,i)> MSG v {{ P }}; p).
+  Proof.
+    rewrite iMsg_base_eq. iIntros "H".
+    iApply iProto_le_send. iIntros (v' p') "(->&Hp&HP)".
+    iApply (iProto_le_send_send_inv with "(H HP)"); simpl; auto.
+  Qed.
+  Lemma iProto_le_payload_intro_l i v P p :
+    P -∗ (<(Send,i)> MSG v {{ P }}; p) ⊑ (<(Send,i)> MSG v; p).
+  Proof.
+    rewrite iMsg_base_eq.
+    iIntros "HP". iApply iProto_le_send. iIntros (v' p') "(->&Hp&_) /=".
+    iExists p'. iSplitR; [iApply iProto_le_refl|]. auto.
+  Qed.
+  Lemma iProto_le_payload_intro_r i v P p :
+    P -∗ (<(Recv,i)> MSG v; p) ⊑ (<(Recv,i)> MSG v {{ P }}; p).
+  Proof.
+    rewrite iMsg_base_eq.
+    iIntros "HP". iApply iProto_le_recv. iIntros (v' p') "(->&Hp&_) /=".
+    iExists p'. iSplitR; [iApply iProto_le_refl|]. auto.
+  Qed.
+  Lemma iProto_le_exist_elim_l {A} i (m1 : A → iMsg Σ V) m2 :
+    (∀ x, (<(Recv,i)> m1 x) ⊑ (<(Recv,i)> m2)) ⊢
+    (<(Recv,i) @ x> m1 x) ⊑ (<(Recv,i)> m2).
+  Proof.
+    rewrite iMsg_exist_eq. iIntros "H".
+    iApply iProto_le_recv. iIntros (v p1') "/=". iDestruct 1 as (x) "Hm".
+    by iApply (iProto_le_recv_recv_inv with "H").
+  Qed.
+  Lemma iProto_le_exist_elim_r {A} i m1 (m2 : A → iMsg Σ V) :
+    (∀ x, (<(Send,i)> m1) ⊑ (<(Send,i)> m2 x)) ⊢
+    (<(Send,i)> m1) ⊑ (<(Send,i) @ x> m2 x).
+  Proof.
+    rewrite iMsg_exist_eq. iIntros "H".
+    iApply iProto_le_send. iIntros (v p2'). iDestruct 1 as (x) "Hm".
+    by iApply (iProto_le_send_send_inv with "H").
+  Qed.
+  Lemma iProto_le_exist_intro_l {A} i (m : A → iMsg Σ V) a :
+    ⊢ (<(Send,i) @ x> m x) ⊑ (<(Send,i)> m a).
+  Proof.
+    rewrite iMsg_exist_eq. iApply iProto_le_send. iIntros (v p') "Hm /=".
+    iExists p'. iSplitR; last by auto. iApply iProto_le_refl.
+  Qed.
+  Lemma iProto_le_exist_intro_r {A} i (m : A → iMsg Σ V) a :
+    ⊢ (<(Recv,i)> m a) ⊑ (<(Recv,i) @ x> m x).
+  Proof.
+    rewrite iMsg_exist_eq. iApply iProto_le_recv. iIntros (v p') "Hm /=".
+    iExists p'. iSplitR; last by auto. iApply iProto_le_refl.
+  Qed.
+
+  Lemma iProto_le_texist_elim_l {TT : tele} i (m1 : TT → iMsg Σ V) m2 :
+    (∀ x, (<(Recv,i)> m1 x) ⊑ (<(Recv,i)> m2)) ⊢
+    (<(Recv,i) @.. x> m1 x) ⊑ (<(Recv,i)> m2).
+  Proof.
+    iIntros "H". iInduction TT as [|T TT] "IH"; simpl; [done|].
+    iApply iProto_le_exist_elim_l; iIntros (x).
+    iApply "IH". iIntros (xs). iApply "H".
+  Qed.
+  Lemma iProto_le_texist_elim_r {TT : tele} i m1 (m2 : TT → iMsg Σ V) :
+    (∀ x, (<(Send,i)> m1) ⊑ (<(Send,i)> m2 x)) -∗
+    (<(Send,i)> m1) ⊑ (<(Send,i) @.. x> m2 x).
+  Proof.
+    iIntros "H". iInduction TT as [|T TT] "IH"; simpl; [done|].
+    iApply iProto_le_exist_elim_r; iIntros (x).
+    iApply "IH". iIntros (xs). iApply "H".
+  Qed.
+
+  Lemma iProto_le_texist_intro_l {TT : tele} i (m : TT → iMsg Σ V) x :
+    ⊢ (<(Send,i) @.. x> m x) ⊑ (<(Send,i)> m x).
+  Proof.
+    induction x as [|T TT x xs IH] using tele_arg_ind; simpl.
+    { iApply iProto_le_refl. }
+    iApply iProto_le_trans; [by iApply iProto_le_exist_intro_l|]. iApply IH.
+  Qed.
+  Lemma iProto_le_texist_intro_r {TT : tele} i (m : TT → iMsg Σ V) x :
+    ⊢ (<(Recv,i)> m x) ⊑ (<(Recv,i) @.. x> m x).
+  Proof.
+    induction x as [|T TT x xs IH] using tele_arg_ind; simpl.
+    { iApply iProto_le_refl. }
+    iApply iProto_le_trans; [|by iApply iProto_le_exist_intro_r]. iApply IH.
+  Qed.
+
+  Lemma iProto_consistent_target ps m a i j :
+    iProto_consistent ps -∗
+    ps !!! i ≡ (<(a, j)> m) -∗
+    ⌜is_Some (ps !! j)⌝.
+  Proof.
+    rewrite iProto_consistent_unfold. iDestruct 1 as "[Htar _]". iApply "Htar".
+  Qed.
+
+  Lemma iProto_consistent_step ps m1 m2 i j v p1 :
+    iProto_consistent ps -∗
+    ps !!! i ≡ (<(Send, j)> m1) -∗
+    ps !!! j ≡ (<(Recv, i)> m2) -∗
+    iMsg_car m1 v (Next p1) -∗
+    ∃ p2, iMsg_car m2 v (Next p2) ∗
+          â–· iProto_consistent (<[i := p1]>(<[j := p2]>ps)).
+  Proof.
+    iIntros "Hprot #Hi #Hj Hm1".
+    rewrite iProto_consistent_unfold /iProto_consistent_pre.
+    iDestruct "Hprot" as "[_ Hprot]".
+    iDestruct ("Hprot" with "Hi Hj Hm1") as (p2) "[Hm2 Hprot]".
+    iExists p2. iFrame.
+  Qed.
+
+  Global Instance iProto_own_frag_ne γ s : NonExpansive (iProto_own_frag γ s).
+  Proof. solve_proper. Qed.
+
+  Lemma iProto_own_auth_agree γ ps i p :
+    iProto_own_auth γ ps -∗ iProto_own_frag γ i p -∗ ▷ (ps !!! i ≡ p).
+  Proof. Admitted.
+  (*   iIntros "H● H◯". *)
+  (*   iDestruct (own_valid_2 with "H● H◯") as "H✓". *)
+  (*   rewrite gmap_view_both_validI. *)
+  (*   iDestruct "H✓" as "[_ [H1 H2]]". *)
+  (*   rewrite list_lookup_total_alt lookup_fmap. *)
+  (*   destruct (ps !! i); last first. *)
+  (*   { rewrite !option_equivI. } *)
+  (*   simpl. rewrite !option_equivI excl_equivI. by iNext. *)
+  (* Qed. *)
+
+  Lemma iProto_own_auth_update γ ps i p p' :
+    iProto_own_auth γ ps -∗ iProto_own_frag γ i p ==∗
+    iProto_own_auth γ (<[i := p']>ps) ∗ iProto_own_frag γ i p'.
+  Proof.
+    iIntros "H● H◯".
+    iMod (own_update_2 with "H● H◯") as "[H1 H2]"; [|iModIntro].
+    { eapply (gmap_view_replace _ _ _ (Excl' (Next p'))). done. }
+    iFrame. rewrite -fmap_insert. Admitted.
+  
+  Lemma iProto_own_auth_alloc ps :
+    ⊢ |==> ∃ γ, iProto_own_auth γ ps ∗ [∗ list] i ↦p ∈ ps, iProto_own γ i p.
+  Proof. Admitted.
+  (*   iMod (own_alloc (gmap_view_auth (DfracOwn 1) ∅)) as (γ) "Hauth". *)
+  (*   { apply gmap_view_auth_valid. } *)
+  (*   iExists γ. *)
+  (*   iInduction ps as [|i p ps Hin] "IH" using map_ind. *)
+  (*   { iModIntro. iFrame. by iApply big_sepM_empty. } *)
+  (*   iMod ("IH" with "Hauth") as "[Hauth Hfrags]". *)
+  (*   rewrite big_sepM_insert; [|done]. iFrame "Hfrags". *)
+  (*   iMod (own_update with "Hauth") as "[Hauth Hfrag]". *)
+  (*   { apply (gmap_view_alloc _ i (DfracOwn 1) (Excl' (Next p))); [|done|done]. *)
+  (*     by rewrite lookup_fmap Hin. } *)
+  (*   iModIntro. rewrite -fmap_insert. iFrame. *)
+  (*   iExists _. iFrame. iApply iProto_le_refl. *)
+  (* Qed. *)
+
+  Lemma iProto_own_le γ s p1 p2 :
+    iProto_own γ s p1 -∗ ▷ (p1 ⊑ p2) -∗ iProto_own γ s p2.
+  Proof.
+    iDestruct 1 as (p1') "[Hle H]". iIntros "Hle'".
+    iExists p1'. iFrame "H". by iApply (iProto_le_trans with "Hle").
+  Qed.
+
+  Lemma iProto_init ps :
+    ▷ iProto_consistent ps -∗
+    |==> ∃ γ, iProto_ctx γ (length ps) ∗ [∗ list] i ↦p ∈ ps, iProto_own γ i p.
+  Proof.
+    iIntros "Hconsistent".
+    iMod iProto_own_auth_alloc as (γ) "[Hauth Hfrags]".
+    iExists γ. iFrame. iExists _. by iFrame.
+  Qed.
+
+
+  Lemma iProto_step γ ps_dom i j m1 m2 p1 v :
+    iProto_ctx γ ps_dom -∗
+    iProto_own γ i (<(Send, j)> m1) -∗
+    iProto_own γ j (<(Recv, i)> m2) -∗
+    iMsg_car m1 v (Next p1) ==∗
+    ▷ ∃ p2, iMsg_car m2 v (Next p2) ∗ iProto_ctx γ ps_dom ∗
+            iProto_own γ i p1 ∗ iProto_own γ j p2.
+  Proof.
+    iIntros "Hctx Hi Hj Hm".
+    iDestruct "Hi" as (pi) "[Hile Hi]".
+    iDestruct "Hj" as (pj) "[Hjle Hj]".
+    iDestruct "Hctx" as (ps Hdom) "[Hauth Hconsistent]".
+    iDestruct (iProto_own_auth_agree with "Hauth Hi") as "#Hpi".
+    iDestruct (iProto_own_auth_agree with "Hauth Hj") as "#Hpj".
+    iDestruct (own_prot_idx with "Hi Hj") as %Hneq.
+    iAssert (▷ (<[i:=<(Send, j)> m1]>ps !!! j ≡ pj))%I as "Hpj'".
+    { by rewrite list_lookup_total_insert_ne. }
+    iAssert (▷ (⌜is_Some (ps !! i)⌝ ∗ (pi ⊑ (<(Send, j)> m1))))%I with "[Hile]"
+      as "[Hi' Hile]".
+    { iNext. iDestruct (iProto_le_msg_inv_r with "Hile") as (m) "#Heq".
+      iFrame. iRewrite "Heq" in "Hpi". rewrite list_lookup_total_alt.
+      destruct (ps !! i); [done|].
+      iDestruct (iProto_end_message_equivI with "Hpi") as "[]". }
+    iAssert (▷ (⌜is_Some (ps !! j)⌝ ∗ (pj ⊑ (<(Recv, i)> m2))))%I with "[Hjle]"
+      as "[Hj' Hjle]".
+    { iNext. iDestruct (iProto_le_msg_inv_r with "Hjle") as (m) "#Heq".
+      iFrame. iRewrite "Heq" in "Hpj". rewrite !list_lookup_total_alt.
+      destruct (ps !! j); [done|].
+      iDestruct (iProto_end_message_equivI with "Hpj") as "[]". }
+    iDestruct (iProto_consistent_le with "Hconsistent Hpi Hile") as "Hconsistent".
+    iDestruct (iProto_consistent_le with "Hconsistent Hpj' Hjle") as "Hconsistent".
+    iDestruct (iProto_consistent_step _ _ _ i j with "Hconsistent [] [] [Hm //]") as
+      (p2) "[Hm2 Hconsistent]".
+    { rewrite list_lookup_total_insert_ne; [|done].
+      rewrite list_lookup_total_insert_ne; [|done].
+      rewrite list_lookup_total_insert; [done|]. admit. }
+    { rewrite list_lookup_total_insert_ne; [|done].
+      rewrite list_lookup_total_insert; [done|]. admit. }
+    iMod (iProto_own_auth_update _ _ _ _ p2 with "Hauth Hj") as "[Hauth Hj]".
+    iMod (iProto_own_auth_update _ _ _ _ p1 with "Hauth Hi") as "[Hauth Hi]".
+    iIntros "!>!>". iExists p2. iFrame "Hm2".
+    iDestruct "Hi'" as %Hi. iDestruct "Hj'" as %Hj.
+    iSplitL "Hconsistent Hauth".
+    { iExists (<[i:=p1]> (<[j:=p2]> ps)).
+      iSplit.
+      { admit.
+      (* rewrite !dom_insert_lookup_L; [done..|by rewrite lookup_insert_ne].  *)}
+      iFrame. rewrite list_insert_insert.
+      rewrite list_insert_commute; [|done]. rewrite list_insert_insert.
+      by rewrite list_insert_commute; [|done]. }
+    iSplitL "Hi"; iExists _; iFrame; iApply iProto_le_refl.
+  Admitted.
+
+  Lemma iProto_target γ ps_dom i a j m :
+    iProto_ctx γ ps_dom -∗
+    iProto_own γ i (<(a, j)> m) -∗
+    ▷ (⌜j < ps_dom⌝) ∗ iProto_ctx γ ps_dom ∗ iProto_own γ i (<(a, j)> m).
+  Proof.
+    iIntros "Hctx Hown".
+    rewrite /iProto_ctx /iProto_own.
+    iDestruct "Hctx" as (ps Hdom) "[Hauth Hps]".
+    iDestruct "Hown" as (p') "[Hle Hown]".
+    iDestruct (iProto_own_auth_agree with "Hauth Hown") as "#Hi".
+    iDestruct (iProto_le_msg_inv_r with "Hle") as (m') "#Heq".
+    iAssert (▷ (⌜is_Some (ps !! j)⌝ ∗ iProto_consistent ps))%I
+      with "[Hps]" as "[HSome Hps]".
+    { iNext. iRewrite "Heq" in "Hi".
+      iDestruct (iProto_consistent_target with "Hps Hi") as "#$". by iFrame. }
+    iSplitL "HSome".
+    { iNext. iDestruct "HSome" as %Heq.
+      iPureIntro. simplify_eq. admit. }
+    iSplitL "Hauth Hps"; iExists _; by iFrame.
+  Admitted.
+
+  (* (** The instances below make it possible to use the tactics [iIntros], *)
+  (* [iExist], [iSplitL]/[iSplitR], [iFrame] and [iModIntro] on [iProto_le] goals. *) *)
+  Global Instance iProto_le_from_forall_l {A} i (m1 : A → iMsg Σ V) m2 name :
+    AsIdentName m1 name →
+    FromForall (iProto_message (Recv,i) (iMsg_exist m1) ⊑ (<(Recv,i)> m2))
+               (λ x, (<(Recv, i)> m1 x) ⊑ (<(Recv, i)> m2))%I name | 10.
+  Proof. intros _. apply iProto_le_exist_elim_l. Qed.
+  Global Instance iProto_le_from_forall_r {A} i m1 (m2 : A → iMsg Σ V) name :
+    AsIdentName m2 name →
+    FromForall ((<(Send,i)> m1) ⊑ iProto_message (Send,i) (iMsg_exist m2))
+               (λ x, (<(Send,i)> m1) ⊑ (<(Send,i)> m2 x))%I name | 11.
+  Proof. intros _. apply iProto_le_exist_elim_r. Qed.
+
+  Global Instance iProto_le_from_wand_l i m v P p :
+    TCIf (TCEq P True%I) False TCTrue →
+    FromWand ((<(Recv,i)> MSG v {{ P }}; p) ⊑ (<(Recv,i)> m)) P ((<(Recv,i)> MSG v; p) ⊑ (<(Recv,i)> m)) | 10.
+  Proof. intros _. apply iProto_le_payload_elim_l. Qed.
+  Global Instance iProto_le_from_wand_r i m v P p :
+    FromWand ((<(Send,i)> m) ⊑ (<(Send,i)> MSG v {{ P }}; p)) P ((<(Send,i)> m) ⊑ (<(Send,i)> MSG v; p)) | 11.
+  Proof. apply iProto_le_payload_elim_r. Qed.
+
+  Global Instance iProto_le_from_exist_l {A} i (m : A → iMsg Σ V) p :
+    FromExist ((<(Send,i) @ x> m x) ⊑ p) (λ a, (<(Send,i)> m a) ⊑ p)%I | 10.
+  Proof.
+    rewrite /FromExist. iDestruct 1 as (x) "H".
+    iApply (iProto_le_trans with "[] H"). iApply iProto_le_exist_intro_l.
+  Qed.
+  Global Instance iProto_le_from_exist_r {A} i (m : A → iMsg Σ V) p :
+    FromExist (p ⊑ <(Recv,i) @ x> m x) (λ a, p ⊑ (<(Recv,i)> m a))%I | 11.
+  Proof.
+    rewrite /FromExist. iDestruct 1 as (x) "H".
+    iApply (iProto_le_trans with "H"). iApply iProto_le_exist_intro_r.
+  Qed.
+
+  Global Instance iProto_le_from_sep_l i m v P p :
+    FromSep ((<(Send,i)> MSG v {{ P }}; p) ⊑ (<(Send,i)> m)) P ((<(Send,i)> MSG v; p) ⊑ (<(Send,i)> m)) | 10.
+  Proof.
+    rewrite /FromSep. iIntros "[HP H]".
+    iApply (iProto_le_trans with "[HP] H"). by iApply iProto_le_payload_intro_l.
+  Qed.
+  Global Instance iProto_le_from_sep_r i m v P p :
+    FromSep ((<(Recv,i)> m) ⊑ (<(Recv,i)> MSG v {{ P }}; p)) P ((<(Recv,i)> m) ⊑ (<(Recv,i)> MSG v; p)) | 11.
+  Proof.
+    rewrite /FromSep. iIntros "[HP H]".
+    iApply (iProto_le_trans with "H"). by iApply iProto_le_payload_intro_r.
+  Qed.
+
+  Global Instance iProto_le_frame_l i q m v R P Q p :
+    Frame q R P Q →
+    Frame q R ((<(Send,i)> MSG v {{ P }}; p) ⊑ (<(Send,i)> m))
+              ((<(Send,i)> MSG v {{ Q }}; p) ⊑ (<(Send,i)> m)) | 10.
+  Proof.
+    rewrite /Frame /=. iIntros (HP) "[HR H]".
+    iApply (iProto_le_trans with "[HR] H"). iApply iProto_le_payload_elim_r.
+    iIntros "HQ". iApply iProto_le_payload_intro_l. iApply HP; iFrame.
+  Qed.
+  Global Instance iProto_le_frame_r i q m v R P Q p :
+    Frame q R P Q →
+    Frame q R ((<(Recv,i)> m) ⊑ (<(Recv,i)> MSG v {{ P }}; p))
+              ((<(Recv,i)> m) ⊑ (<(Recv,i)> MSG v {{ Q }}; p)) | 11.
+  Proof.
+    rewrite /Frame /=. iIntros (HP) "[HR H]".
+    iApply (iProto_le_trans with "H"). iApply iProto_le_payload_elim_l.
+    iIntros "HQ". iApply iProto_le_payload_intro_r. iApply HP; iFrame.
+  Qed.
+
+  Global Instance iProto_le_from_modal a v p1 p2 :
+    FromModal True (modality_instances.modality_laterN 1) (p1 ⊑ p2)
+              ((<a> MSG v; p1) ⊑ (<a> MSG v; p2)) (p1 ⊑ p2).
+  Proof. intros _. iApply iProto_le_base. Qed.
+
+End proto.
+
+Typeclasses Opaque iProto_ctx iProto_own.
+
+Global Hint Extern 0 (environments.envs_entails _ (?x ⊑ ?y)) =>
+  first [is_evar x; fail 1 | is_evar y; fail 1|iApply iProto_le_refl] : core.
diff --git a/multi_actris/examples/basics.v b/multi_actris/examples/basics.v
index 9efa72bdd741c8aa00e88c4fc95c0bb39f38d361..36c500f5638399b41d1f224f642729436330879d 100644
--- a/multi_actris/examples/basics.v
+++ b/multi_actris/examples/basics.v
@@ -1,16 +1,15 @@
 From multi_actris.channel Require Import proofmode.
 Set Default Proof Using "Type".
 
-Definition iProto_empty {Σ} : gmap nat (iProto Σ) := ∅.
+Definition iProto_empty {Σ} : list (iProto Σ) := [].
 
 Lemma iProto_consistent_empty {Σ} :
   ⊢ iProto_consistent (@iProto_empty Σ).
-Proof. iProto_consistent_take_step. Qed.
+Proof. iProto_consistent_take_steps. Qed.
 
-Definition iProto_binary `{!heapGS Σ} : gmap nat (iProto Σ) :=
-  <[0 := (<(Send, 1) @ (x:Z)> MSG #x ; END)%proto ]>
-  (<[1 := (<(Recv, 0) @ (x:Z)> MSG #x ; END)%proto ]>
-   ∅).
+Definition iProto_binary `{!heapGS Σ} : list (iProto Σ) :=
+  [(<(Send, 1) @ (x:Z)> MSG #x ; END)%proto;
+   (<(Recv, 0) @ (x:Z)> MSG #x ; END)%proto].
 
 Lemma iProto_binary_consistent `{!heapGS Σ} :
   ⊢ iProto_consistent iProto_binary.
@@ -29,10 +28,10 @@ Definition roundtrip_prog : val :=
 Section channel.
   Context `{!heapGS Σ, !chanG Σ}.
 
-  Definition iProto_roundtrip : gmap nat (iProto Σ) :=
-     <[0 := (<(Send, 1) @ (x:Z)> MSG #x ; <(Recv, 2)> MSG #x; END)%proto ]>
-    (<[1 := (<(Recv, 0) @ (x:Z)> MSG #x ; <(Send, 2)> MSG #x; END)%proto ]>
-    (<[2 := (<(Recv, 1) @ (x:Z)> MSG #x ; <(Send, 0)> MSG #x; END)%proto ]> ∅)).
+  Definition iProto_roundtrip : list (iProto Σ) :=
+     [(<(Send, 1) @ (x:Z)> MSG #x ; <(Recv, 2)> MSG #x; END)%proto;
+      (<(Recv, 0) @ (x:Z)> MSG #x ; <(Send, 2)> MSG #x; END)%proto;
+      (<(Recv, 1) @ (x:Z)> MSG #x ; <(Send, 0)> MSG #x; END)%proto].
 
   Lemma iProto_roundtrip_consistent :
     ⊢ iProto_consistent iProto_roundtrip.
@@ -43,15 +42,8 @@ Section channel.
     {{{ True }}} roundtrip_prog #() {{{ RET #42 ; True }}}.
   Proof using chanG0 heapGS0 Σ.
     iIntros (Φ) "_ HΦ". wp_lam.
-    wp_smart_apply (new_chan_spec 3 iProto_roundtrip);
-      [lia|set_solver|iApply iProto_roundtrip_consistent|].
-    iIntros (cs) "Hcs".
-    wp_smart_apply (get_chan_spec _ 0 with "Hcs"); [set_solver|].
-    iIntros (c0) "[Hc0 Hcs]".
-    wp_smart_apply (get_chan_spec _ 1 with "Hcs"); [set_solver|].
-    iIntros (c1) "[Hc1 Hcs]".
-    wp_smart_apply (get_chan_spec _ 2 with "Hcs"); [set_solver|].
-    iIntros (c2) "[Hc2 Hcs]".
+    wp_new_chan iProto_roundtrip with iProto_roundtrip_consistent
+      as (c0 c1 c2) "Hc0" "Hc1" "Hc2".
     wp_smart_apply (wp_fork with "[Hc1]").
     { iIntros "!>". wp_recv (x) as "_". wp_send with "[//]". done. }
     wp_smart_apply (wp_fork with "[Hc2]").
@@ -74,13 +66,13 @@ Definition roundtrip_ref_prog : val :=
 Section roundtrip_ref.
   Context `{!heapGS Σ, !chanG Σ}.
 
-  Definition iProto_roundtrip_ref : gmap nat (iProto Σ) :=
-    <[0 := (<(Send, 1) @ (l:loc) (x:Z)> MSG #l {{ (l ↦ #x)%I }} ;
-            <(Recv, 2)> MSG #() {{ l ↦ #(x+2) }} ; END)%proto]>
-   (<[1 := (<(Recv, 0) @ (l:loc) (x:Z)> MSG #l {{ (l ↦ #x)%I }} ;
-            <(Send, 2)> MSG #l {{ l ↦ #(x+1) }}; END)%proto]>
-   (<[2 := (<(Recv, 1) @ (l:loc) (x:Z)> MSG #l {{ (l ↦ #x)%I }} ;
-            <(Send, 0)> MSG #() {{ l ↦ #(x+1) }}; END)%proto]> ∅)).
+  Definition iProto_roundtrip_ref : list (iProto Σ) :=
+    [(<(Send, 1) @ (l:loc) (x:Z)> MSG #l {{ (l ↦ #x)%I }} ;
+            <(Recv, 2)> MSG #() {{ l ↦ #(x+2) }} ; END)%proto;
+     (<(Recv, 0) @ (l:loc) (x:Z)> MSG #l {{ (l ↦ #x)%I }} ;
+            <(Send, 2)> MSG #l {{ l ↦ #(x+1) }}; END)%proto;
+     (<(Recv, 1) @ (l:loc) (x:Z)> MSG #l {{ (l ↦ #x)%I }} ;
+            <(Send, 0)> MSG #() {{ l ↦ #(x+1) }}; END)%proto].
 
   Lemma iProto_roundtrip_ref_consistent :
     ⊢ iProto_consistent iProto_roundtrip_ref.
@@ -95,17 +87,8 @@ Section roundtrip_ref.
     {{{ True }}} roundtrip_ref_prog #() {{{ RET #42 ; True }}}.
   Proof using chanG0.
     iIntros (Φ) "_ HΦ". wp_lam.
-    wp_smart_apply (new_chan_spec 3 iProto_roundtrip_ref with "[]").
-    { lia. }
-    { set_solver. }
-    { iApply iProto_roundtrip_ref_consistent. }
-    iIntros (cs) "Hcs".
-    wp_smart_apply (get_chan_spec _ 0 with "Hcs"); [set_solver|].
-    iIntros (c0) "[Hc0 Hcs]".
-    wp_smart_apply (get_chan_spec _ 1 with "Hcs"); [set_solver|].
-    iIntros (c1) "[Hc1 Hcs]".
-    wp_smart_apply (get_chan_spec _ 2 with "Hcs"); [set_solver|].
-    iIntros (c2) "[Hc2 Hcs]".
+    wp_new_chan iProto_roundtrip_ref with iProto_roundtrip_ref_consistent
+      as (c0 c1 c2) "Hc0" "Hc1" "Hc2".
     wp_smart_apply (wp_fork with "[Hc1]").
     { iIntros "!>".
       wp_recv (l x) as "Hl". wp_load. wp_store. by wp_send with "[$Hl]". }
@@ -188,10 +171,10 @@ Section roundtrip_ref_rec.
                 (iProto_roundtrip_ref_rec3_aux iProto_roundtrip_ref_rec3).
   Proof. apply proto_unfold_eq, (fixpoint_unfold _). Qed.
 
-  Definition iProto_roundtrip_ref_rec : gmap nat (iProto Σ) :=
-    <[0 := iProto_roundtrip_ref_rec1]>
-   (<[1 := iProto_roundtrip_ref_rec2]>
-   (<[2 := iProto_roundtrip_ref_rec3]> ∅)).
+  Definition iProto_roundtrip_ref_rec : list (iProto Σ) :=
+    [iProto_roundtrip_ref_rec1;
+     iProto_roundtrip_ref_rec2;
+     iProto_roundtrip_ref_rec3].
 
   Lemma iProto_roundtrip_ref_rec_consistent :
     ⊢ iProto_consistent iProto_roundtrip_ref_rec.
@@ -210,7 +193,6 @@ Section roundtrip_ref_rec.
     iIntros "Hloc". iSplit; [done|].
     replace (x + 1 + 1)%Z with (x+2)%Z by lia. iFrame.
     rewrite -iProto_roundtrip_ref_rec2_unfold.
-    do 2 clean_map 0. do 2 clean_map 1. do 2 clean_map 2.
     iApply "IH".
   Qed.
 
@@ -218,17 +200,8 @@ Section roundtrip_ref_rec.
     {{{ True }}} roundtrip_ref_rec_prog #() {{{ RET #42 ; True }}}.
   Proof using chanG0.
     iIntros (Φ) "_ HΦ". wp_lam.
-    wp_smart_apply (new_chan_spec 3 iProto_roundtrip_ref_rec with "[]").
-    { lia. }
-    { set_solver. }
-    { iApply iProto_roundtrip_ref_rec_consistent. }
-    iIntros (cs) "Hcs".
-    wp_smart_apply (get_chan_spec _ 0 with "Hcs"); [set_solver|].
-    iIntros (c0) "[Hc0 Hcs]".
-    wp_smart_apply (get_chan_spec _ 1 with "Hcs"); [set_solver|].
-    iIntros (c1) "[Hc1 Hcs]".
-    wp_smart_apply (get_chan_spec _ 2 with "Hcs"); [set_solver|].
-    iIntros (c2) "[Hc2 Hcs]".
+    wp_new_chan iProto_roundtrip_ref_rec with iProto_roundtrip_ref_rec_consistent
+      as (c0 c1 c2) "Hc0" "Hc1" "Hc2".
     wp_smart_apply (wp_fork with "[Hc1]").
     { iIntros "!>". wp_pure _. iLöb as "IH".
       wp_recv (l x) as "Hl". wp_load. wp_store. wp_send with "[$Hl]".
@@ -258,13 +231,13 @@ Definition smuggle_ref_prog : val :=
 Section smuggle_ref.
   Context `{!heapGS Σ, !chanG Σ}.
 
-  Definition iProto_smuggle_ref : gmap nat (iProto Σ) :=
-    <[0 := (<(Send, 1) @ (l:loc) (x:Z)> MSG #l {{ l ↦ #x }} ;
-            <(Recv,1)> MSG #() {{ l ↦ #(x+2) }} ; END)%proto]>
-   (<[1 := (<(Recv, 0) @ (v:val)> MSG v ; <(Send,2)> MSG v ;
-            <(Recv, 2)> MSG #(); <(Send,0)> MSG #() ; END)%proto]>
-   (<[2 := (<(Recv, 1) @ (l:loc) (x:Z)> MSG #l {{ l ↦ #x }} ;
-            <(Send,1)> MSG #() {{ l ↦ #(x+2) }} ; END)%proto]> ∅)).
+  Definition iProto_smuggle_ref : list (iProto Σ) :=
+    [(<(Send, 1) @ (l:loc) (x:Z)> MSG #l {{ l ↦ #x }} ;
+            <(Recv,1)> MSG #() {{ l ↦ #(x+2) }} ; END)%proto;
+     (<(Recv, 0) @ (v:val)> MSG v ; <(Send,2)> MSG v ;
+            <(Recv, 2)> MSG #(); <(Send,0)> MSG #() ; END)%proto;
+     (<(Recv, 1) @ (l:loc) (x:Z)> MSG #l {{ l ↦ #x }} ;
+            <(Send,1)> MSG #() {{ l ↦ #(x+2) }} ; END)%proto].
 
   Lemma iProto_smuggle_ref_consistent :
     ⊢ iProto_consistent iProto_smuggle_ref.
@@ -274,18 +247,8 @@ Section smuggle_ref.
     {{{ True }}} smuggle_ref_prog #() {{{ RET #42 ; True }}}.
   Proof using chanG0 heapGS0 Σ.
     iIntros (Φ) "_ HΦ". wp_lam.
-    wp_smart_apply (new_chan_spec 3 iProto_smuggle_ref with "[]").
-    { lia. }
-    { set_solver. }
-    { iApply iProto_smuggle_ref_consistent. }
-    iIntros (cs) "Hcs".
-    wp_pures.
-    wp_smart_apply (get_chan_spec _ 0 with "Hcs"); [set_solver|].
-    iIntros (c0) "[Hc0 Hcs]".
-    wp_smart_apply (get_chan_spec _ 1 with "Hcs"); [set_solver|].
-    iIntros (c1) "[Hc1 Hcs]".
-    wp_smart_apply (get_chan_spec _ 2 with "Hcs"); [set_solver|].
-    iIntros (c2) "[Hc2 Hcs]".
+    wp_new_chan iProto_smuggle_ref with iProto_smuggle_ref_consistent
+      as (c0 c1 c2) "Hc0" "Hc1" "Hc2".
     wp_smart_apply (wp_fork with "[Hc1]").
     { iIntros "!>". wp_recv (v) as "_". wp_send with "[//]".
       wp_recv as "_". by wp_send with "[//]". }
@@ -311,149 +274,57 @@ Section parallel.
          0
    *)
 
-  Definition iProto_parallel : gmap nat (iProto Σ) :=
-    <[0 := (<(Send, 1) @ (x1:Z)> MSG #x1 ;
+  Definition iProto_parallel : list (iProto Σ) :=
+    [(<(Send, 1) @ (x1:Z)> MSG #x1 ;
             <(Send, 2) @ (x2:Z)> MSG #x2 ;
             <(Recv, 3) @ (y1:Z)> MSG #(x1+y1);
-            <(Recv, 4) @ (y2:Z)> MSG #(x2+y2); END)%proto]>
-   (<[1 := (<(Recv, 0) @ (x:Z)> MSG #x ;
-            <(Send, 3) @ (y:Z)> MSG #(x+y); END)%proto]>
-   (<[2 := (<(Recv, 0) @ (x:Z)> MSG #x ;
-            <(Send, 4) @ (y:Z)> MSG #(x+y) ; END)%proto]>
-   (<[3 := (<(Recv, 1) @ (x:Z)> MSG #x ;
-            <(Send, 0)> MSG #x; END)%proto]>
-   (<[4 := (<(Recv, 2) @ (x:Z)> MSG #x ;
-            <(Send, 0)> MSG #x ; END)%proto]> ∅)))).
+            <(Recv, 4) @ (y2:Z)> MSG #(x2+y2); END)%proto;
+     (<(Recv, 0) @ (x:Z)> MSG #x ;
+            <(Send, 3) @ (y:Z)> MSG #(x+y); END)%proto;
+     (<(Recv, 0) @ (x:Z)> MSG #x ;
+            <(Send, 4) @ (y:Z)> MSG #(x+y) ; END)%proto;
+     (<(Recv, 1) @ (x:Z)> MSG #x ;
+            <(Send, 0)> MSG #x; END)%proto;
+     (<(Recv, 2) @ (x:Z)> MSG #x ;
+            <(Send, 0)> MSG #x ; END)%proto].
 
   Lemma iProto_parallel_consistent :
     ⊢ iProto_consistent iProto_parallel.
-  Proof.
-    rewrite /iProto_parallel.
-    iProto_consistent_take_step.
-    iIntros (x1) "_". iExists _. iSplit; [done|]. iSplit; [done|].
-    clean_map 0. clean_map 1.
-    iProto_consistent_take_step.
-    - iIntros (x2) "_". iExists _. iSplit; [done|]. iSplit; [done|].
-      clean_map 0. clean_map 2.
-      iProto_consistent_take_step.
-      + iIntros (y1) "_". iExists _. iSplit; [done|]. iSplit; [done|].
-        clean_map 1. clean_map 3.
-        iProto_consistent_take_step.
-        * iIntros (y2) "_". iExists _. iSplit; [done|]. iSplit; [done|].
-          clean_map 2. clean_map 4.
-          iProto_consistent_take_step.
-          iIntros "_". iExists _. iSplit; [done|]. iSplit; [done|].
-          clean_map 0. clean_map 3.
-          iProto_consistent_take_step.
-          iIntros "_". iExists _. iSplit; [done|]. iSplit; [done|].
-          iProto_consistent_take_step.
-        * iIntros "_". iExists _. iSplit; [done|]. iSplit; [done|].
-          clean_map 3. clean_map 0.
-          iProto_consistent_take_step.
-          iIntros (y2) "_". iExists _. iSplit; [done|]. iSplit; [done|].
-          clean_map 2. clean_map 4.
-          iProto_consistent_take_step.
-          iIntros "_". iExists _. iSplit; [done|]. iSplit; [done|].
-          clean_map 4. clean_map 0.
-          iProto_consistent_take_step.
-      + iIntros (y1) "_". iExists _. iSplit; [done|]. iSplit; [done|].
-        clean_map 2. clean_map 4.
-        iProto_consistent_take_step.
-        iIntros (y2) "_". iExists _. iSplit; [done|]. iSplit; [done|].
-        clean_map 1. clean_map 3.
-        iProto_consistent_take_step.
-        iIntros "_". iExists _. iSplit; [done|]. iSplit; [done|].
-        clean_map 3. clean_map 0.
-        iProto_consistent_take_step.
-        iIntros "_". iExists _. iSplit; [done|]. iSplit; [done|].
-        clean_map 4. clean_map 0.
-        iProto_consistent_take_step.
-    - iIntros (y1) "_". iExists _. iSplit; [done|]. iSplit; [done|].
-      clean_map 1. clean_map 3.
-      iProto_consistent_take_step.
-      iIntros (x2) "_". iExists _. iSplit; [done|]. iSplit; [done|].
-      clean_map 0. clean_map 2.
-      iProto_consistent_take_step.
-      + iIntros (y2) "_". iExists _. iSplit; [done|]. iSplit; [done|].
-        clean_map 2. clean_map 4.
-        iProto_consistent_take_step.
-        iIntros "_". iExists _. iSplit; [done|]. iSplit; [done|].
-        clean_map 3. clean_map 0.
-        iProto_consistent_take_step.
-        iIntros "_". iExists _. iSplit; [done|]. iSplit; [done|].
-        clean_map 4. clean_map 0.
-        iProto_consistent_take_step.
-      + iIntros "_". iExists _. iSplit; [done|]. iSplit; [done|].
-        clean_map 3. clean_map 0.
-        iProto_consistent_take_step.
-        iIntros (z) "_". iExists _. iSplit; [done|]. iSplit; [done|].
-        clean_map 2. clean_map 4.
-        iProto_consistent_take_step.
-        iIntros "_". iExists _. iSplit; [done|]. iSplit; [done|].
-        clean_map 4. clean_map 0.
-        iProto_consistent_take_step.
-  Qed.
+  Proof. rewrite /iProto_parallel. iProto_consistent_take_steps. Qed.
 
 End parallel.
 
 Section two_buyer.
   Context `{!heapGS Σ}.
 
-  Definition two_buyer_prot : gmap nat (iProto Σ) :=
-    <[0 := (<(Send, 1) @ (title:Z)> MSG #title ;
+  Definition two_buyer_prot : list (iProto Σ) :=
+    [(<(Send, 1) @ (title:Z)> MSG #title ;
             <(Recv, 1) @ (quote:Z)> MSG #quote ;
-            <(Send, 2) @ (contrib:Z)> MSG #contrib ; END)%proto]>
-   (<[1 := (<(Recv, 0) @ (title:Z)> MSG #title ;
+            <(Send, 2) @ (contrib:Z)> MSG #contrib ; END)%proto;
+     (<(Recv, 0) @ (title:Z)> MSG #title ;
             <(Send, 0) @ (quote:Z)> MSG #quote ;
             <(Send, 2)> MSG #quote ;
             <(Recv, 2) @ (b:bool)> MSG #b ;
             if b then
               <(Recv, 2) @ (address:Z)> MSG #address ;
               <(Send, 2) @ (date:Z)> MSG #date ; END
-            else END)%proto]>
-   (<[2 := (<(Recv, 1) @ (quote:Z)> MSG #quote ;
+            else END)%proto;
+     (<(Recv, 1) @ (quote:Z)> MSG #quote ;
             <(Recv, 0) @ (contrib:Z)> MSG #contrib ;
             if bool_decide (contrib >= quote/2)%Z then
               <(Send, 1)> MSG #true ;
               <(Send, 1) @ (address:Z)> MSG #address ;
               <(Recv, 1) @ (date:Z)> MSG #date ; END
             else
-              <(Send, 1)> MSG #false ; END)%proto]>
-      ∅)).
+              <(Send, 1)> MSG #false ; END)%proto].
 
   Lemma two_buyer_prot_consistent :
     ⊢ iProto_consistent two_buyer_prot.
   Proof.
-    rewrite /two_buyer_prot.
-    iProto_consistent_take_step.
-    iIntros (title) "_". iExists _. iSplit; [done|]. iSplit; [done|].
-    clean_map 0. clean_map 1.
-    iProto_consistent_take_step.
-    iIntros (quote) "_". iExists _. iSplit; [done|]. iSplit; [done|].
-    clean_map 0. clean_map 1.
-    iProto_consistent_take_step.
-    iIntros "_". iExists _. iSplit; [done|]. iSplit; [done|].
-    clean_map 1. clean_map 2.
-    iProto_consistent_take_step.
-    iIntros (contrib) "_". iExists _. iSplit; [done|]. iSplit; [done|].
-    clean_map 0. clean_map 2.
-    case_bool_decide.
-    - iProto_consistent_take_step.
-      iIntros "_". iExists _. iSplit; [done|]. iSplit; [done|].
-      clean_map 2. clean_map 1.
-      iProto_consistent_take_step.
-      iIntros (address) "_". iExists _. iSplit; [done|]. iSplit; [done|].
-      clean_map 2. clean_map 1.
-      iProto_consistent_take_step.
-      iIntros (date) "_". iExists _. iSplit; [done|]. iSplit; [done|].
-      clean_map 2. clean_map 1.
-      iProto_consistent_take_step.
-    - iProto_consistent_take_step.
-      iIntros "_". iExists _. iSplit; [done|]. iSplit; [done|].
-      clean_map 2. clean_map 1.
-      iProto_consistent_take_step.
+    rewrite /two_buyer_prot. iProto_consistent_take_steps. 
+    case_bool_decide; iProto_consistent_take_steps.
   Qed.
-
+  
 End two_buyer.
 
 Section two_buyer_ref.
@@ -493,47 +364,21 @@ Section two_buyer_ref.
        END
      else END)%proto.
 
-  Definition two_buyer_ref_prot : gmap nat (iProto Σ) :=
-    <[0 := two_buyer_ref_b1_prot]>
-   (<[1 := two_buyer_ref_s_prot]>
-   (<[2 := two_buyer_ref_b2_prot]>
-      ∅)).
+  Definition two_buyer_ref_prot : list (iProto Σ) :=
+    [two_buyer_ref_b1_prot;
+     two_buyer_ref_s_prot;
+     two_buyer_ref_b2_prot].
 
+  (* TODO: Anonymous variable in this is unsatisfactory *)
   Lemma two_buyer_ref_prot_consistent :
     ⊢ iProto_consistent two_buyer_ref_prot.
   Proof.
-    rewrite /two_buyer_prot.
-    iProto_consistent_take_step.
-    iIntros (title) "_". iExists _. iSplit; [done|]. iSplit; [done|].
-    clean_map 0. clean_map 1.
-    iProto_consistent_take_step.
-    iIntros (quote) "_". iExists _. iSplit; [done|]. iSplit; [done|].
-    clean_map 0. clean_map 1.
-    iProto_consistent_take_step.
-    iIntros "_". iExists _. iSplit; [done|]. iSplit; [done|].
-    clean_map 1. clean_map 2.
-    iProto_consistent_take_step.
-    iIntros (l1 amount1 contrib) "Hl1". iExists _,_,_. iSplit; [done|]. iFrame.
-    clean_map 0. clean_map 2.
-    iProto_consistent_take_step.
-    iIntros (b) "Hl1". iExists _. iSplit; [done|]. iFrame.
-    clean_map 0. clean_map 2.
-    iProto_consistent_take_step.
-    iIntros "_". iExists _. iSplit; [done|]. iSplit; [done|].
-    clean_map 1. clean_map 2.
-    destruct b.
-    - iProto_consistent_take_step.
-      iIntros (l2 amount2 address) "Hl2". iExists _,_,_. iSplit; [done|]. iFrame.
-      clean_map 2. clean_map 1.
-      iProto_consistent_take_step.
-      iIntros (date) "Hl2". iExists _. iSplit; [done|]. iFrame.
-      iProto_consistent_take_step.
-    - iProto_consistent_take_step.
+    rewrite /two_buyer_prot. iProto_consistent_take_steps.
+    iNext. destruct x4; iProto_consistent_take_steps.
   Qed.
 
 End two_buyer_ref.
 
-
 Section forwarder.
   Context `{!heapGS Σ}.
 
@@ -548,36 +393,25 @@ Section forwarder.
 
    *)
 
-  Definition iProto_forwarder : gmap nat (iProto Σ) :=
-    <[0 := (<(Send, 1) @ (x:Z)> MSG #x ;
+  Definition iProto_forwarder : list (iProto Σ) :=
+    [(<(Send, 1) @ (x:Z)> MSG #x ;
             <(Send, 1) @ (b:bool)> MSG #b ;
-            <(Recv, if b then 2 else 3) > MSG #x ; END)%proto]>
-   (<[1 := (<(Recv, 0) @ (x:Z)> MSG #x ;
+            <(Recv, if b then 2 else 3) > MSG #x ; END)%proto;
+     (<(Recv, 0) @ (x:Z)> MSG #x ;
             <(Recv, 0) @ (b:bool)> MSG #b;
-            <(Send, if b then 2 else 3)> MSG #x ; END)%proto]>
-   (<[2 := (<(Recv, 1) @ (x:Z)> MSG #x ;
-            <(Send, 0)> MSG #x ; END)%proto]>
-   (<[3 := (<(Recv, 1) @ (x:Z)> MSG #x ;
-            <(Send, 0)> MSG #x ; END)%proto]> ∅))).
+            <(Send, if b then 2 else 3)> MSG #x ; END)%proto;
+     (<(Recv, 1) @ (x:Z)> MSG #x ;
+            <(Send, 0)> MSG #x ; END)%proto;
+     (<(Recv, 1) @ (x:Z)> MSG #x ;
+            <(Send, 0)> MSG #x ; END)%proto].
 
+  (* TODO: Anonymous variable in this is unsatisfactory *)
   Lemma iProto_forwarder_consistent :
     ⊢ iProto_consistent iProto_forwarder.
   Proof.
     rewrite /iProto_forwarder.
-    iProto_consistent_take_step.
-    iIntros (x) "_". iExists _. iSplit; [done|]. iSplit; [done|].
-    iProto_consistent_take_step.
-    iIntros ([]) "_".
-    - iExists _. iSplit; [done|]. iSplit; [done|].
-      iProto_consistent_take_step.
-      iIntros "_". iExists _. iSplit; [done|]. iSplit; [done|].
-      repeat clean_map 0. repeat clean_map 1.
-      iProto_consistent_take_steps.
-    - iExists _. iSplit; [done|]. iSplit; [done|].
-      iProto_consistent_take_step.
-      iIntros "_". iExists _. iSplit; [done|]. iSplit; [done|].
-      repeat clean_map 0. repeat clean_map 1.
-      iProto_consistent_take_steps.
+    iProto_consistent_take_steps.
+    destruct x0; iProto_consistent_take_steps.
   Qed.
 
 End forwarder.
@@ -637,11 +471,11 @@ Section forwarder_rec.
                 (iProto_forwarder_rec_n_aux iProto_forwarder_rec_n).
   Proof. apply (fixpoint_unfold _). Qed.
 
-  Definition iProto_forwarder_rec : gmap nat (iProto Σ) :=
-    <[0 := iProto_forwarder_rec_0]>
-   (<[1 := iProto_forwarder_rec_1]>
-   (<[2 := iProto_forwarder_rec_n]>
-   (<[3 := iProto_forwarder_rec_n]>∅))).
+  Definition iProto_forwarder_rec : list (iProto Σ) :=
+    [iProto_forwarder_rec_0;
+     iProto_forwarder_rec_1;
+     iProto_forwarder_rec_n;
+     iProto_forwarder_rec_n].
 
   Lemma iProto_forwarder_rec_consistent :
     ⊢ iProto_consistent iProto_forwarder_rec.
@@ -658,14 +492,11 @@ Section forwarder_rec.
     - iExists _. iSplit; [done|]. iSplit; [done|].
       iProto_consistent_take_step.
       iIntros "_". iExists _. iSplit; [done|]. iSplit; [done|].
-      repeat clean_map 1. repeat clean_map 2. repeat clean_map 0. 
       iNext.
       iEval (rewrite iProto_forwarder_rec_1_unfold).
       iEval (rewrite iProto_forwarder_rec_n_unfold).
       iProto_consistent_take_step.
       iIntros "_". iSplit; [done|]. iSplit; [done|].
-      repeat clean_map 1. repeat clean_map 2. repeat clean_map 0. 
-      rewrite (insert_commute _ 2 1); [|done].
       iEval (rewrite -iProto_forwarder_rec_1_unfold).
       iEval (rewrite -iProto_forwarder_rec_n_unfold).
       iEval (rewrite -iProto_forwarder_rec_n_unfold).
@@ -673,14 +504,11 @@ Section forwarder_rec.
     - iExists _. iSplit; [done|]. iSplit; [done|].
       iProto_consistent_take_step.
       iIntros "_". iExists _. iSplit; [done|]. iSplit; [done|].
-      repeat clean_map 1. repeat clean_map 2. repeat clean_map 0. 
       iNext.
       iEval (rewrite iProto_forwarder_rec_1_unfold).
       iEval (rewrite iProto_forwarder_rec_n_unfold).
       iProto_consistent_take_step.
       iIntros "_". iSplit; [done|]. iSplit; [done|].
-      repeat clean_map 1. repeat clean_map 3. repeat clean_map 0. 
-      rewrite (insert_commute _ 3 1); [|done].
       iEval (rewrite -iProto_forwarder_rec_1_unfold).
       iEval (rewrite -iProto_forwarder_rec_n_unfold).
       iEval (rewrite -iProto_forwarder_rec_n_unfold).
diff --git a/multi_actris/examples/leader_election.v b/multi_actris/examples/leader_election.v
index cb21e823d4d481ec63a2fefc575d85f1000644e5..e8810fc0605da0134e8550368a32012fd103daf3 100644
--- a/multi_actris/examples/leader_election.v
+++ b/multi_actris/examples/leader_election.v
@@ -133,98 +133,49 @@ Section ring_leader_election_example.
   Definition prot_tail (i_max : nat) : iProto Σ :=
     (<(Send,0)> MSG #i_max; END)%proto.
 
-  Definition pre_prot_pool id_max : gmap nat (iProto Σ) :=
-     <[0 := (<(Recv,1) @ (id_max : nat)> MSG #id_max ;
+  Definition pre_prot_pool id_max : list (iProto Σ) :=
+     [(<(Recv,1) @ (id_max : nat)> MSG #id_max ;
              <(Recv,2)> MSG #id_max ;
              <(Recv,3)> MSG #id_max ;
-             END)%proto ]>
-    (<[1 := prot_tail id_max ]>
-    (<[2 := prot_tail id_max ]>
-    (<[3 := prot_tail id_max ]> ∅))).
+             END)%proto;
+      prot_tail id_max;
+      prot_tail id_max;
+      prot_tail id_max].
 
   Lemma pre_prot_pool_consistent id_max :
     ⊢ iProto_consistent (pre_prot_pool id_max).
   Proof. rewrite /pre_prot_pool. iProto_consistent_take_steps. Qed.
 
-  Definition prot_pool : gmap nat (iProto Σ) :=
-     <[0 := (<(Recv,1) @ (id_max : nat)> MSG #id_max ;
+  Definition prot_pool : list (iProto Σ) :=
+     [(<(Recv,1) @ (id_max : nat)> MSG #id_max ;
              <(Recv,2)> MSG #id_max ;
              <(Recv,3)> MSG #id_max ;
-             END)%proto ]>
-    (<[1 := rle_preprot 3 1 2 prot_tail ]>
-    (<[2 := rle_prot 1 2 3 prot_tail false ]>
-    (<[3 := rle_preprot 2 3 1 prot_tail ]> ∅))).
+             END)%proto;
+      rle_preprot 3 1 2 prot_tail;
+      rle_prot 1 2 3 prot_tail false;
+      rle_preprot 2 3 1 prot_tail].
 
   Lemma prot_pool_consistent : ⊢ iProto_consistent prot_pool.
   Proof.
     rewrite /prot_pool /rle_preprot.
     rewrite !rle_prot_unfold'.
-    iProto_consistent_take_step.
-    iProto_consistent_resolve_step.
-    iProto_consistent_take_step.
-    iProto_consistent_resolve_step.
-    repeat clean_map 0. repeat clean_map 1.
-    repeat clean_map 2. repeat clean_map 3.
-    iProto_consistent_take_step.
-    iProto_consistent_resolve_step.
-    iProto_consistent_take_step.
-    iProto_consistent_resolve_step.
-    repeat clean_map 0. repeat clean_map 1.
-    repeat clean_map 2. repeat clean_map 3.
+    iProto_consistent_take_steps.
+    case_bool_decide; try lia.
+    case_bool_decide; try lia.
     rewrite !rle_prot_unfold'.
-    iProto_consistent_take_step.
-    iProto_consistent_resolve_step.
-    iProto_consistent_take_step.
-    iProto_consistent_resolve_step.
-    repeat clean_map 0. repeat clean_map 1.
-    repeat clean_map 2. repeat clean_map 3.
-    iProto_consistent_take_step.
-    iProto_consistent_resolve_step.
-    iProto_consistent_take_step.
-    iProto_consistent_resolve_step.
-    repeat clean_map 0. repeat clean_map 1.
-    repeat clean_map 2. repeat clean_map 3.
+    iProto_consistent_take_steps.
+    case_bool_decide; try lia.
+    case_bool_decide; try lia.
     rewrite !rle_prot_unfold'.
-    iProto_consistent_take_step.
-    iProto_consistent_resolve_step.
-    iProto_consistent_take_step.
-    iProto_consistent_resolve_step.
-    repeat clean_map 0. repeat clean_map 1.
-    repeat clean_map 2. repeat clean_map 3.
-    iProto_consistent_take_step.
-    iProto_consistent_resolve_step.
-    iProto_consistent_take_step.
-    iProto_consistent_resolve_step.
-    repeat clean_map 0. repeat clean_map 1.
-    repeat clean_map 2. repeat clean_map 3.
-    iProto_consistent_take_step.
-    iProto_consistent_resolve_step.
-    iProto_consistent_take_step.
-    iProto_consistent_resolve_step.
-    repeat clean_map 0. repeat clean_map 1.
-    repeat clean_map 2. repeat clean_map 3.
-    repeat (rewrite (insert_commute _ _ 3); [|lia]).
-    repeat (rewrite (insert_commute _ _ 2); [|lia]).
-    repeat (rewrite (insert_commute _ _ 1); [|lia]).
-    repeat (rewrite (insert_commute _ _ 0); [|lia]).
-    iApply pre_prot_pool_consistent.
+    iProto_consistent_take_steps.
   Qed.
 
   Lemma program_spec :
     {{{ True }}} program #() {{{ RET #(); True }}}.
   Proof. 
     iIntros (Φ) "_ HΦ". wp_lam.
-    wp_smart_apply (new_chan_spec 4 prot_pool);
-      [lia|set_solver|iApply prot_pool_consistent|].
-    iIntros (cs) "Hcs".
-    wp_smart_apply (get_chan_spec _ 0 with "Hcs"); [done|].
-    iIntros (c0) "[Hc0 Hcs]".
-    wp_smart_apply (get_chan_spec _ 1 with "Hcs"); [done|].
-    iIntros (c1) "[Hc1 Hcs]".
-    wp_smart_apply (get_chan_spec _ 2 with "Hcs"); [done|].
-    iIntros (c2) "[Hc2 Hcs]".
-    wp_smart_apply (get_chan_spec _ 3 with "Hcs"); [done|].
-    iIntros (c3) "[Hc3 Hcs]".
+    wp_new_chan prot_pool with prot_pool_consistent
+      as (c0 c1 c2 c3) "Hc0" "Hc1" "Hc2" "Hc3".
     wp_smart_apply (wp_fork with "[Hc1]").
     { iIntros "!>". wp_smart_apply (init_spec with "Hc1").
       iIntros (i') "Hc1". by wp_send with "[//]". }