Skip to content

Commit

Permalink
Merge pull request #816 from dapphub/nice-errors-unlinked
Browse files Browse the repository at this point in the history
hevm: nicer error message when trying to deploy unlinked bytecode
  • Loading branch information
d-xo authored Oct 7, 2021
2 parents 9d3ca57 + 7ec1507 commit 1729e1d
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 19 deletions.
7 changes: 6 additions & 1 deletion src/hevm/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

## Unreleased

## Fixed
### Changed

- Clearer display for the invalid opcode (`0xfe`) in debug view
- Better error messages when trying to deploy unlinked bytecode

### Fixed

- Test contracts with no code (e.g. `abstract` contracts) are now skipped
- Replay data for invariant tests is now displayed in a form that does not cause errors when used with `dapp test --replay`
Expand Down
2 changes: 1 addition & 1 deletion src/hevm/hevm-cli/hevm-cli.hs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import EVM.Types hiding (word)
import EVM.UnitTest (UnitTestOptions, coverageReport, coverageForUnitTestContract)
import EVM.UnitTest (runUnitTestContract)
import EVM.UnitTest (getParametersFromEnvironmentVariables, testNumber)
import EVM.Dapp (findUnitTests, dappInfo, DappInfo, emptyDapp, regexMatches)
import EVM.Dapp (findUnitTests, dappInfo, DappInfo, emptyDapp)
import EVM.Format (showTraceTree, showTree', renderTree, showBranchInfoWithAbi, showLeafInfo)
import EVM.RLP (rlpdecode)
import qualified EVM.Patricia as Patricia
Expand Down
15 changes: 1 addition & 14 deletions src/hevm/src/EVM/Dapp.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import EVM (Trace, traceContract, traceOpIx, ContractCode(..), Contract(..), cod
import EVM.ABI (Event, AbiType, SolError)
import EVM.Debug (srcMapCodePos)
import EVM.Solidity
import EVM.Types (W256, abiKeccak, keccak, Buffer(..), Addr)
import EVM.Types (W256, abiKeccak, keccak, Buffer(..), Addr, regexMatches)
import EVM.Concrete

import Data.ByteString (ByteString)
Expand All @@ -26,8 +26,6 @@ import Control.Lens

import Data.List (find)
import qualified Data.Map as Map
import qualified Data.Sequence as Seq
import qualified Text.Regex.TDFA as Regex

data DappInfo = DappInfo
{ _dappRoot :: FilePath
Expand Down Expand Up @@ -122,17 +120,6 @@ mkTest sig
| "invariant" `isPrefixOf` sig = Just (InvariantTest sig)
| otherwise = Nothing

regexMatches :: Text -> Text -> Bool
regexMatches regexSource =
let
compOpts =
Regex.defaultCompOpt { Regex.lastStarGreedy = True }
execOpts =
Regex.defaultExecOpt { Regex.captureGroups = False }
regex = Regex.makeRegexOpts compOpts execOpts (unpack regexSource)
in
Regex.matchTest regex . Seq.fromList . unpack

findUnitTests :: Text -> ([SolcContract] -> [(Text, [(Test, [AbiType])])])
findUnitTests match =
concatMap $ \c ->
Expand Down
5 changes: 3 additions & 2 deletions src/hevm/src/EVM/Flatten.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ module EVM.Flatten (flatten) where
-- This module is mostly independent from the rest of Hevm,
-- using only the source code metadata support modules.

import EVM.Dapp (DappInfo, dappSources, regexMatches)
import EVM.Dapp (DappInfo, dappSources)
import EVM.Types (regexMatches)
import EVM.Solidity (sourceAsts)
import EVM.Demand (demand)

Expand Down Expand Up @@ -218,7 +219,7 @@ joinLicenses asts =
maximalPragma :: [Value] -> Text
maximalPragma asts = (
case mapMaybe versions asts of
[] -> "" -- allow for no pragma
[] -> "" -- allow for no pragma
xs ->
"pragma solidity "
<> pack (show (rangeIntersection xs))
Expand Down
8 changes: 7 additions & 1 deletion src/hevm/src/EVM/Solidity.hs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ module EVM.Solidity
, lineSubrange
, astIdMap
, astSrcMap
, containsLinkerHole
) where

import EVM.ABI
Expand Down Expand Up @@ -532,10 +533,15 @@ parseMethodInput x =
, force "internal error: method type" (parseTypeName' x)
)

containsLinkerHole :: Text -> Bool
containsLinkerHole = regexMatches "__\\$[a-z0-9]{34}\\$__"

toCode :: Text -> ByteString
toCode t = case BS16.decode (encodeUtf8 t) of
Right d -> d
Left e -> error e
Left e -> if containsLinkerHole t
then error "unlinked libraries detected in bytecode"
else error e

solidity' :: Text -> IO (Text, Text)
solidity' src = withSystemTempFile "hevm.sol" $ \path handle -> do
Expand Down
14 changes: 14 additions & 0 deletions src/hevm/src/EVM/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ import qualified Data.ByteString as BS
import qualified Data.Serialize.Get as Cereal
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text
import qualified Data.Sequence as Seq
import qualified Text.Regex.TDFA as Regex
import qualified Text.Read

-- Some stuff for "generic programming", needed to create Word512
Expand Down Expand Up @@ -553,6 +555,18 @@ abiKeccak =
>>> BS.unpack
>>> word32

-- Utils

concatMapM :: Monad m => (a -> m [b]) -> [a] -> m [b]
concatMapM f xs = liftM concat (mapM f xs)

regexMatches :: Text -> Text -> Bool
regexMatches regexSource =
let
compOpts =
Regex.defaultCompOpt { Regex.lastStarGreedy = True }
execOpts =
Regex.defaultExecOpt { Regex.captureGroups = False }
regex = Regex.makeRegexOpts compOpts execOpts (Text.unpack regexSource)
in
Regex.matchTest regex . Seq.fromList . Text.unpack
9 changes: 9 additions & 0 deletions src/hevm/test/test.hs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,15 @@ tests = testGroup "hevm"
in x == y
]

, testGroup "Unresolved link detection"
[ testCase "holes detected" $ do
let code' = "608060405234801561001057600080fd5b5060405161040f38038061040f83398181016040528101906100329190610172565b73__$f3cbc3eb14e5bd0705af404abcf6f741ec$__63ab5c1ffe826040518263ffffffff1660e01b81526004016100699190610217565b60206040518083038186803b15801561008157600080fd5b505af4158015610095573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100b99190610145565b50506103c2565b60006100d36100ce84610271565b61024c565b9050828152602081018484840111156100ef576100ee610362565b5b6100fa8482856102ca565b509392505050565b600081519050610111816103ab565b92915050565b600082601f83011261012c5761012b61035d565b5b815161013c8482602086016100c0565b91505092915050565b60006020828403121561015b5761015a61036c565b5b600061016984828501610102565b91505092915050565b6000602082840312156101885761018761036c565b5b600082015167ffffffffffffffff8111156101a6576101a5610367565b5b6101b284828501610117565b91505092915050565b60006101c6826102a2565b6101d081856102ad565b93506101e08185602086016102ca565b6101e981610371565b840191505092915050565b60006102016003836102ad565b915061020c82610382565b602082019050919050565b6000604082019050818103600083015261023181846101bb565b90508181036020830152610244816101f4565b905092915050565b6000610256610267565b905061026282826102fd565b919050565b6000604051905090565b600067ffffffffffffffff82111561028c5761028b61032e565b5b61029582610371565b9050602081019050919050565b600081519050919050565b600082825260208201905092915050565b60008115159050919050565b60005b838110156102e85780820151818401526020810190506102cd565b838111156102f7576000848401525b50505050565b61030682610371565b810181811067ffffffffffffffff821117156103255761032461032e565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f6261720000000000000000000000000000000000000000000000000000000000600082015250565b6103b4816102be565b81146103bf57600080fd5b50565b603f806103d06000396000f3fe6080604052600080fdfea26469706673582212207d03b26e43dc3d116b0021ddc9817bde3762a3b14315351f11fc4be384fd14a664736f6c63430008060033"
assertBool "linker hole not detected" (containsLinkerHole code'),
testCase "no false positives" $ do
let code' = "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806317bf8bac1461003b578063acffee6b1461005d575b600080fd5b610043610067565b604051808215151515815260200191505060405180910390f35b610065610073565b005b60008060015414905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f8a8fd6d6040518163ffffffff1660e01b815260040160206040518083038186803b1580156100da57600080fd5b505afa1580156100ee573d6000803e3d6000fd5b505050506040513d602081101561010457600080fd5b810190808051906020019092919050505060018190555056fea265627a7a723158205d775f914dcb471365a430b5f5b2cfe819e615cbbb5b2f1ccc7da1fd802e43c364736f6c634300050b0032"
assertBool "false positive" (not . containsLinkerHole $ code')
]

, testGroup "metadata stripper"
[ testCase "it strips the metadata for solc => 0.6" $ do
let code' = hexText "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806317bf8bac1461003b578063acffee6b1461005d575b600080fd5b610043610067565b604051808215151515815260200191505060405180910390f35b610065610073565b005b60008060015414905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f8a8fd6d6040518163ffffffff1660e01b815260040160206040518083038186803b1580156100da57600080fd5b505afa1580156100ee573d6000803e3d6000fd5b505050506040513d602081101561010457600080fd5b810190808051906020019092919050505060018190555056fea265627a7a723158205d775f914dcb471365a430b5f5b2cfe819e615cbbb5b2f1ccc7da1fd802e43c364736f6c634300050b0032"
Expand Down

0 comments on commit 1729e1d

Please sign in to comment.