def generate_test_contract(
    contract: Contract,
    type_property: str,
    output_dir: Path,
    property_file: Path,
    initialization_recommendation: str,
) -> Tuple[str, str]:
    test_contract_name = f"Test{contract.name}{type_property}"
    properties_name = f"Properties{contract.name}{type_property}"

    content = ""
    content += f'import "./{property_file}";\n'
    content += f"contract {test_contract_name} is {properties_name} {{\n"
    content += "\tconstructor() public{\n"
    content += "\t\t// Existing addresses:\n"
    content += "\t\t// - crytic_owner: If the contract has an owner, it must be crytic_owner\n"
    content += "\t\t// - crytic_user: Legitimate user\n"
    content += "\t\t// - crytic_attacker: Attacker\n"
    content += "\t\t// \n"
    content += initialization_recommendation
    content += "\t\t// \n"
    content += "\t\t// \n"
    content += "\t\t// Update the following if totalSupply and balanceOf are external functions or state variables:\n\n"
    content += "\t\tinitialTotalSupply = totalSupply();\n"
    content += "\t\tinitialBalance_owner = balanceOf(crytic_owner);\n"
    content += "\t\tinitialBalance_user = balanceOf(crytic_user);\n"
    content += "\t\tinitialBalance_attacker = balanceOf(crytic_attacker);\n"

    content += "\t}\n}\n"

    filename = f"{test_contract_name}.sol"
    write_file(output_dir, filename, content, allow_overwrite=False)

    return filename, test_contract_name
def generate_solidity_interface(output_dir: Path, addresses: Addresses):
    content = f"""
contract CryticInterface{{
    address internal crytic_owner = address({addresses.owner});
    address internal crytic_user = address({addresses.user});
    address internal crytic_attacker = address({addresses.attacker});
    uint internal initialTotalSupply;
    uint internal initialBalance_owner;
    uint internal initialBalance_user;
    uint internal initialBalance_attacker;
}}"""

    # Static file, we discard if it exists as it should never change
    write_file(output_dir, "interfaces.sol", content, discard_if_exist=True)
Ejemplo n.º 3
0
def generate_echidna_config(output_dir: Path, addresses: Addresses) -> str:
    """
    Generate the echidna configuration file
    :param output_dir:
    :param addresses:
    :return:
    """
    content = 'prefix: crytic_\n'
    content += f'deployer: "{addresses.owner}"\n'
    content += f'sender: ["{addresses.user}", "{addresses.attacker}"]\n'
    content += f'psender: "{addresses.user}"\n'
    content += 'coverage: true\n'
    filename = 'echidna_config.yaml'
    write_file(output_dir, filename, content)
    return filename
Ejemplo n.º 4
0
def generate_solidity_properties(contract: Contract, type_property: str,
                                 solidity_properties: str,
                                 output_dir: Path) -> Path:

    solidity_import = f'import "./interfaces.sol";\n'
    solidity_import += f'import "../{contract.source_mapping["filename_short"]}";'

    test_contract_name = f'Properties{contract.name}{type_property}'

    solidity_content = f'{solidity_import}\ncontract {test_contract_name} is CryticInterface,{contract.name}'
    solidity_content += f'{{\n\n{solidity_properties}\n}}\n'

    filename = f'{test_contract_name}.sol'
    write_file(output_dir, filename, solidity_content)

    return Path(filename)
Ejemplo n.º 5
0
def generate_migration(test_contract: str, output_dir: Path,
                       owner_address: str):
    """
    Generate migration file
    :param test_contract:
    :param output_dir:
    :param owner_address:
    :return:
    """
    content = f"""{test_contract} = artifacts.require("{test_contract}");
module.exports = function(deployer) {{
  deployer.deploy({test_contract}, {{from: "{owner_address}"}});
}};
"""

    output_dir = Path(output_dir, "migrations")

    output_dir.mkdir(exist_ok=True)

    migration_files = [
        js_file for js_file in output_dir.iterdir() if js_file.suffix == ".js"
        and PATTERN_TRUFFLE_MIGRATION.match(js_file.name)
    ]

    idx = len(migration_files)
    filename = f"{idx + 1}_{test_contract}.js"
    potential_previous_filename = f"{idx}_{test_contract}.js"

    for m in migration_files:
        if m.name == potential_previous_filename:
            write_file(output_dir, potential_previous_filename, content)
            return
        if test_contract in m.name:
            logger.error(f"Potential conflicts with {m.name}")

    write_file(output_dir, filename, content)
Ejemplo n.º 6
0
def generate_unit_test(
    test_contract: str,
    filename: str,
    unit_tests: List[Property],
    output_dir: Path,
    addresses: Addresses,
    assert_message: str = "",
):
    """
    Generate unit tests files
    :param test_contract:
    :param filename:
    :param unit_tests:
    :param output_dir:
    :param addresses:
    :param assert_message:
    :return:
    """
    content = f'{test_contract} = artifacts.require("{test_contract}");\n\n'

    content += _helpers()

    content += f'contract("{test_contract}", accounts => {{\n'

    content += f'\tlet owner = "{addresses.owner}";\n'
    content += f'\tlet user = "******";\n'
    content += f'\tlet attacker = "{addresses.attacker}";\n'
    for unit_test in unit_tests:
        content += f'\tit("{unit_test.description}", async () => {{\n'
        content += f"\t\tlet instance = await {test_contract}.deployed();\n"
        callers = _extract_caller(unit_test.caller)
        if unit_test.return_type == PropertyReturn.SUCCESS:
            for caller in callers:
                content += f"\t\tlet test_{caller} = await instance.{unit_test.name[:-2]}.call({{from: {caller}}});\n"
                if assert_message:
                    content += f'\t\tassert.equal(test_{caller}, true, "{assert_message}");\n'
                else:
                    content += f"\t\tassert.equal(test_{caller}, true);\n"
        elif unit_test.return_type == PropertyReturn.FAIL:
            for caller in callers:
                content += f"\t\tlet test_{caller} = await instance.{unit_test.name[:-2]}.call({{from: {caller}}});\n"
                if assert_message:
                    content += f'\t\tassert.equal(test_{caller}, false, "{assert_message}");\n'
                else:
                    content += f"\t\tassert.equal(test_{caller}, false);\n"
        elif unit_test.return_type == PropertyReturn.FAIL_OR_THROW:
            for caller in callers:
                content += f"\t\tawait catchRevertThrowReturnFalse(instance.{unit_test.name[:-2]}.call({{from: {caller}}}));\n"
        elif unit_test.return_type == PropertyReturn.THROW:
            callers = _extract_caller(unit_test.caller)
            for caller in callers:
                content += f"\t\tawait catchRevertThrow(instance.{unit_test.name[:-2]}.call({{from: {caller}}}));\n"
        content += "\t});\n"

    content += "});\n"

    output_dir = Path(output_dir, "test")
    output_dir.mkdir(exist_ok=True)

    output_dir = Path(output_dir, "crytic")
    output_dir.mkdir(exist_ok=True)

    write_file(output_dir, filename, content)
    return output_dir