Theory Bank

theory Bank
  imports Solidity_Main
begin

section ‹Banking Contract›

subsection ‹Specification›

abbreviation "balances  STR ''balances''"
abbreviation "bal  STR ''bal''"

contract Bank
  for balances: "SType.TMap (SType.TValue TAddress) (SType.TValue TSint)"

constructor
where
  "⟨skip⟩"

cfunction deposit external payable
where
  "balances [⟨sender⟩] ::=s balances ~s [⟨sender⟩] ⟨+⟩ ⟨value⟩" ,

cfunction reset
where
  "balances [⟨sender⟩] ::=s ⟨sint⟩ 0" ,

cfunction withdraw external
where
  "do {
    bal :: TSint;
    bal [] ::= balances ~s [⟨sender⟩];
    icall reset;
    ⟨transfer⟩ ⟨sender⟩ (bal ~ [])
  }"

context bank
begin
  thm constructor_def
  thm deposit_def
  thm withdraw_def
end

subsection ‹Verifying an Invariant›

abbreviation
  "SUMM x 
    adUNIV. unat (valtype.uint (storage_data.vt (storage_data.mp (x balances) (Address ad))))"

context Solidity
begin

lemma sum_leq_value:
    fixes bal mp
  assumes "(adUNIV. unat (valtype.uint (storage_data.vt (mp (Address ad)))))  Balances s this"
      and "mp (Address msg_sender) = storage_data.Value (Uint y)"
      and "unat y + unat msg_value < 2^256"
    shows "(adUNIV. unat (valtype.uint (storage_data.vt
             (if ad = msg_sender then storage_data.Value (Uint (y + msg_value))
              else mp (Address ad)))))
            Balances s this + unat msg_value"
proof -
  from sum_addr[of _ msg_sender]
  have "(ad|ad  UNIV  ad  msg_sender.
    unat (valtype.uint (storage_data.vt (mp (Address ad))))) +
    unat (valtype.uint (storage_data.vt (mp (Address msg_sender)))) + unat msg_value
     Balances s this + unat msg_value"
  using assms(1) by simp
  moreover have "unat (valtype.uint (storage_data.vt (storage_data.Value (Uint (y + msg_value)))))
     unat (valtype.uint (storage_data.vt (mp (Address msg_sender)))) + unat msg_value"
    using assms(2,3) unat_add_lem[where ?'a =256] by simp
  ultimately show ?thesis using sum_addr[of _ msg_sender] by simp
qed

lemma sum_leq_this:
    fixes bal bal' mp
  assumes "(adUNIV. unat (valtype.uint (storage_data.vt (mp (Address ad)))))  Balances sa this"
      and "mp (Address this) = storage_data.Value (Uint y)"
      and "mp' (Address this) = storage_data.Value (Uint 0)"
      and "Balances s = Balances sa"
      and "x. x  this  mp' (Address x) = mp (Address x)"
    shows "(adUNIV. unat (valtype.uint (storage_data.vt (mp' (Address ad)))))  Balances sa this"
proof -
  from sum_addr[of _ this] have
    "(ad|ad  UNIV  ad  this. unat (valtype.uint (storage_data.vt (mp (Address ad))))) +
      (unat (valtype.uint (storage_data.vt (mp (Address this)))) - unat y)
     Balances sa this - unat y"
    using assms(1,2) by simp
  moreover have "unat (valtype.uint (storage_data.vt (storage_data.Value (Uint 0))))
     unat (valtype.uint (storage_data.vt (mp (Address msg_sender)))) + unat msg_value - unat y"
    using assms(2) unat_add_lem[where ?'a =256] by simp
  ultimately show ?thesis using assms sum_addr[of _ this] by auto
qed

lemma sum_leq_sender:
    fixes bal bal' mp
  assumes "(adUNIV. unat (valtype.uint (storage_data.vt (mp (Address ad)))))  Balances sa this"
      and "mp (Address msg_sender) = storage_data.Value (Uint y)"
      and "mp' (Address msg_sender) = storage_data.Value (Uint 0)"
      and "Balances s = Balances sa"
      and "x. x  msg_sender  mp' (Address x) = mp (Address x)"
    shows "(adUNIV. unat (valtype.uint (storage_data.vt (mp' (Address ad)))))  Balances sa this - unat y"
proof -
  from sum_addr[of _ msg_sender] have
    "(ad|ad  UNIV  ad  msg_sender. unat (valtype.uint (storage_data.vt (mp (Address ad))))) +
      (unat (valtype.uint (storage_data.vt (mp (Address msg_sender)))) - unat y)
     Balances sa this - unat y"
    using assms(1,2) by simp
  moreover have "unat (valtype.uint (storage_data.vt (storage_data.Value (Uint 0))))
     unat (valtype.uint (storage_data.vt (mp (Address msg_sender)))) + unat msg_value - unat y"
    using assms(2) unat_add_lem[where ?'a =256] by simp
  ultimately show ?thesis using assms sum_addr[of _ msg_sender] by auto
qed

lemma(in Solidity) bal_msg_sender:
  fixes bal
  assumes "x. y. bal x = storage_data.Value (Uint y)"
  obtains y where "bal (Address msg_sender) = storage_data.Value (Uint y)"
  using assms by auto

text ‹
  Now we can start verifying the invariant.
  To this end our package provides a keyword invariant which takes a property as parameter and generates proof obligations.
›
end

invariant sum_bal sb where
    "snd sb  SUMM (fst sb)"
  for "Bank"

abbreviation(in Solidity) reset_post where
"reset_post start_state return_value end_state 
  Balances start_state = Balances end_state 
  (mp. state.Storage start_state this balances = storage_data.Map mp 
  (y. si. mp y = storage_data.Value (Uint si))
   (mp'. state.Storage end_state this balances = storage_data.Map mp'
     mp' (Address msg_sender) = storage_data.Value (valtype.Uint 0)
     (x  msg_sender. mp' (Address x) = mp (Address x))
     (y. si. mp' y = storage_data.Value (Uint si))))"

abbreviation(in Solidity) true_pre where
"true_pre _   True"

abbreviation(in Solidity) true_post where
"true_post _ _ _   True"

declare(in bank) sum_balI[wprules del]

verification sum_bal:
  sum_bal
  deposit and
  withdraw and
  reset (true_pre, reset_post)
  for "Bank"
proof -
  show
    "call.
       (x h r. effect (call x) h r  vcond x h r)
         effect (constructor call) s r
         post s r (K (K (inv_state sum_bal))) (K True)"
    unfolding constructor_def
    apply (erule post_exc_true, erule_tac post_wp)
    unfolding  inv_state_def
    by (vcg wprules: sum_balI | auto)+

  show
    b: "call.
      effect (reset call) s r
         (x h r. effect (call x) h r  vcond x h r)
         True  (post s r reset_post (K True))"
    unfolding reset_def
    apply (erule post_exc_true, erule_tac post_wp)
    unfolding inv_state_def
    apply vcg
    by auto

  show
    c: "call.
      (x h r. effect (call x) h r  vcond x h r)
         effect (deposit call) s r
         inv_state sum_bal s
         post s r (K (K (inv_state sum_bal))) (K True)"
    unfolding deposit_def
    apply (erule post_exc_true, erule_tac post_wp)
    unfolding inv_state_def
    apply vcg
         apply (auto simp add: wpsimps)
     apply (rule bal_msg_sender, assumption)
     apply vcg
    by (auto simp add: wpsimps elim: allE[of _ "Address msg_sender"] intro!: sum_balI sum_leq_value)

  show
    d: "call.
      (x h r. effect (call x) h r  vcond x h r)
         effect (withdraw call) s r
         inv_state sum_bal s
         post s r (K (K (inv_state sum_bal))) (K True)"
    unfolding withdraw_def
    apply (erule post_exc_true, erule_tac post_wp)
    unfolding inv_state_def icall_def
    apply (case_tac "msg_sender = this")
     apply (vcg)
     apply (rule_tac s = msg_sender in subst,assumption)
     apply (vcg)
    (* Apply precondition for internal method call *)
           apply (subgoal_tac "(x h r. effect (call x) h r  vcond x h r)")
            apply (rule_tac c=call and x=Reset_m and P'=reset_post in wp_post)
    using vcond(3) apply simp apply blast
    (* End: Apply precondition for internal method call *)
             apply (vcg)
             apply (rule_tac s=msg_sender in subst, assumption)
             apply (vcg)
             apply (auto simp add:wpsimps)
      apply (vcg)
         apply (auto simp add:wpsimps)
      apply (rule bal_msg_sender, assumption)
      apply (vcg)
         apply (rule_tac mp = mp' in sum_balI)
          apply (auto simp add:wpsimps intro: sum_leq_this)
       apply (vcg)
       apply (rule_tac mp = mpa in sum_balI)
        apply (vcg)
    (* Apply precondition for internal method call *)
        apply (subgoal_tac "(x h r. effect (call x) h r  vcond x h r)")
         apply (rule_tac c=call and x=Reset_m and P'=reset_post in wp_post)
    using vcond(3) apply simp apply blast
    (* End: Apply precondition for internal method call *)
          apply vcg
          apply (auto simp add:wpsimps)
     apply vcg
        apply (auto simp add:wpsimps)
     apply (rule bal_msg_sender, assumption)
     apply vcg
        apply (rule_tac mp = mp' in sum_balI)
         apply (auto simp add:wpsimps intro: sum_leq_sender)
      defer
      apply vcg
    apply (rule_tac mp = mpa in sum_balI)
     apply vcg
    done
qed

context bank_external
begin
  thm sum_bal
  thm vcond_def
end

end