Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify exponentiation in SMT backend #106

Merged
merged 7 commits into from
May 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 54 additions & 2 deletions src/RefinedAst.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,21 @@
{-# Language ScopedTypeVariables #-}
{-# Language TypeFamilies #-}
{-# Language TypeApplications #-}
{-# LANGUAGE MonadComprehensions #-}

module RefinedAst where

import Control.Applicative (empty)

import Data.List (genericDrop,genericTake)
import Data.Text (pack)
import Data.Type.Equality
import Data.Typeable
import Data.Map.Strict (Map)
import Data.List.NonEmpty hiding (fromList)
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Data.String (fromString)

import EVM.Solidity (SlotType(..))

Expand Down Expand Up @@ -149,8 +155,8 @@ data Exp t where
NewAddr :: Exp Integer -> Exp Integer -> Exp Integer

-- polymorphic
Eq :: (Typeable t, ToJSON (Exp t)) => Exp t -> Exp t -> Exp Bool
NEq :: (Typeable t, ToJSON (Exp t)) => Exp t -> Exp t -> Exp Bool
Eq :: (Eq t, Typeable t, ToJSON (Exp t)) => Exp t -> Exp t -> Exp Bool
NEq :: (Eq t, Typeable t, ToJSON (Exp t)) => Exp t -> Exp t -> Exp Bool
ITE :: Exp Bool -> Exp t -> Exp t -> Exp t
TEntry :: (TStorageItem t) -> Exp t

Expand Down Expand Up @@ -215,6 +221,52 @@ data ReturnExp
| ExpBytes (Exp ByteString)
deriving (Eq, Show)

-- | Simplifies concrete expressions into literals.
-- Returns `Nothing` if the expression contains symbols.
eval :: Exp a -> Maybe a
eval e = case e of
And a b -> [a' && b' | a' <- eval a, b' <- eval b]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

monad comprehension! Cool, haven't seen this before

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In lieu of idiom brackets you take what you can get :)

Or a b -> [a' || b' | a' <- eval a, b' <- eval b]
Impl a b -> [a' <= b' | a' <- eval a, b' <- eval b]
Neg a -> not <$> eval a
LE a b -> [a' < b' | a' <- eval a, b' <- eval b]
LEQ a b -> [a' <= b' | a' <- eval a, b' <- eval b]
GE a b -> [a' > b' | a' <- eval a, b' <- eval b]
GEQ a b -> [a' >= b' | a' <- eval a, b' <- eval b]
LitBool a -> pure a
BoolVar _ -> empty

Add a b -> [a' + b' | a' <- eval a, b' <- eval b]
Sub a b -> [a' - b' | a' <- eval a, b' <- eval b]
Mul a b -> [a' * b' | a' <- eval a, b' <- eval b]
Div a b -> [a' `div` b' | a' <- eval a, b' <- eval b]
Mod a b -> [a' `mod` b' | a' <- eval a, b' <- eval b]
Exp a b -> [a' ^ b' | a' <- eval a, b' <- eval b]
LitInt a -> pure a
IntVar _ -> empty
IntEnv _ -> empty
IntMin a -> pure . negate $ 2 ^ (a - 1)
IntMax a -> pure $ 2 ^ (a - 1) - 1
UIntMin _ -> pure 0
UIntMax a -> pure $ 2 ^ a - 1

Cat s t -> [s' <> t' | s' <- eval s, t' <- eval t]
Slice s a b -> [BS.pack . genericDrop a' . genericTake b' $ s'
| s' <- BS.unpack <$> eval s
, a' <- eval a
, b' <- eval b]
ByVar _ -> empty
ByStr s -> pure . fromString $ s
ByLit s -> pure s
ByEnv _ -> empty

NewAddr _ _ -> empty

Eq a b -> [a' == b' | a' <- eval a, b' <- eval b]
NEq a b -> [a' /= b' | a' <- eval a, b' <- eval b]
ITE a b c -> eval a >>= \cond -> if cond then eval b else eval c
TEntry _ -> empty

-- intermediate json output helpers ---
instance ToJSON Claim where
toJSON (S storages) = object [ "kind" .= (String "Storages")
Expand Down
11 changes: 8 additions & 3 deletions src/SMT.hs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MonadComprehensions #-}

module SMT (Solver(..), SMTConfig(..), Query(..), SMTResult(..), runSMT, runQuery, mkPostconditionQueries, mkInvariantQueries, getTarget, getSMT) where

import Control.Applicative ((<|>))

import Data.Map (Map)
import Data.List.NonEmpty (NonEmpty(..))
import Data.Maybe
Expand Down Expand Up @@ -406,10 +409,12 @@ expToSMT2 ctx@(Ctx behvName whn) e = case e of

-- | SMT2 has no support for exponentiation, but we can do some preprocessing
-- if the RHS is concrete to provide some limited support for exponentiation
-- TODO: support any exponentiation expression where the RHS evaluates to a concrete value
simplifyExponentiation :: Exp Integer -> Exp Integer -> Exp Integer
simplifyExponentiation (LitInt a) (LitInt b) = LitInt (a ^ b)
simplifyExponentiation _ _ = error "Internal Error: exponentiation is unsupported in SMT lib"
simplifyExponentiation a b = fromMaybe (error "Internal Error: no support for symbolic exponents in SMT lib")
$ [LitInt $ a' ^ b' | a' <- eval a, b' <- evalb]
<|> [foldr Mul (LitInt 1) (genericReplicate b' a) | b' <- evalb]
where
evalb = eval b

constant :: Id -> MType -> SMT2
constant name tp = "(declare-const " <> name <> " " <> (sType tp) <> ")"
Expand Down
10 changes: 10 additions & 0 deletions tests/invariants/fail/concretebase.act
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
constructor of Exponentiation
interface constructor()

creates

uint256 thirtytwo := (10 - 2 * 4) ^ (100 - 5 * 19)

invariants

thirtytwo == 33
10 changes: 10 additions & 0 deletions tests/invariants/fail/concreteexponent.act
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
constructor of Exponentiation
interface constructor(uint256 x)

creates

uint256 xpow5 := x ^ (100 - 5 * 19)

invariants

xpow5 == x ^ 4
10 changes: 10 additions & 0 deletions tests/invariants/fail/symbolicexponent.act
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
constructor of Exponentiation
interface constructor(uint x)

creates

uint256 exponential := (10 - 2 * 4) ^ (100 - x * 19)

invariants

exponential == 32
10 changes: 10 additions & 0 deletions tests/invariants/pass/concretebase.act
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
constructor of Exponentiation
interface constructor()

creates

uint256 thirtytwo := (10 - 2 * 4) ^ (100 - 5 * 19)

invariants

thirtytwo == 32
10 changes: 10 additions & 0 deletions tests/invariants/pass/concreteexponent.act
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
constructor of Exponentiation
interface constructor(uint256 x)

creates

uint256 xpow5 := x ^ (100 - 5 * 19)

invariants

xpow5 == x ^ 5