Skip to content

Commit b23878f

Browse files
Add values from tuple elements into the dictionary (crytic#1406)
* add values from tuple elements into the dictionary * added test * avoid adding cheatcode into dictionary
1 parent a54b09b commit b23878f

File tree

3 files changed

+52
-7
lines changed

3 files changed

+52
-7
lines changed

lib/Echidna/Campaign.hs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@ import Data.Set (Set)
2626
import Data.Set qualified as Set
2727
import Data.Text (Text, unpack)
2828
import Data.Time (LocalTime)
29+
import Data.Vector qualified as V
2930
import System.Random (mkStdGen)
3031

3132
import EVM (cheatCode)
32-
import EVM.ABI (getAbi, AbiType(AbiAddressType), AbiValue(AbiAddress))
33+
import EVM.ABI (getAbi, AbiType(AbiAddressType, AbiTupleType), AbiValue(AbiAddress, AbiTuple), abiValueType)
3334
import EVM.Dapp (DappInfo(..))
3435
import EVM.Types hiding (Env, Frame(state), Gas)
3536
import EVM.Solidity (SolcContract(..), Method(..))
@@ -521,20 +522,32 @@ callseq vm txSeq = do
521522
-> (FunctionName -> Maybe AbiType)
522523
-> Map AbiType (Set AbiValue)
523524
returnValues txResults returnTypeOf =
524-
Map.fromList . flip mapMaybe txResults $ \(tx, result) -> do
525-
case result of
525+
Map.unionsWith Set.union . mapMaybe extractValues $ txResults
526+
where
527+
extractValues (tx, result) = case result of
526528
VMSuccess (ConcreteBuf buf) -> do
527529
fname <- case tx.call of
528530
SolCall (fname, _) -> Just fname
529531
_ -> Nothing
530532
type' <- returnTypeOf fname
531533
case runGetOrFail (getAbi type') (LBS.fromStrict buf) of
532-
-- make sure we don't use cheat codes to form fuzzing call sequences
533-
Right (_, _, abiValue) | abiValue /= AbiAddress (forceAddr cheatCode) ->
534-
Just (type', Set.singleton abiValue)
534+
Right (_, _, abiValue) ->
535+
if isTuple type'
536+
then Just $ Map.fromListWith Set.union
537+
[ (abiValueType val, Set.singleton val)
538+
| val <- filter (/= AbiAddress (forceAddr cheatCode)) $ V.toList $ getTupleVector abiValue
539+
]
540+
else if abiValue /= AbiAddress (forceAddr cheatCode)
541+
then Just $ Map.singleton type' (Set.singleton abiValue)
542+
else Nothing
535543
_ -> Nothing
536544
_ -> Nothing
537545

546+
isTuple (AbiTupleType _) = True
547+
isTuple _ = False
548+
getTupleVector (AbiTuple ts) = ts
549+
getTupleVector _ = error "Not a tuple!"
550+
538551
-- | Add transactions to the corpus, discarding reverted ones
539552
addToCorpus :: Int -> [(Tx, VMResult Concrete RealWorld)] -> Corpus -> Corpus
540553
addToCorpus n res corpus =

src/test/Tests/Values.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import Echidna.Types.Worker (WorkerType(..))
88

99
valuesTests :: TestTree
1010
valuesTests = testGroup "Value extraction tests"
11-
[
11+
[
1212
testContract "values/nearbyMining.sol" Nothing
1313
[ ("echidna_findNearby passed", solved "echidna_findNearby") ]
1414
, testContract' "values/smallValues.sol" Nothing Nothing (Just "coverage/test.yaml") False FuzzWorker
@@ -46,6 +46,8 @@ valuesTests = testGroup "Value extraction tests"
4646
, testContract' "values/contract.sol" (Just "Test") Nothing (Just "values/contract.yaml") False FuzzWorker
4747
[ ("verify_first passed", solved "verify_first")
4848
, ("verify_later passed", solved "verify_later") ]
49+
, testContract' "values/struct.sol" Nothing Nothing (Just "values/contract.yaml") False FuzzWorker
50+
[ ("getItem passed", solved "getItem") ]
4951
, testContract' "values/events.sol" Nothing Nothing (Just "values/events.yaml") False FuzzWorker
5052
[ ("check passed", solved "check") ]
5153
]

tests/solidity/values/struct.sol

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma experimental ABIEncoderV2;
3+
4+
contract Marketplace {
5+
uint256 internal currentKey = 1375566;
6+
struct Item {
7+
uint16 nonsense;
8+
uint256 keyId;
9+
bytes32 data;
10+
}
11+
12+
mapping(uint256 => Item) public items;
13+
14+
function addItem(bytes32 _data) public returns (Item memory) {
15+
currentKey += 174;
16+
17+
// Store the item
18+
items[currentKey] = Item({
19+
nonsense: 43,
20+
keyId: currentKey,
21+
data: _data
22+
});
23+
return items[currentKey];
24+
}
25+
26+
function getItem(uint256 _keyId) public view {
27+
assert(items[_keyId].keyId == 0);
28+
}
29+
}
30+

0 commit comments

Comments
 (0)