|
| 1 | + |
| 2 | +-- The formal Agda specification with all the fancy types. |
| 3 | + |
| 4 | +module FormalSpec where |
| 5 | + |
| 6 | +open import Data.Nat |
| 7 | +open import Data.Nat.Properties |
| 8 | +open import Data.List.Base |
| 9 | +open import Data.Product.Base |
| 10 | +open import Relation.Binary.PropositionalEquality |
| 11 | +open import Relation.Nullary |
| 12 | +open import Function |
| 13 | + |
| 14 | +open import CommonTypes |
| 15 | + |
| 16 | +-- Super simple protocol: |
| 17 | +-- - The hosts take turns round robin to produce blocks. |
| 18 | +-- - `blockIndex` is incremented with each block on the chain. |
| 19 | +-- - If a node misses its window the other node should produce the missed block in its slot |
| 20 | +-- instead. |
| 21 | +-- Network model: π-calculus style instant delivery |
| 22 | + |
| 23 | +record LocalState : Set where |
| 24 | + constructor _,_ |
| 25 | + field |
| 26 | + lastBlock : Block |
| 27 | + lastBlockSlot : Slot |
| 28 | + |
| 29 | +open LocalState public |
| 30 | + |
| 31 | +record State : Set where |
| 32 | + constructor ⟦_,_,_⟧ |
| 33 | + field |
| 34 | + clock : Slot |
| 35 | + aliceState : LocalState |
| 36 | + bobState : LocalState |
| 37 | + |
| 38 | +open State public |
| 39 | + |
| 40 | +initLocalState : LocalState |
| 41 | +initLocalState = Blk 0 , 0 |
| 42 | + |
| 43 | +initState : State |
| 44 | +initState = ⟦ 0 , initLocalState , initLocalState ⟧ |
| 45 | + |
| 46 | +-- State manipulation |
| 47 | + |
| 48 | +getLocalState : Party → State → LocalState |
| 49 | +getLocalState Alice = aliceState |
| 50 | +getLocalState Bob = bobState |
| 51 | + |
| 52 | +modifyLocalState : Party → (LocalState → LocalState) → State → State |
| 53 | +modifyLocalState Alice f ⟦ t , as , bs ⟧ = ⟦ t , f as , bs ⟧ |
| 54 | +modifyLocalState Bob f ⟦ t , as , bs ⟧ = ⟦ t , as , f bs ⟧ |
| 55 | + |
| 56 | +setLocalState : Party → LocalState → State → State |
| 57 | +setLocalState p ls = modifyLocalState p λ _ → ls |
| 58 | + |
| 59 | +-- Honesty models. Encodes who is allowed to perform adversarial actions. |
| 60 | + |
| 61 | +data Honesty : Set where |
| 62 | + happyPath : Honesty |
| 63 | + badAlice : Honesty |
| 64 | + badBob : Honesty |
| 65 | + |
| 66 | +-- Certificate that the given party is allowed to do bad things. |
| 67 | +data Dishonest : Honesty → Party → Set where |
| 68 | + badAlice : Dishonest badAlice Alice |
| 69 | + badBob : Dishonest badBob Bob |
| 70 | + |
| 71 | +variable |
| 72 | + s s₀ s₁ s₂ s₃ s₄ : State |
| 73 | + ls ls₀ ls₁ : LocalState |
| 74 | + p q : Party |
| 75 | + b b₁ : Block |
| 76 | + i j : BlockIndex |
| 77 | + t t₁ now : Slot |
| 78 | + h : Honesty |
| 79 | + |
| 80 | +-- Alice has even slots, Bob has odd slots. |
| 81 | +data SlotOf (t : Slot) : Party → Set where |
| 82 | + AliceSlot : t % 2 ≡ 0 → SlotOf t Alice |
| 83 | + BobSlot : t % 2 ≡ 1 → SlotOf t Bob |
| 84 | + |
| 85 | +-- `ValidBlock now ls p b`: At time `now` is it valid for a party `p` |
| 86 | +-- to send a block `b` from the point of view of a node with local |
| 87 | +-- state `ls`. |
| 88 | +data ValidBlock : Slot → LocalState → Party → Block → Set where |
| 89 | + valid : t < now |
| 90 | + → SlotOf now p |
| 91 | + → ValidBlock now (Blk i , t) p (Blk (suc i)) |
| 92 | + |
| 93 | +-- Local state update on receive |
| 94 | +data _⊢_[_,_]?_ : Slot → LocalState → Party → Block → LocalState → Set where |
| 95 | + correctBlock : ValidBlock now ls p b |
| 96 | + → now ⊢ ls [ p , b ]? (b , now) |
| 97 | + wrongBlock : ¬ ValidBlock now ls p b |
| 98 | + → now ⊢ ls [ p , b ]? ls |
| 99 | + |
| 100 | +-- Global state update on receive |
| 101 | +data _[_,_↦_]?_ : State → Party → Block → Party → State → Set where |
| 102 | + receive : clock s ⊢ getLocalState q s [ p , b ]? ls → |
| 103 | + s [ p , b ↦ q ]? setLocalState q ls s |
| 104 | + |
| 105 | +-- Local state update on send |
| 106 | +data _,_,_⊢_[_↦_]!_ : Honesty → Slot → Party → LocalState → Block → Party → LocalState → Set where |
| 107 | + correctBlock : ValidBlock now ls p b |
| 108 | + → h , now , p ⊢ ls [ b ↦ q ]! (b , now) |
| 109 | + dishonestBlock : Dishonest h p |
| 110 | + → h , now , p ⊢ ls [ b ↦ q ]! ls |
| 111 | + |
| 112 | +-- Global state update on send |
| 113 | +data _⊢_[_,_↦_]!_ : Honesty → State → Party → Block → Party → State → Set where |
| 114 | + send : ∀ q b → h , clock s , p ⊢ getLocalState p s [ b ↦ q ]! ls |
| 115 | + → p ≢ q |
| 116 | + → h ⊢ s [ p , b ↦ q ]! setLocalState p ls s |
| 117 | + |
| 118 | +-- The top-level small-step semantics |
| 119 | +data _⊢_↝_ : Honesty → State → State → Set where |
| 120 | + |
| 121 | + -- π-calculus-style instant message delivery |
| 122 | + deliver : h ⊢ s₀ [ p , b ↦ q ]! s₁ |
| 123 | + → s₁ [ p , b ↦ q ]? s₂ |
| 124 | + → h ⊢ s₀ ↝ s₂ |
| 125 | + |
| 126 | + -- We can only tick if the party whose slot it is has produced (or |
| 127 | + -- pretends to have produced) their block. |
| 128 | + tick : SlotOf (clock s) p |
| 129 | + → lastBlockSlot (getLocalState p s) ≡ clock s |
| 130 | + → h ⊢ s ↝ record s { clock = suc (clock s) } |
| 131 | + |
| 132 | + -- A dishonest party can update their local state to whatever they want. |
| 133 | + trickery : Dishonest h p → ∀ ls → h ⊢ s ↝ setLocalState p ls s |
| 134 | + |
| 135 | +-- Standard reflexive-transitive closure. |
| 136 | +data _⊢_↝*_ : Honesty → State → State → Set where |
| 137 | + [] : h ⊢ s ↝* s |
| 138 | + _∷_ : h ⊢ s₀ ↝ s₁ → h ⊢ s₁ ↝* s₂ → h ⊢ s₀ ↝* s₂ |
| 139 | + |
| 140 | +infixr 5 _<>_ |
| 141 | +_<>_ : h ⊢ s₀ ↝* s₁ → h ⊢ s₁ ↝* s₂ → h ⊢ s₀ ↝* s₂ |
| 142 | +[] <> tr = tr |
| 143 | +(r ∷ tr) <> tr₁ = r ∷ tr <> tr₁ |
| 144 | + |
| 145 | +-- Which blocks does Alice produce in a given trace? |
| 146 | +aliceBlocks : h ⊢ s₀ ↝* s₁ → List (Slot × Block) |
| 147 | +aliceBlocks [] = [] |
| 148 | +aliceBlocks {s₀ = s₀} (deliver {p = Alice} {b} _ _ ∷ tr) = (clock s₀ , b) ∷ aliceBlocks tr |
| 149 | +aliceBlocks (deliver _ _ ∷ tr) = aliceBlocks tr |
| 150 | +aliceBlocks (trickery _ _ ∷ tr) = aliceBlocks tr |
| 151 | +aliceBlocks (tick _ _ ∷ tr) = aliceBlocks tr |
| 152 | + |
| 153 | +-- Some proofs |
| 154 | + |
| 155 | +liftHonesty : happyPath ⊢ s ↝ s₁ → h ⊢ s ↝ s₁ |
| 156 | +liftHonesty (deliver (send q b (correctBlock v) !q) r) = deliver (send q b (correctBlock v) !q) r |
| 157 | +liftHonesty (tick sl sent) = tick sl sent |
| 158 | + |
| 159 | +liftHonesty* : happyPath ⊢ s ↝* s₁ → h ⊢ s ↝* s₁ |
| 160 | +liftHonesty* [] = [] |
| 161 | +liftHonesty* (r ∷ tr) = liftHonesty r ∷ liftHonesty* tr |
| 162 | + |
| 163 | +sameAliceBlocks : (tr : happyPath ⊢ s ↝* s₁) → aliceBlocks tr ≡ aliceBlocks (liftHonesty* {h = h} tr) |
| 164 | +sameAliceBlocks [] = refl |
| 165 | +sameAliceBlocks {h = h} (deliver {p = Alice} {b} (send _ _ (correctBlock _) _) _ ∷ tr) |
| 166 | + rewrite sameAliceBlocks {h = h} tr = refl |
| 167 | +sameAliceBlocks (deliver {p = Bob} (send _ _ (correctBlock _) _) _ ∷ tr) = sameAliceBlocks tr |
| 168 | +sameAliceBlocks (tick _ _ ∷ tr) = sameAliceBlocks tr |
| 169 | + |
| 170 | +appendAliceBlocks : (tr : h ⊢ s₀ ↝* s₁) (tr₁ : h ⊢ s₁ ↝* s₂) → aliceBlocks (tr <> tr₁) ≡ aliceBlocks tr ++ aliceBlocks tr₁ |
| 171 | +appendAliceBlocks [] tr₁ = refl |
| 172 | +appendAliceBlocks (deliver {p = Alice} _ _ ∷ tr) tr₁ rewrite appendAliceBlocks tr tr₁ = refl |
| 173 | +appendAliceBlocks (deliver {p = Bob } _ _ ∷ tr) tr₁ = appendAliceBlocks tr tr₁ |
| 174 | +appendAliceBlocks (trickery _ _ ∷ tr) tr₁ = appendAliceBlocks tr tr₁ |
| 175 | +appendAliceBlocks (tick _ _ ∷ tr) tr₁ = appendAliceBlocks tr tr₁ |
| 176 | + |
| 177 | +-- Examples |
| 178 | + |
| 179 | +_ : happyPath ⊢ initState ↝* ⟦ 2 , (Blk 2 , 2) , (Blk 2 , 2) ⟧ |
| 180 | +_ = tick (AliceSlot refl) refl |
| 181 | + ∷ deliver (send Alice (Blk 1) (correctBlock (valid ≤-refl (BobSlot refl))) λ()) |
| 182 | + (receive (correctBlock (valid ≤-refl (BobSlot refl)))) |
| 183 | + ∷ tick (BobSlot refl) refl |
| 184 | + ∷ deliver (send Bob (Blk 2) (correctBlock (valid ≤-refl (AliceSlot refl))) λ()) |
| 185 | + (receive (correctBlock (valid ≤-refl (AliceSlot refl)))) |
| 186 | + ∷ [] |
| 187 | + |
| 188 | +_ : badBob ⊢ initState ↝* ⟦ 2 , (Blk 1 , 2) , (Blk 1 , 2) ⟧ |
| 189 | +_ = tick (AliceSlot refl) refl |
| 190 | + ∷ trickery badBob (Blk 0 , 1) -- Bob pretends to have sent a message (bumping lastBlkSlot) |
| 191 | + ∷ tick (BobSlot refl) refl |
| 192 | + ∷ deliver (send Bob (Blk 1) (correctBlock (valid (s≤s z≤n) (AliceSlot refl))) λ()) |
| 193 | + (receive (correctBlock (valid ≤-refl (AliceSlot refl)))) |
| 194 | + ∷ [] |
0 commit comments