Theory WP

theory WP
imports Solidity "HOL-Eisbach.Eisbach"
begin

section "Weakest precondition calculus"

named_theorems wprules
named_theorems wperules
named_theorems wpdrules
named_theorems wpsimps

declare(in Contract) inv_state_def[wpsimps]
declare icall_def[wpsimps]
declare ecall_def[wpsimps]

method wp declares wprules wpdrules wperules wpsimps = (rule wprules | drule wpdrules | erule wperules | simp add: wpsimps)
method vcg declares wprules wpdrules wperules wpsimps = wp+

subsection "Simplification rules"

lemma mapping[wpsimps]:
  "mapping x y = x"
  unfolding mapping_def ..

lemma Value_vt[wpsimps]:
  assumes "storage_data.Value x = v"
    shows "storage_data.vt v = x"
  using assms by auto

subsubsection "Kdata"

lemma kdbool_simp[wpsimps]:
  "kdbool x = Value (Bool x)"
  unfolding kdbool_def by simp

lemma kdSint_simp[wpsimps]:
  "kdSint x = Value (Uint x)"
  unfolding kdSint_def by simp

lemma kdBytes_simp[wpsimps]:
  "kdBytes xs = Value (Bytes xs)"
  unfolding kdBytes_def by simp

lemma kdAddress_simp[wpsimps]:
  "kdAddress x = Value (Address x)"
  unfolding kdAddress_def by simp

lemma kdminus[wpsimps]:
  "kdminus (rvalue.Value (Uint l)) (rvalue.Value (Uint r)) = Some (rvalue.Value (Uint (l - r)))"
  unfolding kdminus_def vtminus_def by simp

lemma kdminus_safe[wpsimps]:
  assumes "r  l"
  shows "kdminus_safe (rvalue.Value (Uint l)) (rvalue.Value (Uint r)) = Some (rvalue.Value (Uint (l - r)))"
  unfolding kdminus_safe_def using assms by (simp add: vtminus_safe.simps)

lemma kdminus_safe_dest[wpdrules]:
  assumes "kdminus_safe (rvalue.Value (Uint l)) (rvalue.Value (Uint r)) = Some ya"
  shows "r  l  ya = rvalue.Value (Uint (l - r))"
  using assms unfolding kdminus_safe_def by (simp split:if_split_asm add:vtminus_safe.simps)

lemma kdminus_storage[wpsimps]:
  "kdminus (rvalue.Storage x) z = None"
  unfolding kdminus_def vtminus_def by simp

lemma kdplus[wpsimps]:
  "kdplus (rvalue.Value (Uint l)) (rvalue.Value (Uint r)) = Some (rvalue.Value (Uint (l + r)))"
  unfolding kdplus_def vtplus_def by simp

lemma kdplus_safe[wpsimps]:
  assumes "unat l + unat r < 2^256"
  shows "kdplus_safe (rvalue.Value (Uint l)) (rvalue.Value (Uint r)) = Some (rvalue.Value (Uint (l + r)))"
  unfolding kdplus_safe_def using assms by (simp add:vtplus_safe.simps)

lemma kdplus_safe_dest[wpdrules]:
  assumes "kdplus_safe (rvalue.Value (Uint l)) (rvalue.Value (Uint r)) = Some ya"
  shows "unat l + unat r < 2^256  ya = rvalue.Value (Uint (l + r))"
  using assms unfolding kdplus_safe_def by (simp split:if_split_asm add:vtplus_safe.simps)

lemma kdmult[wpsimps]:
  "kdmult (rvalue.Value (Uint l)) (rvalue.Value (Uint r)) = Some (rvalue.Value (Uint (l * r)))"
  unfolding kdmult_def vtmult_def by simp

lemma kdmult_safe[wpsimps]:
  assumes "unat l * unat r < 2^256"
  shows "kdmult_safe (rvalue.Value (Uint l)) (rvalue.Value (Uint r)) = Some (rvalue.Value (Uint (l * r)))"
  unfolding kdmult_safe_def using assms by (simp add:vtmult_safe.simps)

lemma kdmult_safe_dest[wpdrules]:
  assumes "kdmult_safe (rvalue.Value (Uint l)) (rvalue.Value (Uint r)) = Some ya"
  shows "unat l * unat r < 2^256  ya = rvalue.Value (Uint (l * r))"
  using assms unfolding kdmult_safe_def by (simp split:if_split_asm add:vtmult_safe.simps)

subsubsection "Updates"

lemma stack_stack_update_diff[wpsimps]:
  assumes "i  i'"
  shows "Stack (stack_update i x s) $$ i' = Stack s $$ i'"
  using assms unfolding stack_update_def by simp

lemma (in Contract) stack_storage_update[wpsimps]:
  "Stack (storage_update i x s) = Stack s"
  unfolding storage_update_def by simp

lemma stack_balances_update[wpsimps]:
  "Stack (balances_update i x s) = Stack s"
  unfolding balances_update_def by simp

lemma stack_calldata_update[wpsimps]:
  "Stack (calldata_update i x s) = Stack s"
  unfolding calldata_update_def by simp

lemma stack_update_eq[wpsimps]:
  "Stack (stack_update i x s) $$ i = Some x"
  unfolding stack_update_def by simp

lemma memory_balances_update[wpsimps]:
  "state.Memory (balances_update i x s) = state.Memory s"
  unfolding balances_update_def by simp

lemma memory_stack_update[wpsimps]:
  "state.Memory (stack_update i x s) = state.Memory s"
  unfolding stack_update_def by simp

lemma calldata_balances_update[wpsimps]:
  "state.Calldata (balances_update i x s) = state.Calldata s"
  unfolding balances_update_def by simp

lemma calldata_stack_update[wpsimps]:
  "state.Calldata (stack_update i x s) = state.Calldata s"
  unfolding stack_update_def by simp

lemma storage_stack_update[wpsimps]:
 "state.Storage (stack_update i v s) = state.Storage s"
  unfolding stack_update_def by simp

lemma storage_calldata_update[wpsimps]:
 "state.Storage (calldata_update i v s) = state.Storage s"
  unfolding calldata_update_def by simp

lemma storage_balances_update[wpsimps]:
 "state.Storage (balances_update i v s) = state.Storage s"
  unfolding balances_update_def by simp

lemma calldata_calldata_update[wpsimps]:
 "state.Calldata (calldata_update i v s) $$ i = Some v"
  unfolding calldata_update_def by simp

lemma (in Contract) storage_update_diff[wpsimps]:
  assumes "i  i'"
  shows "state.Storage (storage_update i x s) this i' = state.Storage s this i'"
  using assms unfolding storage_update_def by simp

lemma (in Contract) storage_update_eq[wpsimps]:
  "state.Storage (storage_update i x s) this i = x"
  unfolding storage_update_def by simp

lemma (in Contract) balances_storage_update[wpsimps]:
  "Balances (storage_update i' x s) = Balances s"
  unfolding storage_update_def by simp

lemma balances_stack_update[wpsimps]:
  "Balances (stack_update i' x s) = Balances s"
  unfolding stack_update_def by simp

lemma balances_balances_update_diff[wpsimps]:
  assumes "i  i'"
  shows "Balances (balances_update i x s) i' = Balances s i'"
  using assms unfolding balances_update_def by simp

lemma balances_balances_update_same[wpsimps]:
  "Balances (balances_update i x s) i = x"
  unfolding balances_update_def by simp

subsection "Destruction rules"

lemma some_some[wpdrules]:
  assumes "Some x = Some y"
  shows "x = y" using assms by simp

subsection "Weakest Precondition"

definition wp::"('a, 'b, 'c) state_monad  ('a  'c  bool)  ('b  'c  bool)  'c  bool" where
  "wp f P E s 
    (case execute f s of
      Normal (r,s')  P r s'
    | Exception (e,s')  E e s'
    | NT  True)"

lemma wpI:
  assumes "r s'. execute f s = Normal (r, s')  P r s'"
      and "e s'. execute f s = Exception (e, s')  E e s'"
    shows "wp f P E s"
  unfolding wp_def by (cases "execute f s" rule:result_cases) (simp_all add: assms)

lemma wpE:
  assumes "wp f P E s"
  obtains (1) r s' where "execute f s = Normal (r, s')  P r s'"
        | (2) e s' where "execute f s = Exception (e, s')  E e s'"
        | (3) "execute f s = NT"
  using assms unfolding wp_def by (cases "execute f s" rule:result_cases) simp_all

lemma wp_simp1:
  assumes "execute f s = Normal (r, s')"
    shows "wp f P E s = P r s'"
  unfolding wp_def by (cases "execute f s" rule:result_cases) (simp_all add: assms)

lemma wp_simp2:
  assumes "execute f s = Exception (e, s')"
    shows "wp f P E s = E e s'"
  unfolding wp_def by (cases "execute f s" rule:result_cases) (simp_all add: assms)

lemma wp_simp3:
  assumes "execute f s = NT"
    shows "wp f P E s"
  unfolding wp_def by (cases "execute f s" rule:result_cases) (simp_all add: assms)

lemma wp_if[wprules]:
  assumes "b  wp a P E s"
      and "¬ b  wp c P E s"
  shows "wp (if b then a else c) P E s"
  using assms by simp

lemma wpreturn[wprules]: 
  assumes "P x s"
  shows "wp (return x) P E s"
  unfolding wp_def using assms by (simp add: execute_simps)

lemma wpget[wprules]: 
  assumes "P s s"
  shows "wp get P E s"
  unfolding wp_def using assms by (simp add: execute_simps)

lemma wpbind[wprules]:
  assumes "wp f (λa. (wp (g a) P E)) E s"
  shows "wp (f  g) P E s"
proof (cases "execute f s")
  case nf: (n a s')
  then have **:"wp (g a) P E s'" using wp_def[of f "λa. wp (g a) P E"] assms by simp
  show ?thesis
  proof (cases "execute (g a) s'")
    case ng: (n a' s'')
    then have "P a' s''" using wp_def[of "g a" P] ** by simp
    moreover from nf ng have "execute (f  g) s = Normal (a', s'')" by (simp add: execute_simps)
    ultimately show ?thesis using wp_def by fastforce
  next
    case (e e s'')
    then have "E e s''" using wp_def[of "g a" P] ** by simp
    moreover from nf e have "execute (f  g) s = Exception (e, s'')" by (simp add: execute_simps)
    ultimately show ?thesis using wp_def by fastforce
  next
    case t
    with nf have "execute (f  g) s = NT" by (simp add: execute_simps)
    then show ?thesis using wp_def by fastforce
  qed
next
  case (e e s')
  then have "E e s'" using wp_def[of f "λa. wp (g a) P E"] assms by simp
  moreover from e have "execute (f  g) s = Exception (e, s')" by (simp add: execute_simps)
  ultimately show ?thesis using wp_def by fastforce
next
  case t
  then have "execute (f  g) s = NT" by (simp add: execute_simps)
  then show ?thesis using wp_def by fastforce
qed

lemma wpthrow[wprules]:
  assumes "E x s"
  shows "wp (throw x) P E s"
  unfolding wp_def using assms by (simp add: execute_simps)

lemma wp_lfold:
  assumes "P [] s"
  assumes "a list. xs = a#list  wp (a  (λl. option Err (λ_. the_value l)  (λl'. lfold list  (λls. return (l' # ls))))) P E s"
  shows "wp (lfold xs) P E s"
  using assms  unfolding wp_def
  apply (cases xs)
  by (simp_all add: execute_simps)

lemma result_cases2[cases type: result]:
  fixes x :: "('a × 's, 'e × 's) result"
  obtains (n) a s e where "x = Normal (a, s)  x = Exception (e, s)"
        | (t) "x = NT"
proof (cases x)
  case (n a s)
  then show ?thesis using that by simp
next
  case (e e)
  then show ?thesis using that by fastforce
next
  case t
  then show ?thesis using that by simp
qed

lemma wpmodify[wprules]:
  assumes "P () (f s)"
  shows "wp (modify f) P E s"
  unfolding wp_def using assms by (simp add: execute_simps)

lemma wpnewStack[wprules]:
  assumes "P Empty (sStack := {$$})"
  shows "wp newStack P E s"
  unfolding wp_def newStack_def using assms by (simp add: execute_simps)

lemma wpnewMemory[wprules]:
  assumes "P Empty (sMemory := [])"
  shows "wp newMemory P E s"
  unfolding wp_def newMemory_def using assms by (simp add: execute_simps)

lemma wpnewCalldata[wprules]:
  assumes "P Empty (sCalldata := {$$})"
  shows "wp newCalldata P E s"
  unfolding wp_def newCalldata_def using assms by (simp add: execute_simps)

lemma wp_lift_op_monad:
  assumes "wp lm (λa. wp (rm  (λrv. option Err (K (op a rv))  return)) P E) E s"
  shows "wp (lift_op_monad op lm rm) P E s"
  unfolding lift_op_monad_def using assms by (rule wprules)

lemma wp_equals_monad[wprules]:
  assumes "wp lm (λa. wp (rm  (λrv. option Err (K (kdequals a rv))  return)) P E) E s"
  shows "wp (equals_monad lm rm) P E s"
  unfolding equals_monad_def using assms by (rule wp_lift_op_monad)

lemma wp_less_monad[wprules]:
  assumes "wp lm (λa. wp (rm  (λrv. option Err (K (kdless a rv))  return)) P E) E s"
  shows "wp (less_monad lm rm) P E s"
  unfolding less_monad_def using assms by (rule wp_lift_op_monad)

lemma wp_mod_monad[wprules]:
  assumes "wp lm (λa. wp (rm  (λrv. option Err (K (kdmod a rv))  return)) P E) E s"
  shows "wp (mod_monad lm rm) P E s"
  unfolding mod_monad_def using assms by (rule wp_lift_op_monad)

lemma wp_minus_monad[wprules]:
  assumes "wp lm (λa. wp (rm  (λrv. option Err (K (kdminus a rv))  return)) P E) E s"
  shows "wp (minus_monad lm rm) P E s"
  unfolding minus_monad_def using assms by (rule wp_lift_op_monad)

lemma wp_minus_monad_safe[wprules]:
  assumes " wp lm (λa. wp (rm  (λrv. option Err (K (kdminus_safe a rv))  return)) P E) E s"
  shows "wp (minus_monad_safe lm rm) P E s"
  unfolding minus_monad_safe_def using assms by (rule wp_lift_op_monad)

lemma wp_plus_monad[wprules]:
  assumes "wp lm (λa. wp (rm  (λrv. option Err (K (kdplus a rv))  return)) P E) E s"
  shows "wp (plus_monad lm rm) P E s"
  unfolding plus_monad_def using assms by (rule wp_lift_op_monad)

lemma wp_plus_monad_safe[wprules]:
  assumes "wp lm (λa. wp (rm  (λrv. option Err (K (kdplus_safe a rv))  return)) P E) E s"
  shows "wp (plus_monad_safe lm rm) P E s"
  unfolding plus_monad_safe_def using assms by (rule wp_lift_op_monad)

lemma wp_mult_monad[wprules]:
  assumes "wp lm (λa. wp (rm  (λrv. option Err (K (kdmult a rv))  return)) P E) E s"
  shows "wp (mult_monad lm rm) P E s"
  unfolding mult_monad_def using assms by (rule wp_lift_op_monad)

lemma wp_mult_monad_safe[wprules]:
  assumes "wp lm (λa. wp (rm  (λrv. option Err (K (kdmult_safe a rv))  return)) P E) E s"
  shows "wp (mult_monad_safe lm rm) P E s"
  unfolding mult_monad_safe_def using assms by (rule wp_lift_op_monad)

lemma wp_bool_monad[wprules]:
  assumes "P (kdbool b) s"
  shows "wp (bool_monad b) P E s"
  unfolding bool_monad_def using assms by (simp add: wprules)

lemma wp_true_monad[wprules]:
  assumes "P (kdbool True) s"
  shows "wp true_monad P E s"
  unfolding true_monad_def using assms by (rule wp_bool_monad)

lemma wp_false_monad[wprules]:
  assumes "P (kdbool False) s"
  shows "wp false_monad P E s"
  unfolding false_monad_def using assms by (rule wp_bool_monad)

lemma wp_or_monad[wprules]:
  assumes "wp l (λa. wp (r  (λrv. option Err (K (lift_value_binary vtor a rv))  return)) P E) E s"
  shows "wp (or_monad l r) P E s"
  unfolding or_monad_def kdor_def using assms by (rule wp_lift_op_monad)

lemma wp_sint_monad[wprules]:
  assumes "P (kdSint x) s"
  shows "wp (sint_monad x) P E s"
  unfolding sint_monad_def using assms by (simp add: wprules)

lemma wp_bytest_monad[wprules]:
  assumes "P (kdBytes x) s" "n = length x" "n  {1..<33}"
  shows "wp (bytes_monad n x) P E s"
  unfolding bytes_monad_def using assms by (simp add: wprules)

lemma (in Method) wp_value_monad[wprules]:
  assumes "P (kdSint msg_value) s"
  shows "wp value_monad P E s"
  unfolding value_monad_def using assms by (rule wp_sint_monad)

lemma (in Method) wp_stamp_monad[wprules]:
  assumes "P (kdSint timestamp) s"
  shows "wp block_timestamp_monad P E s"
  unfolding block_timestamp_monad_def using assms by (rule wp_sint_monad)

lemma wp_cond_monad[wprules]:
  assumes "wp bm (λa. wp (true_monad  (λrv. option Err (K (kdequals a rv))  return)) (λa. wp (if a = kdbool True then mt else if a = kdbool False then fm else throw Err) P E) E) E s"
  shows "wp (cond_monad bm mt fm) P E s"
  unfolding cond_monad_def
  apply (rule wprules)+ by (rule assms)

lemma wp_assert_monad[wprules]:
  assumes "wp (Solidity.cond_monad bm (return Empty) (throw Err)) P E s"
  shows "wp (assert_monad bm) P E s"
  unfolding assert_monad_def
  using assms by simp

lemma wpoption[wprules]:
  assumes "y. f s = Some y  P y s"
      and "f s = None  E x s"
    shows "wp (option x f) P E s"
proof (cases "f s")
  case None
  then show ?thesis unfolding option_def wp_def using assms(2) by (simp add:execute_simps)
next
  case (Some a)
  then show ?thesis unfolding option_def wp_def using assms(1) by (simp add:execute_simps)
qed

lemma wp_lift_unary_monad:
  assumes "wp lm (λa. wp (option Err (K (op a))  return) P E) E s"
  shows "wp (lift_unary_monad op lm) P E s"
  unfolding lift_unary_monad_def apply (rule wprules)+ by (rule assms)

lemma wp_not_monad[wprules]:
  assumes "wp lm (λa. wp (option Err (K (kdnot a))  return) P E) E s"
  shows "wp (not_monad lm) P E s"
  unfolding not_monad_def using assms by (rule wp_lift_unary_monad)

lemma wp_address_monad[wprules]:
  assumes "P (kdAddress a) s"
  shows "wp (address_monad a) P E s"
  unfolding address_monad_def by (simp add: wprules assms)

lemma(in Method) wp_sender_monad[wprules]:
  assumes "P (kdAddress msg_sender) s"
  shows "wp sender_monad P E s"
  unfolding sender_monad_def using assms by (rule wp_address_monad)

lemma wp_require_monad[wprules]:
  assumes "wp (x  (λv. if v = rvalue.Value (Bool True) then return Empty else throw Err)) P E s"
  shows "wp (require_monad x) P E s"
  unfolding require_monad_def using assms by (simp add:wpsimps)

lemma (in Contract) wp_storeLookup[wprules]:
  assumes "wp (lfold es)
     (λa. wp (option Err (λs. slookup a (state.Storage s this i)) 
              (λsd. if storage_data.is_Value sd then return (rvalue.Value (storage_data.vt sd)) else return (rvalue.Storage (Some Location=i, Offset= a))))
           P E)
     E s"
    shows "wp (storeLookup i es) P E s"
  unfolding storeLookup_def by (rule wprules | auto simp add: assms split:if_split)+

lemma wpassert[wprules]:
  assumes "t s  wp (return ()) P E s"
      and "¬ t s  wp (throw x) P E s"
    shows "wp (assert x t) P E s"
  unfolding wp_def apply (cases "execute (assert x t) s") apply (auto simp add: execute_simps)
  apply (metis assms(1) assms(2) execute_assert(1) execute_assert(2) wp_simp1)
  by (metis assms(1) assms(2) execute_assert(1) execute_assert(2) wp_simp2)

lemma wp_bool[wprules]:
  "wp (bool_monad b) (λa _. a = kdbool b) (K x) s"
  unfolding bool_monad_def
  by (simp add: wprules)


lemma wpskip[wprules]: 
  assumes "P Empty s"
  shows "wp skip_monad P E s"
  unfolding skip_monad_def using assms by vcg

lemma effect_bind:
  assumes "effect (m  (λx. n x)) ss r"
      and "execute m ss = Normal (a2, s)"
    shows "effect (n a2) s r"
  using assms unfolding cond_monad_def effect_def bind_def execute_create by simp

lemma effect_cond_monad:
  assumes "effect (Solidity.cond_monad c mt mf) ss r"
      and "execute (equals_monad c true_monad) ss = Normal (kdbool True, s)"
    shows "effect mt s r"
  using assms unfolding cond_monad_def
  by (metis (no_types, lifting) assms(1) execute_cond_monad_simp1 effect_def)

lemma wpwhile:
  assumes "s. iv s
             wp (equals_monad c true_monad)
                   (λa s. (a = kdbool True  wp m (K iv) E s) 
                          (a = kdbool False  P Empty s) 
                          (a  kdbool False  a  kdbool True  E Err s))
                E s"
      and "iv s"
    shows "wp (while_monad c m) P E s"
proof (cases "execute (while_monad c m) s" rule: result_cases2)
  case (n a s' ex)
  then obtain r where effect_while:"effect (while_monad c m) s r" unfolding effect_def by auto
  show ?thesis using assms
  proof (induction rule: while_monad.raw_induct[OF _ effect_while])
    case a: (1 while_monad' c m ss sn)
    have "wp (cond_monad c (bind m (K (while_monad c m))) (return Empty)) P E ss"
    proof (rule wpI)
      fix a s'
      assume "execute (cond_monad c (bind m (K (while_monad c m))) (return Empty)) ss = Normal (a, s')"
      then show "P a s'"
      proof (rule execute_cond_monad_normal_E)
        fix s''
        assume "execute (equals_monad c true_monad) ss = Normal (kdbool True, s'')"
        and "execute (m  K (while_monad c m)) s'' = Normal (a, s')"
        then have execute_equals: "execute (equals_monad c true_monad) ss = Normal (kdbool True, s'')"
        and "execute (m  K (while_monad c m)) s'' = Normal (a, s')" by simp+
        from this(2) show "P a s'"
        proof (rule execute_bind_normal_E)
          fix s''' x
          assume execute_m: "execute m s'' = Normal (x, s''')"
          and execute_while: "execute (K (while_monad c m) x) s''' = Normal (a, s')"
          moreover from a(3)[OF a(4)] have "wp m (K iv) E s''" using execute_equals unfolding wp_def by simp
          ultimately have "iv s'''" unfolding wp_def by (cases "execute m s''") (simp)+
          moreover from a(2) obtain sn where "effect (while_monad' c m) s''' sn"
          proof -
            from effect_cond_monad[OF a(2) execute_equals]
            have "effect (m  K (while_monad' c m)) s'' sn" by simp
            with effect_bind show ?thesis using that execute_m by fastforce
          qed
          ultimately have "wp (while_monad c m) P E s'''" using a(1)[OF _ a(3), where ?h=s'''] by simp
          with execute_while show "P a s'" unfolding wp_def by simp
        qed
      next
        fix s''
        assume execute_equals: "execute (equals_monad c true_monad) ss = Normal (kdbool False, s'')"
        and "execute (return Empty) s'' = Normal (a, s')"
        then have "s'' = s'" using execute_returnE by meson
        moreover from a(3)[OF a(4)] have "P Empty s''" using execute_equals unfolding wp_def by simp
        ultimately show "P a s'" by (metis execute (return Empty) s'' = Normal (a, s') execute_returnE(1))
      qed
    next
      fix x s'
      assume "execute (Solidity.cond_monad c (m  K (while_monad c m)) (return Empty)) ss = Exception (x, s')"
      then show "E x s'"
      proof (rule execute_cond_monad_exception_E)
        assume "execute (equals_monad c true_monad) ss = Exception (x, s')"
        then show "E x s'" using a(3)[OF a(4)] unfolding wp_def by simp
      next
        fix a
        assume "execute (equals_monad c true_monad) ss = Normal (a, s')"
        and "a  kdbool True  a  kdbool False  x = Err"
        then show "E x s'" using a(3)[OF a(4)] unfolding wp_def by simp
      next
        fix s''
        assume execute_equals: "execute (equals_monad c true_monad) ss = Normal (kdbool True, s'')"
        and "execute (m  K (while_monad c m)) s'' = Exception (x, s')"
        then have "execute (m  K (while_monad c m)) s'' = Exception (x, s')" by simp
        then show "E x s'"
        proof (rule execute_bind_exception_E)
          assume "execute m s'' = Exception (x, s')"
          then show "E x s'" using a(3)[OF a(4)] execute_equals unfolding wp_def by simp
        next
          fix a s'''
          assume execute_m: "execute m s'' = Normal (a, s''')"
          and execute_while:"execute (K (while_monad c m) a) s''' = Exception (x, s')"
          moreover from a(3)[OF a(4)] have "wp m (K iv) E s''" using execute_equals unfolding wp_def by simp
          ultimately have "iv s'''" unfolding wp_def by (cases "execute m s''") (simp)+
          moreover from a(2) obtain sn where "effect (while_monad' c m) s''' sn"
          proof -
            from effect_cond_monad[OF a(2) execute_equals]
            have "effect (m  K (while_monad' c m)) s'' sn" by simp
            with effect_bind show ?thesis using that execute_m by fastforce
          qed
          ultimately have "wp (while_monad c m) P E s'''" using a(1)[OF _ a(3), where ?h=s'''] by simp
          with execute_while show "E x s'" unfolding wp_def by simp
        qed
      next
        fix s''
        assume "execute (equals_monad c true_monad) ss = Normal (kdbool False, s'')"
        and "execute (return Empty) s'' = Exception (x, s')"
        then show "E x s'" by (simp add:execute_return)
      qed
    qed
    then show "wp (while_monad c m) P E ss" by (subst while_monad.simps)
  qed
next
  case t
  then show ?thesis unfolding wp_def by simp
qed

lemma wp_applyf[wprules]:
  assumes "P (f s) s"
  shows "wp (applyf f) P E s"
  unfolding applyf_def get_def return_def wp_def using assms by (auto simp add:wpsimps execute_simps)

lemma wp_case_option[wprules]:
  assumes "x = None  wp a P E s"
      and "a. x = Some a  wp (b a) P E s"
  shows "wp (case x of None  a | Some x  b x) P E s"
  unfolding wp_def apply (cases x, auto) apply (fold wp_def) by (simp add:assms)+

lemma wp_case_kdata[wprules]:
  assumes "x1. a = kdata.Storage x1  wp (S x1) P E s"
      and "x2. a = kdata.Memory x2  wp (M x2) P E s"
      and "x3. a = kdata.Calldata x3  wp (C x3) P E s"
      and "x4. a = kdata.Value x4  wp (V x4) P E s"
  shows "wp (case a of kdata.Storage p  S p | kdata.Memory l  M l | kdata.Calldata p  C p | kdata.Value x  V x) P E s"
  unfolding wp_def apply (cases a, auto) apply (fold wp_def) by (simp add:assms)+

lemma wp_init[wprules]:
  assumes "P Empty (stack_update i (kdata.Value v) s)"
  shows "wp (init v i) P E s"
  unfolding init_def wp_def kinit_def using assms by(auto simp add:wpsimps execute_simps)

lemma wp_decl[wprules]:
  assumes "wp (init (Solidity.default t) i) P E s"
  shows "wp (decl t i) P E s"
  unfolding decl_def using assms by simp

lemma wp_write[wprules]:
  assumes "x1 x2.
       Memory.write c (state.Memory s) = (x1, x2) 
       P Empty (sStack := Stack s(i $$:= kdata.Memory x1), Memory := x2)"
    shows "wp (write c i) P E s"
  unfolding write_def wp_def using assms by (auto simp add:wpsimps execute_simps split: prod.split)

lemma wp_sinit[wprules]:
  assumes "P Empty (stack_update i (kdata.Storage None) s)"
  shows "wp (sinit i) P E s"
  unfolding sinit_def wp_def using assms by (auto simp add:wpsimps execute_simps)

lemma wp_sdecl[wprules]:
  assumes "x51 x52. t = SType.TArray x51 x52  wp (sinit i) P E s"
      and "x6. t = SType.DArray x6  wp (sinit i) P E s"
      and "x71 x72. t = SType.TMap x71 x72  wp (sinit i) P E s"
      and "x8. t = SType.TEnum x8  wp (sinit i) P E s"
      and "x. t = SType.TValue x  E Err s"
  shows "wp (sdecl t i) P E s"
  unfolding wp_def apply (case_tac t) using assms by (auto simp add:wpsimps sdecl.simps execute_simps wp_def)

lemma (in Contract) wp_initStorage[wprules]:
  assumes "P Empty (storage_update i v s)"
  shows "wp (initStorage i v) P E s"
  unfolding initStorage_def wp_def using assms by(auto simp add:wpsimps execute_simps)

lemma (in Solidity) wp_init_balance[wprules]:
  assumes "P Empty (balance_update (Balances s this + unat msg_value) s)"
  shows "wp init_balance P E s"
  unfolding init_balance_def wp_def using assms by (auto simp add:wpsimps execute_simps)

lemma (in Solidity) wp_init_balance_np[wprules]:
  assumes "P Empty (balance_update (Balances s this) s)"
  shows "wp init_balance_np P E s"
  unfolding init_balance_np_def wp_def using assms by (auto simp add:wpsimps execute_simps)

lemma (in Solidity) wp_cinit[wprules]:
  assumes "P Empty (calldata_update i c (stack_update i (kdata.Calldata (Some Location = i, Offset = [])) s))"
  shows "wp (cinit (c:: 'a valtype call_data) i) P E s"
  unfolding cinit_def  wp_def using assms by (auto simp add:wpsimps execute_simps)

lemma (in Contract) wp_assign_stack_monad[wprules]:
  assumes "wp m (λa. wp (lfold is  (λis. assign_stack i is a  (λ_. return Empty))) P E) E s"
  shows "wp (assign_stack_monad i is m) P E s"
  unfolding assign_stack_monad_def apply (rule wprules) using assms by simp

lemma (in Contract) wp_storage_update_monad[wprules]:
  assumes "y. updateStore (xs @ is) sd (state.Storage s this p) = Some y  P Empty (storage_update p y s)"
      and "updateStore (xs @ is) sd (state.Storage s this p) = None  E Err s"
  shows "wp (storage_update_monad xs is sd p) P E s"
  unfolding storage_update_monad_def by (rule wprules | simp add: assms)+

lemma (in Contract) wp_assign_storage1[wperules]:
  assumes "y = rvalue.Value v"
      and "wp (storage_update_monad [] is (K (storage_data.Value v)) i) P E s"
    shows "wp (assign_storage i is y) P E s"
  using assms by simp

lemma (in Contract) wp_assign_storage2[wprules]:
  assumes "wp (storage_update_monad [] is (K (storage_data.Value v)) i) P E s"
    shows "wp (assign_storage i is (rvalue.Value v)) P E s"
  using assms by simp

lemma (in Contract) wp_assign_storage_monad[wprules]:
  assumes "wp m (λa. wp (lfold is  (λis. assign_storage i is a)) P E) E s"
  shows "wp (assign_storage_monad i is m) P E s"
  unfolding assign_storage_monad_def apply (rule wprules) using assms by simp

lemma (in Contract) wp_stackLookup[wprules]:
  assumes "wp (lfold es)
     (λa. wp (stack_disjoint x (λk. return (rvalue.Value k))
                (λp. option Err (λs. mlookup (state.Memory s) a p) 
                      (λl. option Err (λs. state.Memory s $ l) 
                            (λmd. if mdata.is_Value md then return (rvalue.Value (mdata.vt md))
                                   else return (rvalue.Memory l))))
                (λp xs.
                    option Err (λs. state.Calldata s $$ p  clookup (xs @ a)) 
                    (λsd. if call_data.is_Value sd then return (rvalue.Value (call_data.vt sd))
                           else return (rvalue.Calldata (Some Location = p, Offset = xs @ a))))
                (return (rvalue.Calldata None))
                (λp xs.
                    option Err (λs. slookup (xs @ a) (state.Storage s this p)) 
                    (λsd. if storage_data.is_Value sd then return (rvalue.Value (storage_data.vt sd))
                           else return (rvalue.Storage (Some Location = p, Offset = xs @ a))))
                (return (rvalue.Storage None)))
            P E)
     E s"
  shows "wp (stackLookup x es) P E s"
  unfolding stackLookup_def apply (vcg) using assms by simp

lemma (in Keccak256) wp_keccak256[wprules]:
  assumes "wp m (λa. wp (return (keccak256 a)) P E) E s"
  shows "wp (keccak256_monad m) P E s"
  unfolding keccak256_monad_def using assms by (rule wprules)+

lemma (in External) wp_transfer_monad[wprules]:
  assumes " wp am
     (λa. wp (readValue a 
               (λav. readAddress av 
                      (λa. vm 
                            (λvk. readValue vk 
                                   (λvv. readSint vv 
                                          (λv.
assert Err (λs. unat v  Balances s this) 
(λ_. modify (λs. balance_update (Balances s this - unat v) s) 
      (λ_. modify (λs. balances_update a (Balances s a + unat v) s) 
            (λ_. ecall (external call))))))))))
            P E)
     E s"
  shows "wp (transfer_monad call am vm) P E s"
  unfolding transfer_monad_def apply (rule wprules)+ by (rule assms)

lemma wp_readValue[wprules]:
  assumes "P (storage_data.vt yp) s"
  shows "wp (readValue (rvalue.Value (storage_data.vt yp))) P E s"
  unfolding wp_def readValue.simps by (simp add:execute_return assms)

lemma wp_readAddress[wprules]:
  assumes "P yp s"
  shows "wp (readAddress (Address yp)) P E s"
  unfolding wp_def readAddress.simps by (simp add:execute_return assms)

lemma wp_stackCheck[wprules]:
  assumes "p. Stack s $$ i = Some (kdata.Storage (Some p))  wp (sf (Location p) (Offset p)) P E s"
      and "l. Stack s $$ i = Some (kdata.Memory l)  wp (mf l) P E s"
      and "p. Stack s $$ i = Some (kdata.Calldata (Some p))  wp (cf (Location p) (Offset p)) P E s"
      and "v. Stack s $$ i = Some (kdata.Value v)  wp (kf v) P E s"
      and "Stack s $$ i = None  E Err s"
      and "Stack s $$ i = Some (kdata.Storage None)  wp sp P E s"
      and "Stack s $$ i = Some (kdata.Calldata None)  wp cp P E s"
    shows "wp (stack_disjoint i kf mf cf cp sf sp) P E s"
  unfolding wp_def stack_disjoint_def
  apply (simp add:execute_simps applyf_def get_def return_def bind_def)
  apply (cases "Stack s $$ i")
  apply (auto simp add:execute_simps)
  defer apply (case_tac a)
  apply (fold wp_def) using assms
  by (auto simp add:wprules)

lemma execute_normal:
  assumes "execute x s = Normal (a, b)"
  shows "effect x s (Inl (a,b))" using assms unfolding effect_def by simp

lemma execute_exception:
  assumes "execute x s = Exception (a, b)"
  shows "effect x s (Inr (a,b))" using assms unfolding effect_def by simp

lemma (in Contract) inv_wp:
  assumes "effect m s r"
      and "wp m (K x) (K y) s"
    shows "inv r x y"
  using assms unfolding inv_def effect_def wp_def apply (cases "execute m s") by auto

lemma (in Contract) post_wp:
  assumes "effect m s r"
      and "wp m (λr s'. P s r s'  inv_state Is s') (K (inv_state Ie)) s"
    shows "post s r Is Ie P"
  using assms unfolding post_def effect_def wp_def apply (cases "execute m s") by auto

lemma (in Contract) wp_storeArrayLength[wprules]:
  assumes "wp (lfold xs)
     (λa. wp (option Err (λs. slookup a (state.Storage s this v)) 
              (λsd. storage_disjoint sd (K (throw Err)) (λsa. return (rvalue.Value (Uint (word_of_nat (length (storage_data.ar sd)))))) (K (throw Err))))
           P E)
     E s"
  shows "wp (storeArrayLength v xs) P E s"
  unfolding storeArrayLength_def apply vcg using assms by simp

lemma (in Contract) wp_arrayLength[wprules]:
  assumes "wp (lfold xs)
     (λa. wp (stack_disjoint v (K (throw Err))
                (λp. option Err (λs. mlookup (state.Memory s) a p) 
                      (λl. option Err (λs. state.Memory s $ l) 
                            (λmd. if mdata.is_Array md
                                   then return (rvalue.Value (Uint (word_of_nat (length (mdata.ar md))))) else throw Err)))
                (λp xs.
                    option Err (λs. state.Calldata s $$ p  clookup (xs @ a)) 
                    (λsd. if call_data.is_Array sd then return (rvalue.Value (Uint (word_of_nat (length (call_data.ar sd)))))
                           else throw Err))
                (throw Err)
                (λp xs.
                    option Err (λs. slookup (xs @ a) (state.Storage s this p)) 
                    (λsd. if storage_data.is_Array sd then return (rvalue.Value (Uint (word_of_nat (length (storage_data.ar sd)))))
                           else throw Err))
                (throw Err))
            P E)
     E s"
  shows "wp (arrayLength v xs) P E s"
  unfolding arrayLength_def apply vcg using assms by simp

lemma (in Contract) wp_storearrayLength[wprules]:
  assumes "slookup [] (state.Storage s this STR ''proposals'') = None  E Err s"
 and "wp (storage_disjoint (state.Storage s this STR ''proposals'') (K (throw Err))
         (λsa. return (rvalue.Value (Uint (word_of_nat (length (storage_data.ar (state.Storage s this STR ''proposals''))))))) (K (throw Err)))
     P E s"
  shows "wp (storeArrayLength STR ''proposals'' []) P E s"
  unfolding storeArrayLength_def apply vcg using assms apply simp apply vcg done

lemma (in Contract) wp_storage_disjoint[wprules]:
  assumes "v. sd = storage_data.Value v  wp (vf v) P E s"
     and "a. sd = storage_data.Array a  wp (af a) P E s"
     and "m. sd = storage_data.Map m  wp (mf m) P E s"
  shows "wp (storage_disjoint sd vf af mf) P E s"
  using assms apply (cases sd) by (simp add:wpsimps)+

lemma (in Contract) wp_allocate[wprules]:
  assumes "wp (lfold es)
     (λa. wp (option Err (λs. slookup a (state.Storage s this i)  push d) 
              (λar. storage_update_monad [] a (K ar) i))
           P E)
     E s"
  shows "wp (allocate i es d) P E s"
  unfolding allocate_def apply vcg using assms by simp

lemma (in Contract) wp_create_memory_array[wprules]:
  assumes "wp sm
     (λa. wp (case a of
              rvalue.Value (Uint s') 
                Solidity.write (adata.Array (array (unat s') (cdefault t))) i
              | rvalue.Value _  throw Err | _  throw Err)
           P E)
     E s"
  shows "wp (create_memory_array i t sm) P E s"
  unfolding create_memory_array_def apply vcg using assms by simp

text ‹
  Using postconditions for WP
›
lemma (in Solidity) wp_post:
  assumes "(r. effect (c x) s r  post s r (K True) (K True) P')"
      and "a sa. P' s a sa  P a sa"
      and "sa e. Q e sa"
    shows "wp (c x) P Q s"
  using assms unfolding wp_def effect_def post_def inv_state_def
  by (cases "execute (c x) s") (auto)

declare(in Contract) wp_stackCheck[wprules del]

lemma (in Contract) wp_assign_stack_kdvalue[wprules]:
  assumes "Stack s $$ i = None  E Err s"
      and "¬(x2. Stack s $$ i = Some (kdata.Memory x2))"
      and "Stack s $$ i = Some (kdata.Storage None)  E Err s"
      and "Stack s $$ i = Some (kdata.Calldata None)  E Err s"
      and "aa. Stack s $$ i = Some (kdata.Storage (Some aa)) 
          wp (storage_update_monad (Offset aa) is (K (storage_data.Value v)) (Location aa)) P E s"
      and "x4. Stack s $$ i = Some (kdata.Value x4) 
          wp (modify (stack_update i (kdata.Value v))  (λa. return Empty)) P E s"
      and "a. Stack s $$ i = Some (kdata.Calldata (Some a))  E Err s"
    shows "wp (assign_stack i is (rvalue.Value v)) P E s"
  apply (vcg | auto simp add:assms stack_disjoint_def)+
  using assms apply blast
  by (vcg | auto simp add:assms stack_disjoint_def)+
declare(in Contract) wp_stackCheck[wprules]

declare write.simps [simp del]
declare mupdate.simps [simp del]
declare mlookup.simps [simp del]
declare alookup.simps [simp del]
declare locations.simps [simp del]

end