import pytest
from pathlib import Path
from mythril . mythril import MythrilDisassembler
from mythril . solidity . features import SolidityFeatureExtractor
from mythril . solidity . soliditycontract import SolidityContract , SolcAST
TEST_FILES = Path ( __file__ ) . parent / " testdata/input_contracts "
solc_binary_5 = MythrilDisassembler . _init_solc_binary ( " v0.5.0 " )
solc_binary_8 = MythrilDisassembler . _init_solc_binary ( " v0.8.0 " )
solc_binary_82 = MythrilDisassembler . _init_solc_binary ( " v0.8.20 " )
test_cases = [
( " suicide.sol " , 1 , " kill " , " contains_selfdestruct " , True , solc_binary_5 ) ,
(
" SimpleModifier.sol " ,
1 ,
" withdrawfunds " ,
" all_require_vars " ,
set ( [ " msg " , " owner " ] ) ,
solc_binary_5 ,
) ,
(
" SimpleModifier.sol " ,
1 ,
" withdrawfunds " ,
" transfer_vars " ,
set ( [ " owner " ] ) ,
solc_binary_5 ,
) ,
( " rubixi.sol " , 18 , " init " , " contains_selfdestruct " , False , solc_binary_5 ) ,
( " rubixi.sol " , 18 , " collectAllFees " , " has_owner_modifier " , True , solc_binary_5 ) ,
( " rubixi.sol " , 18 , " collectAllFees " , " is_payable " , False , solc_binary_5 ) ,
(
" rubixi.sol " ,
18 ,
" collectAllFees " ,
" all_require_vars " ,
set ( [ " collectedFees " , " creator " ] ) ,
solc_binary_5 ,
) ,
(
" rubixi.sol " ,
18 ,
" collectPercentOfFees " ,
" all_require_vars " ,
set ( [ " collectedFees " , " _pcent " , " creator " ] ) ,
solc_binary_5 ,
) ,
(
" rubixi.sol " ,
18 ,
" changeMultiplier " ,
" all_require_vars " ,
set ( [ " _mult " , " creator " ] ) ,
solc_binary_5 ,
) ,
( " rubixi.sol " , 18 , " " , " is_payable " , True , solc_binary_5 ) ,
(
" rubixi.sol " ,
18 ,
" collectAllFees " ,
" transfer_vars " ,
set ( [ " creator " ] ) ,
solc_binary_5 ,
) ,
( " exceptions.sol " , 8 , " assert3 " , " contains_assert " , True , solc_binary_5 ) ,
(
" exceptions.sol " ,
8 ,
" requireisfine " ,
" all_require_vars " ,
set ( [ " input " ] ) ,
solc_binary_5 ,
) ,
( " WalletLibrary.sol " , 23 , " execute " , " has_owner_modifier " , True , solc_binary_5 ) ,
( " WalletLibrary.sol " , 23 , " initWallet " , " has_owner_modifier " , False , solc_binary_5 ) ,
(
" WalletLibrary.sol " ,
23 ,
" initWallet " ,
" all_require_vars " ,
set ( [ " m_numOwners " ] ) ,
solc_binary_5 ,
) ,
(
" WalletLibrary.sol " ,
23 ,
" confirm " ,
" all_require_vars " ,
set ( [ " success " ] ) ,
solc_binary_5 ,
) ,
( " kcalls.sol " , 3 , " callSetN " , " contains_call " , True , solc_binary_5 ) ,
( " kcalls.sol " , 3 , " delegatecallSetN " , " contains_delegatecall " , True , solc_binary_5 ) ,
( " kcalls.sol " , 3 , " callcodeSetN " , " contains_staticcall " , True , solc_binary_5 ) ,
(
" regression_1.sol " ,
5 ,
" transfer " ,
" all_require_vars " ,
set ( [ " userBalances " , " msg " , " _to " , " _amount " ] ) ,
solc_binary_8 ,
) ,
( " SecureVault.sol " , 11 , " withdraw " , " has_owner_modifier " , True , solc_binary_82 ) ,
( " SecureVault.sol " , 11 , " deposit " , " has_owner_modifier " , False , solc_binary_82 ) ,
(
" SecureVault.sol " ,
11 ,
" withdraw " ,
" all_require_vars " ,
set ( [ " amount " , " this " ] ) ,
solc_binary_82 ,
) ,
]
@pytest . mark . parametrize (
" file_name, num_funcs, func_name, field, expected_value, solc_binary " , test_cases
)
def test_features ( file_name , num_funcs , func_name , field , expected_value , solc_binary ) :
input_file = TEST_FILES / file_name
name = file_name . split ( " . " ) [ 0 ]
if name [ 0 ] . islower ( ) :
name = name . capitalize ( )
contract = SolidityContract ( str ( input_file ) , name = name , solc_binary = solc_binary )
ms = contract . solc_json [ " sources " ] [ str ( input_file ) ] [ " ast " ]
sfe = SolidityFeatureExtractor ( ms )
fe = sfe . extract_features ( )
assert len ( fe ) == num_funcs
assert fe [ func_name ] [ field ] == expected_value