From 6ec83e48c04ff16a50274843368c76e2883031f3 Mon Sep 17 00:00:00 2001 From: Anish Naik Date: Wed, 30 Aug 2023 09:58:27 -0400 Subject: [PATCH 01/14] - merged all three testing modes - created testing provider utils file to evaluate whether an abi.method is an optimization / property test - updated fuzzer tests - made default property test prefix "invariant_" --- cmd/fuzz_flags.go | 24 -------- fuzzing/config/config.go | 56 ++++--------------- fuzzing/config/config_defaults.go | 22 ++------ fuzzing/fuzzer.go | 19 +++---- fuzzing/fuzzer_test.go | 52 +++++------------ fuzzing/test_case_assertion_provider.go | 17 +++++- fuzzing/test_case_optimization_provider.go | 25 +++------ fuzzing/test_case_property_provider.go | 24 +++----- .../assertions/assert_and_property_test.sol | 2 +- .../contracts/chain/tx_out_of_gas.sol | 2 +- .../specific_call_sequence.sol | 2 +- .../deployments/deployment_order.sol | 4 +- .../deployments/deployment_with_args.sol | 8 +-- .../deployments/inner_deployment.sol | 2 +- .../inner_deployment_on_construction.sol | 2 +- .../deployments/inner_inner_deployment.sol | 2 +- .../deployments/internal_library.sol | 2 +- .../contracts/deployments/testing_scope.sol | 4 +- .../value_generation/generate_all_types.sol | 2 +- .../value_generation/match_addr_contract.sol | 2 +- .../value_generation/match_addr_exact.sol | 2 +- .../value_generation/match_addr_sender.sol | 2 +- .../value_generation/match_ints_xy.sol | 2 +- .../value_generation/match_payable_xy.sol | 2 +- .../value_generation/match_string_exact.sol | 2 +- .../value_generation/match_structs_xy.sol | 2 +- .../value_generation/match_uints_xy.sol | 2 +- .../vm_tests/block_hash_store_check.sol | 2 +- .../vm_tests/block_number_increasing.sol | 2 +- .../vm_tests/block_timestamp_increasing.sol | 2 +- utils/testing_provider_utils.go | 35 ++++++++++++ 31 files changed, 127 insertions(+), 201 deletions(-) create mode 100644 utils/testing_provider_utils.go diff --git a/cmd/fuzz_flags.go b/cmd/fuzz_flags.go index 02300ecb..bf292796 100644 --- a/cmd/fuzz_flags.go +++ b/cmd/fuzz_flags.go @@ -57,14 +57,6 @@ func addFuzzFlags() error { fuzzCmd.Flags().String("deployer", "", "account address used to deploy contracts") - // Assertion mode - fuzzCmd.Flags().Bool("assertion-mode", false, - fmt.Sprintf("enable assertion mode (unless a config file is provided, default is %t)", defaultConfig.Fuzzing.Testing.AssertionTesting.Enabled)) - - // Optimization mode - fuzzCmd.Flags().Bool("optimization-mode", false, - fmt.Sprintf("enable optimization mode (unless a config file is provided, default is %t)", defaultConfig.Fuzzing.Testing.OptimizationTesting.Enabled)) - // Trace all fuzzCmd.Flags().Bool("trace-all", false, fmt.Sprintf("print the execution trace for every element in a shrunken call sequence instead of only the last element (unless a config file is provided, default is %t)", defaultConfig.Fuzzing.Testing.TraceAll)) @@ -153,22 +145,6 @@ func updateProjectConfigWithFuzzFlags(cmd *cobra.Command, projectConfig *config. } } - // Update assertion mode enablement - if cmd.Flags().Changed("assertion-mode") { - projectConfig.Fuzzing.Testing.AssertionTesting.Enabled, err = cmd.Flags().GetBool("assertion-mode") - if err != nil { - return err - } - } - - // Update optimization mode enablement - if cmd.Flags().Changed("optimization-mode") { - projectConfig.Fuzzing.Testing.OptimizationTesting.Enabled, err = cmd.Flags().GetBool("optimization-mode") - if err != nil { - return err - } - } - // Update trace all enablement if cmd.Flags().Changed("trace-all") { projectConfig.Fuzzing.Testing.TraceAll, err = cmd.Flags().GetBool("trace-all") diff --git a/fuzzing/config/config.go b/fuzzing/config/config.go index 283e5329..f394da2c 100644 --- a/fuzzing/config/config.go +++ b/fuzzing/config/config.go @@ -106,31 +106,21 @@ type TestingConfig struct { // even if this option is not enabled. TraceAll bool `json:"traceAll"` - // AssertionTesting describes the configuration used for assertion testing. - AssertionTesting AssertionTestingConfig `json:"assertionTesting"` - - // PropertyTesting describes the configuration used for property testing. - PropertyTesting PropertyTestConfig `json:"propertyTesting"` - - // OptimizationTesting describes the configuration used for optimization testing. - OptimizationTesting OptimizationTestingConfig `json:"optimizationTesting"` -} - -// AssertionTestingConfig describes the configuration options used for assertion testing -type AssertionTestingConfig struct { - // Enabled describes whether testing is enabled. - Enabled bool `json:"enabled"` - // TestViewMethods dictates whether constant/pure/view methods should be tested. TestViewMethods bool `json:"testViewMethods"` - // AssertionModes describes the various panic codes that can be enabled and be treated as a "failing case" - AssertionModes AssertionModesConfig `json:"assertionModes"` + // InvariantTestPrefixes dictates what method name prefixes will determine if a contract method is an invariant test. + InvariantTestPrefixes []string `json:"invariantTestPrefixes"` + + // OptimizationTestPrefixes dictates what method name prefixes will determine if a contract method is an optimization test. + OptimizationTestPrefixes []string `json:"optimizationTestPrefixes"` + + // PanicCodeConfig describes the various panic codes that can be enabled and be treated as a "failing case" + PanicCodeConfig PanicCodeConfig `json:"panicCodeConfig"` } -// AssertionModesConfig describes the configuration options for the various modes that can be enabled for assertion -// testing -type AssertionModesConfig struct { +// PanicCodeConfig describes the various panic codes that can be enabled and be treated as a failing assertion test +type PanicCodeConfig struct { // FailOnCompilerInsertedPanic describes whether a generic compiler inserted panic should be treated as a failing case FailOnCompilerInsertedPanic bool `json:"failOnCompilerInsertedPanic"` @@ -162,24 +152,6 @@ type AssertionModesConfig struct { FailOnCallUninitializedVariable bool `json:"failOnCallUninitializedVariable"` } -// PropertyTestConfig describes the configuration options used for property testing -type PropertyTestConfig struct { - // Enabled describes whether testing is enabled. - Enabled bool `json:"enabled"` - - // TestPrefixes dictates what method name prefixes will determine if a contract method is a property test. - TestPrefixes []string `json:"testPrefixes"` -} - -// OptimizationTestingConfig describes the configuration options used for optimization testing -type OptimizationTestingConfig struct { - // Enabled describes whether testing is enabled. - Enabled bool `json:"enabled"` - - // TestPrefixes dictates what method name prefixes will determine if a contract method is an optimization test. - TestPrefixes []string `json:"testPrefixes"` -} - // LoggingConfig describes the configuration options for logging to console and file type LoggingConfig struct { // Level describes whether logs of certain severity levels (eg info, warning, etc.) will be emitted or discarded. @@ -284,14 +256,6 @@ func (p *ProjectConfig) Validate() error { return errors.New("project configuration must specify only a well-formed deployer address") } - // Verify property testing fields. - if p.Fuzzing.Testing.PropertyTesting.Enabled { - // Test prefixes must be supplied if property testing is enabled. - if len(p.Fuzzing.Testing.PropertyTesting.TestPrefixes) == 0 { - return errors.New("project configuration must specify test name prefixes if property testing is enabled") - } - } - // Ensure that the log level is a valid one if _, err := zerolog.ParseLevel(p.Logging.Level.String()); err != nil { return err diff --git a/fuzzing/config/config_defaults.go b/fuzzing/config/config_defaults.go index 2a6e92f0..f1ac72de 100644 --- a/fuzzing/config/config_defaults.go +++ b/fuzzing/config/config_defaults.go @@ -57,24 +57,14 @@ func GetDefaultProjectConfig(platform string) (*ProjectConfig, error) { StopOnNoTests: true, TestAllContracts: false, TraceAll: false, - AssertionTesting: AssertionTestingConfig{ - Enabled: false, - TestViewMethods: false, - AssertionModes: AssertionModesConfig{ - FailOnAssertion: true, - }, + InvariantTestPrefixes: []string{ + "invariant_", }, - PropertyTesting: PropertyTestConfig{ - Enabled: true, - TestPrefixes: []string{ - "fuzz_", - }, + OptimizationTestPrefixes: []string{ + "optimize_", }, - OptimizationTesting: OptimizationTestingConfig{ - Enabled: false, - TestPrefixes: []string{ - "optimize_", - }, + PanicCodeConfig: PanicCodeConfig{ + FailOnAssertion: true, }, }, TestChainConfig: *chainConfig, diff --git a/fuzzing/fuzzer.go b/fuzzing/fuzzer.go index b69df568..160d34ad 100644 --- a/fuzzing/fuzzer.go +++ b/fuzzing/fuzzer.go @@ -168,18 +168,13 @@ func NewFuzzer(config config.ProjectConfig) (*Fuzzer, error) { fuzzer.AddCompilationTargets(compilations) } - // Register any default providers if specified. - if fuzzer.config.Fuzzing.Testing.PropertyTesting.Enabled { - attachPropertyTestCaseProvider(fuzzer) - } - if fuzzer.config.Fuzzing.Testing.AssertionTesting.Enabled { - attachAssertionTestCaseProvider(fuzzer) - } - if fuzzer.config.Fuzzing.Testing.OptimizationTesting.Enabled { - // TODO: Remove this warning when call sequence shrinking is improved - fuzzer.logger.Warn("Currently, optimization mode's call sequence shrinking is inefficient; this may lead to minor performance issues") - attachOptimizationTestCaseProvider(fuzzer) - } + // Register the default testing providers + attachPropertyTestCaseProvider(fuzzer) + attachAssertionTestCaseProvider(fuzzer) + + // TODO: Remove this warning when call sequence shrinking is improved + fuzzer.logger.Warn("Currently, optimization mode's call sequence shrinking is inefficient; this may lead to minor performance issues") + attachOptimizationTestCaseProvider(fuzzer) return fuzzer, nil } diff --git a/fuzzing/fuzzer_test.go b/fuzzing/fuzzer_test.go index 55c845fd..5e1f3cef 100644 --- a/fuzzing/fuzzer_test.go +++ b/fuzzing/fuzzer_test.go @@ -22,8 +22,6 @@ func TestFuzzerHooks(t *testing.T) { filePath: "testdata/contracts/assertions/assert_immediate.sol", configUpdates: func(config *config.ProjectConfig) { config.Fuzzing.DeploymentOrder = []string{"TestContract"} - config.Fuzzing.Testing.PropertyTesting.Enabled = false - config.Fuzzing.Testing.AssertionTesting.Enabled = true }, method: func(f *fuzzerTestContext) { // Attach to fuzzer hooks which simply set a success state. @@ -77,17 +75,15 @@ func TestAssertionMode(t *testing.T) { filePath: filePath, configUpdates: func(config *config.ProjectConfig) { config.Fuzzing.DeploymentOrder = []string{"TestContract"} - config.Fuzzing.Testing.PropertyTesting.Enabled = false - config.Fuzzing.Testing.AssertionTesting.Enabled = true - config.Fuzzing.Testing.AssertionTesting.AssertionModes.FailOnAssertion = true - config.Fuzzing.Testing.AssertionTesting.AssertionModes.FailOnAllocateTooMuchMemory = true - config.Fuzzing.Testing.AssertionTesting.AssertionModes.FailOnArithmeticUnderflow = true - config.Fuzzing.Testing.AssertionTesting.AssertionModes.FailOnCallUninitializedVariable = true - config.Fuzzing.Testing.AssertionTesting.AssertionModes.FailOnEnumTypeConversionOutOfBounds = true - config.Fuzzing.Testing.AssertionTesting.AssertionModes.FailOnDivideByZero = true - config.Fuzzing.Testing.AssertionTesting.AssertionModes.FailOnIncorrectStorageAccess = true - config.Fuzzing.Testing.AssertionTesting.AssertionModes.FailOnOutOfBoundsArrayAccess = true - config.Fuzzing.Testing.AssertionTesting.AssertionModes.FailOnPopEmptyArray = true + config.Fuzzing.Testing.PanicCodeConfig.FailOnAssertion = true + config.Fuzzing.Testing.PanicCodeConfig.FailOnAllocateTooMuchMemory = true + config.Fuzzing.Testing.PanicCodeConfig.FailOnArithmeticUnderflow = true + config.Fuzzing.Testing.PanicCodeConfig.FailOnCallUninitializedVariable = true + config.Fuzzing.Testing.PanicCodeConfig.FailOnEnumTypeConversionOutOfBounds = true + config.Fuzzing.Testing.PanicCodeConfig.FailOnDivideByZero = true + config.Fuzzing.Testing.PanicCodeConfig.FailOnIncorrectStorageAccess = true + config.Fuzzing.Testing.PanicCodeConfig.FailOnOutOfBoundsArrayAccess = true + config.Fuzzing.Testing.PanicCodeConfig.FailOnPopEmptyArray = true }, method: func(f *fuzzerTestContext) { // Start the fuzzer @@ -109,8 +105,6 @@ func TestAssertionsNotRequire(t *testing.T) { configUpdates: func(config *config.ProjectConfig) { config.Fuzzing.DeploymentOrder = []string{"TestContract"} config.Fuzzing.TestLimit = 500 - config.Fuzzing.Testing.PropertyTesting.Enabled = false - config.Fuzzing.Testing.AssertionTesting.Enabled = true }, method: func(f *fuzzerTestContext) { // Start the fuzzer @@ -132,8 +126,6 @@ func TestAssertionsAndProperties(t *testing.T) { config.Fuzzing.DeploymentOrder = []string{"TestContract"} config.Fuzzing.TestLimit = 500 config.Fuzzing.Testing.StopOnFailedTest = false - config.Fuzzing.Testing.PropertyTesting.Enabled = true - config.Fuzzing.Testing.AssertionTesting.Enabled = true }, method: func(f *fuzzerTestContext) { // Start the fuzzer @@ -156,9 +148,6 @@ func TestOptimizationMode(t *testing.T) { filePath: filePath, configUpdates: func(config *config.ProjectConfig) { config.Fuzzing.DeploymentOrder = []string{"TestContract"} - config.Fuzzing.Testing.PropertyTesting.Enabled = false - config.Fuzzing.Testing.AssertionTesting.Enabled = false - config.Fuzzing.Testing.OptimizationTesting.Enabled = true config.Fuzzing.TestLimit = 10_000 // this test should expose a failure quickly. }, method: func(f *fuzzerTestContext) { @@ -168,11 +157,10 @@ func TestOptimizationMode(t *testing.T) { // Check the value found for optimization test var testCases = f.fuzzer.TestCasesWithStatus(TestCaseStatusPassed) - switch v := testCases[0].(type) { - case *OptimizationTestCase: - assert.EqualValues(t, v.Value().Cmp(big.NewInt(4241)), 0) - default: - t.Errorf("invalid test case found %T", v) + for _, testCase := range testCases { + if optimizationTestCase, ok := testCase.(*OptimizationTestCase); ok { + assert.EqualValues(t, optimizationTestCase.Value().Cmp(big.NewInt(4241)), 0) + } } }, }) @@ -239,13 +227,10 @@ func TestCheatCodes(t *testing.T) { configUpdates: func(config *config.ProjectConfig) { config.Fuzzing.DeploymentOrder = []string{"TestContract"} - // some tests require full sequence + revert to test fully + // Some tests require full sequence + revert to test fully config.Fuzzing.Workers = 3 config.Fuzzing.TestLimit = uint64(config.Fuzzing.CallSequenceLength*config.Fuzzing.Workers) * 3 - // enable assertion testing only - config.Fuzzing.Testing.PropertyTesting.Enabled = false - config.Fuzzing.Testing.AssertionTesting.Enabled = true config.Fuzzing.TestChainConfig.CheatCodeConfig.EnableFFI = true }, method: func(f *fuzzerTestContext) { @@ -281,9 +266,6 @@ func TestConsoleLog(t *testing.T) { configUpdates: func(config *config.ProjectConfig) { config.Fuzzing.DeploymentOrder = []string{"TestContract"} config.Fuzzing.TestLimit = 10000 - // enable assertion testing only - config.Fuzzing.Testing.PropertyTesting.Enabled = true - config.Fuzzing.Testing.AssertionTesting.Enabled = true }, method: func(f *fuzzerTestContext) { // Start the fuzzer @@ -442,8 +424,6 @@ func TestExecutionTraces(t *testing.T) { filePath: filePath, configUpdates: func(config *config.ProjectConfig) { config.Fuzzing.DeploymentOrder = []string{"TestContract"} - config.Fuzzing.Testing.PropertyTesting.Enabled = false - config.Fuzzing.Testing.AssertionTesting.Enabled = true }, method: func(f *fuzzerTestContext) { // Start the fuzzer @@ -485,8 +465,6 @@ func TestTestingScope(t *testing.T) { config.Fuzzing.TestLimit = 1_000 // this test should expose a failure quickly. config.Fuzzing.Testing.TestAllContracts = testingAllContracts config.Fuzzing.Testing.StopOnFailedTest = false - config.Fuzzing.Testing.AssertionTesting.Enabled = true - config.Fuzzing.Testing.PropertyTesting.Enabled = true }, method: func(f *fuzzerTestContext) { // Start the fuzzer @@ -638,8 +616,6 @@ func TestASTValueExtraction(t *testing.T) { filePath: "testdata/contracts/value_generation/ast_value_extraction.sol", configUpdates: func(config *config.ProjectConfig) { config.Fuzzing.TestLimit = 1 // stop immediately to simply see what values were mined. - config.Fuzzing.Testing.AssertionTesting.Enabled = true - config.Fuzzing.Testing.PropertyTesting.Enabled = false }, method: func(f *fuzzerTestContext) { // Start the fuzzer diff --git a/fuzzing/test_case_assertion_provider.go b/fuzzing/test_case_assertion_provider.go index 300d97dc..d4ec95e9 100644 --- a/fuzzing/test_case_assertion_provider.go +++ b/fuzzing/test_case_assertion_provider.go @@ -5,6 +5,7 @@ import ( "github.com/crytic/medusa/fuzzing/calls" "github.com/crytic/medusa/fuzzing/config" "github.com/crytic/medusa/fuzzing/contracts" + "github.com/crytic/medusa/utils" "github.com/ethereum/go-ethereum/accounts/abi" "golang.org/x/exp/slices" "sync" @@ -44,8 +45,18 @@ func attachAssertionTestCaseProvider(fuzzer *Fuzzer) *AssertionTestCaseProvider // isTestableMethod checks whether the method is configured by the attached fuzzer to be a target of assertion testing. // Returns true if this target should be tested, false otherwise. func (t *AssertionTestCaseProvider) isTestableMethod(method abi.Method) bool { + // Do not test optimization tests + if utils.IsOptimizationTest(method, t.fuzzer.config.Fuzzing.Testing.OptimizationTestPrefixes) { + return false + } + + // Do not test property tests + if utils.IsPropertyTest(method, t.fuzzer.config.Fuzzing.Testing.InvariantTestPrefixes) { + return false + } + // Only test constant methods (pure/view) if we are configured to. - return !method.IsConstant() || t.fuzzer.config.Fuzzing.Testing.AssertionTesting.TestViewMethods + return !method.IsConstant() || t.fuzzer.config.Fuzzing.Testing.TestViewMethods } // checkAssertionFailures checks the results of the last call for assertion failures. @@ -73,7 +84,7 @@ func (t *AssertionTestCaseProvider) checkAssertionFailures(callSequence calls.Ca panicCode := abiutils.GetSolidityPanicCode(lastExecutionResult.Err, lastExecutionResult.ReturnData, true) failure := false if panicCode != nil { - failure = encounteredAssertionFailure(panicCode.Uint64(), t.fuzzer.config.Fuzzing.Testing.AssertionTesting.AssertionModes) + failure = encounteredAssertionFailure(panicCode.Uint64(), t.fuzzer.config.Fuzzing.Testing.PanicCodeConfig) } return &methodId, failure, nil @@ -241,7 +252,7 @@ func (t *AssertionTestCaseProvider) callSequencePostCallTest(worker *FuzzerWorke // code was enabled in the config. Note that the panic codes are defined in the abiutils package and that this function // panic if it is provided a panic code that is not defined in the abiutils package. // TODO: This is a terrible design and a future PR should be made to maintain assertion and panic logic correctly -func encounteredAssertionFailure(panicCode uint64, conf config.AssertionModesConfig) bool { +func encounteredAssertionFailure(panicCode uint64, conf config.PanicCodeConfig) bool { // Switch on panic code switch panicCode { case abiutils.PanicCodeCompilerInserted: diff --git a/fuzzing/test_case_optimization_provider.go b/fuzzing/test_case_optimization_provider.go index 424db7e5..9bcaff83 100644 --- a/fuzzing/test_case_optimization_provider.go +++ b/fuzzing/test_case_optimization_provider.go @@ -5,10 +5,9 @@ import ( "github.com/crytic/medusa/fuzzing/calls" "github.com/crytic/medusa/fuzzing/contracts" "github.com/crytic/medusa/fuzzing/executiontracer" - "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/crytic/medusa/utils" "github.com/ethereum/go-ethereum/core" "math/big" - "strings" "sync" ) @@ -45,6 +44,11 @@ type optimizationTestCaseProviderWorkerState struct { // attachOptimizationTestCaseProvider attaches a new OptimizationTestCaseProvider to the Fuzzer and returns it. func attachOptimizationTestCaseProvider(fuzzer *Fuzzer) *OptimizationTestCaseProvider { + // If there are no optimization test prefixes, don't create a test case provider and don't subscribe to any events + if len(fuzzer.config.Fuzzing.Testing.OptimizationTestPrefixes) == 0 { + return nil + } + // Create a test case provider t := &OptimizationTestCaseProvider{ fuzzer: fuzzer, @@ -60,21 +64,6 @@ func attachOptimizationTestCaseProvider(fuzzer *Fuzzer) *OptimizationTestCasePro return t } -// isOptimizationTest check whether the method is an optimization test given potential naming prefixes it must conform to -// and its underlying input/output arguments. -func (t *OptimizationTestCaseProvider) isOptimizationTest(method abi.Method) bool { - // Loop through all enabled prefixes to find a match - for _, prefix := range t.fuzzer.Config().Fuzzing.Testing.OptimizationTesting.TestPrefixes { - if strings.HasPrefix(method.Name, prefix) { - // An optimization test must take no inputs and return an int256 - if len(method.Inputs) == 0 && len(method.Outputs) == 1 && method.Outputs[0].Type.T == abi.IntTy && method.Outputs[0].Type.Size == 256 { - return true - } - } - } - return false -} - // runOptimizationTest executes a given optimization test method (w/ an optional execution trace) and returns the return value // from the optimization test method. This is called after every call the Fuzzer makes when testing call sequences for each test case. func (t *OptimizationTestCaseProvider) runOptimizationTest(worker *FuzzerWorker, optimizationTestMethod *contracts.DeployedContractMethod, trace bool) (*big.Int, *executiontracer.ExecutionTrace, error) { @@ -143,7 +132,7 @@ func (t *OptimizationTestCaseProvider) onFuzzerStarting(event FuzzerStartingEven for _, contract := range t.fuzzer.ContractDefinitions() { for _, method := range contract.CompiledContract().Abi.Methods { // Verify this method is an optimization test method - if !t.isOptimizationTest(method) { + if !utils.IsOptimizationTest(method, t.fuzzer.config.Fuzzing.Testing.OptimizationTestPrefixes) { continue } // Create local variables to avoid pointer types in the loop being overridden. diff --git a/fuzzing/test_case_property_provider.go b/fuzzing/test_case_property_provider.go index 10399cc5..284d87b1 100644 --- a/fuzzing/test_case_property_provider.go +++ b/fuzzing/test_case_property_provider.go @@ -5,11 +5,10 @@ import ( "github.com/crytic/medusa/fuzzing/calls" "github.com/crytic/medusa/fuzzing/contracts" "github.com/crytic/medusa/fuzzing/executiontracer" - "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/crytic/medusa/utils" "github.com/ethereum/go-ethereum/core" "golang.org/x/exp/slices" "math/big" - "strings" "sync" ) @@ -47,6 +46,11 @@ type propertyTestCaseProviderWorkerState struct { // attachPropertyTestCaseProvider attaches a new PropertyTestCaseProvider to the Fuzzer and returns it. func attachPropertyTestCaseProvider(fuzzer *Fuzzer) *PropertyTestCaseProvider { + // If there are no property test prefixes, don't create a test case provider and don't subscribe to any events + if len(fuzzer.config.Fuzzing.Testing.InvariantTestPrefixes) == 0 { + return nil + } + // Create a test case provider t := &PropertyTestCaseProvider{ fuzzer: fuzzer, @@ -62,20 +66,6 @@ func attachPropertyTestCaseProvider(fuzzer *Fuzzer) *PropertyTestCaseProvider { return t } -// isPropertyTest check whether the method is a property test given potential naming prefixes it must conform to -// and its underlying input/output arguments. -func (t *PropertyTestCaseProvider) isPropertyTest(method abi.Method) bool { - // Loop through all enabled prefixes to find a match - for _, prefix := range t.fuzzer.Config().Fuzzing.Testing.PropertyTesting.TestPrefixes { - if strings.HasPrefix(method.Name, prefix) { - if len(method.Inputs) == 0 && len(method.Outputs) == 1 && method.Outputs[0].Type.T == abi.BoolTy { - return true - } - } - } - return false -} - // checkPropertyTestFailed executes a given property test method to see if it returns a failed status. This is used to // facilitate testing of property test methods after every call the Fuzzer makes when testing call sequences. // A boolean indicating whether an execution trace should be captured and returned is provided to the method. @@ -150,7 +140,7 @@ func (t *PropertyTestCaseProvider) onFuzzerStarting(event FuzzerStartingEvent) e for _, method := range contract.CompiledContract().Abi.Methods { // Verify this method is a property test method - if !t.isPropertyTest(method) { + if !utils.IsPropertyTest(method, t.fuzzer.config.Fuzzing.Testing.InvariantTestPrefixes) { continue } diff --git a/fuzzing/testdata/contracts/assertions/assert_and_property_test.sol b/fuzzing/testdata/contracts/assertions/assert_and_property_test.sol index 90051ba5..b7cdc1dc 100644 --- a/fuzzing/testdata/contracts/assertions/assert_and_property_test.sol +++ b/fuzzing/testdata/contracts/assertions/assert_and_property_test.sol @@ -5,7 +5,7 @@ contract TestContract { assert(false); } - function fuzz_failing_property() public view returns (bool) { + function invariant_failing_property() public view returns (bool) { // ASSERTION: fail immediately. return false; } diff --git a/fuzzing/testdata/contracts/chain/tx_out_of_gas.sol b/fuzzing/testdata/contracts/chain/tx_out_of_gas.sol index 96fb55d0..ec623084 100644 --- a/fuzzing/testdata/contracts/chain/tx_out_of_gas.sol +++ b/fuzzing/testdata/contracts/chain/tx_out_of_gas.sol @@ -10,7 +10,7 @@ contract TestContract { } } - function fuzz_never_apply_state_when_oog() public view returns (bool) { + function invariant_never_apply_state_when_oog() public view returns (bool) { // ASSERTION: this state should never be applied, as our out of gas error should revert changes. return x == 0; } diff --git a/fuzzing/testdata/contracts/corpus_mutation/specific_call_sequence.sol b/fuzzing/testdata/contracts/corpus_mutation/specific_call_sequence.sol index e66c3771..2ccc1cbe 100644 --- a/fuzzing/testdata/contracts/corpus_mutation/specific_call_sequence.sol +++ b/fuzzing/testdata/contracts/corpus_mutation/specific_call_sequence.sol @@ -29,7 +29,7 @@ contract TestContract { } } - function fuzz_solve_me() public view returns (bool) { + function invariant_solve_me() public view returns (bool) { // ASSERTION: The fuzzer should be able to fail this test case and solve all challenges. return index < 7; } diff --git a/fuzzing/testdata/contracts/deployments/deployment_order.sol b/fuzzing/testdata/contracts/deployments/deployment_order.sol index efdff8bc..e21c20a5 100644 --- a/fuzzing/testdata/contracts/deployments/deployment_order.sol +++ b/fuzzing/testdata/contracts/deployments/deployment_order.sol @@ -15,7 +15,7 @@ contract InheritedFirstContract is FirstContract { y = value + 9; } - function fuzz_never_specific_values() public view returns (bool) { + function invariant_never_specific_values() public view returns (bool) { // ASSERTION: x should never be 10 at the same time y is 80 return !(x == 10 && y == 80); } @@ -41,7 +41,7 @@ contract InheritedSecondContract is SecondContract { c = value + 7; } - function fuzz_never_specific_values() public view returns (bool) { + function invariant_never_specific_values() public view returns (bool) { // ASSERTION: a should never be 10 at the same time b is 80 at the same time c is 14 return !(a == 10 && b == 80 && c == 14); } diff --git a/fuzzing/testdata/contracts/deployments/deployment_with_args.sol b/fuzzing/testdata/contracts/deployments/deployment_with_args.sol index 11e62d12..397e4b2a 100644 --- a/fuzzing/testdata/contracts/deployments/deployment_with_args.sol +++ b/fuzzing/testdata/contracts/deployments/deployment_with_args.sol @@ -15,15 +15,15 @@ contract DeploymentWithArgs { z = _z; } - function fuzz_checkX() public returns (bool) { + function invariant_checkX() public returns (bool) { return x != 123456789; } - function fuzz_checkY() public returns (bool) { + function invariant_checkY() public returns (bool) { return y != 0x5465; } - function fuzz_checkZ() public returns (bool) { + function invariant_checkZ() public returns (bool) { return z.a != 0x4d2; } @@ -40,7 +40,7 @@ contract Dependent { deployed = _deployed; } - function fuzz_checkDeployed() public returns (bool) { + function invariant_checkDeployed() public returns (bool) { return deployed == 0x0000000000000000000000000000000000000000; } diff --git a/fuzzing/testdata/contracts/deployments/inner_deployment.sol b/fuzzing/testdata/contracts/deployments/inner_deployment.sol index d853687e..46ccebe5 100644 --- a/fuzzing/testdata/contracts/deployments/inner_deployment.sol +++ b/fuzzing/testdata/contracts/deployments/inner_deployment.sol @@ -1,7 +1,7 @@ // InnerDeploymentFactory deploys InnerDeployment when a method is called after deployment, and verifies the fuzzer can // match bytecode and fail the test appropriately. contract InnerDeployment { - function fuzz_inner_deployment() public view returns (bool) { + function invariant_inner_deployment() public view returns (bool) { // ASSERTION: Fail immediately. return false; } diff --git a/fuzzing/testdata/contracts/deployments/inner_deployment_on_construction.sol b/fuzzing/testdata/contracts/deployments/inner_deployment_on_construction.sol index 15aec27c..93f10d03 100644 --- a/fuzzing/testdata/contracts/deployments/inner_deployment_on_construction.sol +++ b/fuzzing/testdata/contracts/deployments/inner_deployment_on_construction.sol @@ -6,7 +6,7 @@ contract InnerDeployment { x = 7; } - function fuzz_inner_deployment() public view returns (bool) { + function invariant_inner_deployment() public view returns (bool) { // ASSERTION: Fail immediately. return false; } diff --git a/fuzzing/testdata/contracts/deployments/inner_inner_deployment.sol b/fuzzing/testdata/contracts/deployments/inner_inner_deployment.sol index 8a1b1140..9de74545 100644 --- a/fuzzing/testdata/contracts/deployments/inner_inner_deployment.sol +++ b/fuzzing/testdata/contracts/deployments/inner_inner_deployment.sol @@ -2,7 +2,7 @@ // deployed, a method can be used to deploy an InnerInnerDeployment. We verify we can violate an invariant // in a two-layer deep dynamic deployment. contract InnerInnerDeployment { - function fuzz_inner_inner_deployment() public view returns (bool) { + function invariant_inner_inner_deployment() public view returns (bool) { // ASSERTION: Fail immediately. return false; } diff --git a/fuzzing/testdata/contracts/deployments/internal_library.sol b/fuzzing/testdata/contracts/deployments/internal_library.sol index ba03a08a..35f52a69 100644 --- a/fuzzing/testdata/contracts/deployments/internal_library.sol +++ b/fuzzing/testdata/contracts/deployments/internal_library.sol @@ -28,7 +28,7 @@ contract TestInternalLibrary { return a + b; } - function fuzz_library_linking_broken() public view returns (bool) { + function invariant_library_linking_broken() public view returns (bool) { // ASSERTION: We should always be able to compute correctly. return !failedTest; } diff --git a/fuzzing/testdata/contracts/deployments/testing_scope.sol b/fuzzing/testdata/contracts/deployments/testing_scope.sol index af4f6605..113e650e 100644 --- a/fuzzing/testdata/contracts/deployments/testing_scope.sol +++ b/fuzzing/testdata/contracts/deployments/testing_scope.sol @@ -6,7 +6,7 @@ contract TestContractChild { assert(false); } - function fuzz_failing_property_test_method_child() public view returns (bool) { + function invariant_failing_property_test_method_child() public view returns (bool) { return false; } } @@ -22,7 +22,7 @@ contract TestContract { assert(false); } - function fuzz_failing_property_test_method() public view returns (bool) { + function invariant_failing_property_test_method() public view returns (bool) { return false; } } diff --git a/fuzzing/testdata/contracts/value_generation/generate_all_types.sol b/fuzzing/testdata/contracts/value_generation/generate_all_types.sol index a4d0114c..8e887963 100644 --- a/fuzzing/testdata/contracts/value_generation/generate_all_types.sol +++ b/fuzzing/testdata/contracts/value_generation/generate_all_types.sol @@ -24,7 +24,7 @@ contract GenerateAllTypes { s = ""; } - function fuzz_never_fail() public view returns (bool) { + function invariant_never_fail() public view returns (bool) { // ASSERTION: never fail, to keep testing value generation return true; } diff --git a/fuzzing/testdata/contracts/value_generation/match_addr_contract.sol b/fuzzing/testdata/contracts/value_generation/match_addr_contract.sol index 27654a16..da3de1f4 100644 --- a/fuzzing/testdata/contracts/value_generation/match_addr_contract.sol +++ b/fuzzing/testdata/contracts/value_generation/match_addr_contract.sol @@ -6,7 +6,7 @@ contract TestContract { a = value; } - function fuzz_never_specific_values() public view returns (bool) { + function invariant_never_specific_values() public view returns (bool) { // ASSERTION: a should not be the contract's address itself. return !(a == address(this)); } diff --git a/fuzzing/testdata/contracts/value_generation/match_addr_exact.sol b/fuzzing/testdata/contracts/value_generation/match_addr_exact.sol index 385e2bfb..89cf70e1 100644 --- a/fuzzing/testdata/contracts/value_generation/match_addr_exact.sol +++ b/fuzzing/testdata/contracts/value_generation/match_addr_exact.sol @@ -12,7 +12,7 @@ contract TestContract { y = value; } - function fuzz_never_specific_values() public view returns (bool) { + function invariant_never_specific_values() public view returns (bool) { // ASSERTION: x and y should not equal the exact addresses below. return !(x == address(0x12345) && y == address(7)); } diff --git a/fuzzing/testdata/contracts/value_generation/match_addr_sender.sol b/fuzzing/testdata/contracts/value_generation/match_addr_sender.sol index 6be91398..4720f2f8 100644 --- a/fuzzing/testdata/contracts/value_generation/match_addr_sender.sol +++ b/fuzzing/testdata/contracts/value_generation/match_addr_sender.sol @@ -8,7 +8,7 @@ contract TestContract { sender = msg.sender; } - function fuzz_never_specific_values() public view returns (bool) { + function invariant_never_specific_values() public view returns (bool) { // ASSERTION: a should not be sender's address who set it. return a != sender; } diff --git a/fuzzing/testdata/contracts/value_generation/match_ints_xy.sol b/fuzzing/testdata/contracts/value_generation/match_ints_xy.sol index 2cb5ca2b..679d5387 100644 --- a/fuzzing/testdata/contracts/value_generation/match_ints_xy.sol +++ b/fuzzing/testdata/contracts/value_generation/match_ints_xy.sol @@ -12,7 +12,7 @@ contract TestContract { } - function fuzz_never_specific_values() public view returns (bool) { + function invariant_never_specific_values() public view returns (bool) { // ASSERTION: x should never be -10 at the same time y is -62 return !(x == -10 && y == -62); } diff --git a/fuzzing/testdata/contracts/value_generation/match_payable_xy.sol b/fuzzing/testdata/contracts/value_generation/match_payable_xy.sol index 68221d87..74e34592 100644 --- a/fuzzing/testdata/contracts/value_generation/match_payable_xy.sol +++ b/fuzzing/testdata/contracts/value_generation/match_payable_xy.sol @@ -11,7 +11,7 @@ contract TestContract { paidAmount2 = msg.value; } - function fuzz_never_pay_exact_amounts() public view returns (bool) { + function invariant_never_pay_exact_amounts() public view returns (bool) { // ASSERTION: paid amounts should never equal the exact numbers below. return !(paidAmount == 7777 && paidAmount2 == 8888); } diff --git a/fuzzing/testdata/contracts/value_generation/match_string_exact.sol b/fuzzing/testdata/contracts/value_generation/match_string_exact.sol index 6dda6770..ad2799fc 100644 --- a/fuzzing/testdata/contracts/value_generation/match_string_exact.sol +++ b/fuzzing/testdata/contracts/value_generation/match_string_exact.sol @@ -6,7 +6,7 @@ contract TestContract { s = value; } - function fuzz_never_specific_values() public view returns (bool) { + function invariant_never_specific_values() public view returns (bool) { // ASSERTION: s should not be the MAGIC_STRING return keccak256(abi.encodePacked((s))) != keccak256(abi.encodePacked((MAGIC_STRING))); } diff --git a/fuzzing/testdata/contracts/value_generation/match_structs_xy.sol b/fuzzing/testdata/contracts/value_generation/match_structs_xy.sol index cc6d8495..8c296b52 100644 --- a/fuzzing/testdata/contracts/value_generation/match_structs_xy.sol +++ b/fuzzing/testdata/contracts/value_generation/match_structs_xy.sol @@ -23,7 +23,7 @@ contract TestContract { s = ts; } - function fuzz_never_specific_values() public view returns (bool) { + function invariant_never_specific_values() public view returns (bool) { // ASSERTION: x should never be 10 at the same time y is 80 return !(s.x == 10 && s.i.y == 80); } diff --git a/fuzzing/testdata/contracts/value_generation/match_uints_xy.sol b/fuzzing/testdata/contracts/value_generation/match_uints_xy.sol index a064f423..a955f8e3 100644 --- a/fuzzing/testdata/contracts/value_generation/match_uints_xy.sol +++ b/fuzzing/testdata/contracts/value_generation/match_uints_xy.sol @@ -12,7 +12,7 @@ contract TestContract { } - function fuzz_never_specific_values() public view returns (bool) { + function invariant_never_specific_values() public view returns (bool) { // ASSERTION: x should never be 10 at the same time y is 80 return !(x == 10 && y == 80); } diff --git a/fuzzing/testdata/contracts/vm_tests/block_hash_store_check.sol b/fuzzing/testdata/contracts/vm_tests/block_hash_store_check.sol index d9f3d8df..47eb56d8 100644 --- a/fuzzing/testdata/contracts/vm_tests/block_hash_store_check.sol +++ b/fuzzing/testdata/contracts/vm_tests/block_hash_store_check.sol @@ -49,7 +49,7 @@ contract TestContract { lastBlockNumber = block.number; } - function fuzz_violate_block_hash_continuity() public view returns (bool) { + function invariant_violate_block_hash_continuity() public view returns (bool) { // ASSERTION: we fail if our blockHash works as expected so our fuzzer will catch it. return !failedTest; } diff --git a/fuzzing/testdata/contracts/vm_tests/block_number_increasing.sol b/fuzzing/testdata/contracts/vm_tests/block_number_increasing.sol index cbcecab4..fee29dfc 100644 --- a/fuzzing/testdata/contracts/vm_tests/block_number_increasing.sol +++ b/fuzzing/testdata/contracts/vm_tests/block_number_increasing.sol @@ -10,7 +10,7 @@ contract TestContract { // This method does nothing but is left exposed so it can be called by the fuzzer to advance block.number } - function fuzz_increase_block_number_by_10() public view returns (bool) { + function invariant_increase_block_number_by_10() public view returns (bool) { // ASSERTION: block number should never increase more than 10 (we expect failure) return !(block.number - startingBlockNumber >= 10); } diff --git a/fuzzing/testdata/contracts/vm_tests/block_timestamp_increasing.sol b/fuzzing/testdata/contracts/vm_tests/block_timestamp_increasing.sol index f14bbaa7..20abd19c 100644 --- a/fuzzing/testdata/contracts/vm_tests/block_timestamp_increasing.sol +++ b/fuzzing/testdata/contracts/vm_tests/block_timestamp_increasing.sol @@ -10,7 +10,7 @@ contract TestContract { // This method does nothing but is left exposed so it can be called by the fuzzer to advance blocks/timestamps. } - function fuzz_increase_block_timestamp() public view returns (bool) { + function invariant_increase_block_timestamp() public view returns (bool) { // ASSERTION: block timestamp should never increase more than 10 (we expect failure) return !(block.timestamp - startingBlockTimestamp >= 10); } diff --git a/utils/testing_provider_utils.go b/utils/testing_provider_utils.go new file mode 100644 index 00000000..bd0aefea --- /dev/null +++ b/utils/testing_provider_utils.go @@ -0,0 +1,35 @@ +package utils + +import ( + "github.com/ethereum/go-ethereum/accounts/abi" + "strings" +) + +// IsOptimizationTest checks whether the method is an optimization test given potential naming prefixes it must conform to +// and its underlying input/output arguments. +func IsOptimizationTest(method abi.Method, prefixes []string) bool { + // Loop through all enabled prefixes to find a match + for _, prefix := range prefixes { + if strings.HasPrefix(method.Name, prefix) { + // An optimization test must take no inputs and return an int256 + if len(method.Inputs) == 0 && len(method.Outputs) == 1 && method.Outputs[0].Type.T == abi.IntTy && method.Outputs[0].Type.Size == 256 { + return true + } + } + } + return false +} + +// IsPropertyTest checks whether the method is a property test given potential naming prefixes it must conform to +// and its underlying input/output arguments. +func IsPropertyTest(method abi.Method, prefixes []string) bool { + // Loop through all enabled prefixes to find a match + for _, prefix := range prefixes { + if strings.HasPrefix(method.Name, prefix) { + if len(method.Inputs) == 0 && len(method.Outputs) == 1 && method.Outputs[0].Type.T == abi.BoolTy { + return true + } + } + } + return false +} From a3e87bc4bcdecf2963bfcc0a29063f0ce1063a4b Mon Sep 17 00:00:00 2001 From: Anish Naik Date: Wed, 30 Aug 2023 14:09:21 -0400 Subject: [PATCH 02/14] enable all testing modes, update fuzzer tests --- cmd/fuzz_flags.go | 3 +- fuzzing/config/config.go | 39 ++++++++++-- fuzzing/config/config_defaults.go | 22 +++++-- fuzzing/fuzzer.go | 18 ++++-- fuzzing/fuzzer_test.go | 59 +++++++++++++++---- fuzzing/test_case_assertion_provider.go | 8 +-- fuzzing/test_case_optimization_provider.go | 7 +-- fuzzing/test_case_property_provider.go | 7 +-- .../assertions/assert_and_property_test.sol | 2 +- .../contracts/chain/tx_out_of_gas.sol | 2 +- .../specific_call_sequence.sol | 2 +- .../deployments/deployment_order.sol | 4 +- .../deployments/deployment_with_args.sol | 8 +-- .../deployments/inner_deployment.sol | 2 +- .../inner_deployment_on_construction.sol | 2 +- .../deployments/inner_inner_deployment.sol | 2 +- .../deployments/internal_library.sol | 2 +- .../contracts/deployments/testing_scope.sol | 4 +- .../value_generation/generate_all_types.sol | 2 +- .../value_generation/match_addr_contract.sol | 2 +- .../value_generation/match_addr_exact.sol | 2 +- .../value_generation/match_addr_sender.sol | 2 +- .../value_generation/match_ints_xy.sol | 2 +- .../value_generation/match_payable_xy.sol | 2 +- .../value_generation/match_string_exact.sol | 2 +- .../value_generation/match_structs_xy.sol | 2 +- .../value_generation/match_uints_xy.sol | 2 +- .../vm_tests/block_hash_store_check.sol | 2 +- .../vm_tests/block_number_increasing.sol | 2 +- .../vm_tests/block_timestamp_increasing.sol | 2 +- utils/testing_provider_utils.go | 7 +-- 31 files changed, 147 insertions(+), 77 deletions(-) diff --git a/cmd/fuzz_flags.go b/cmd/fuzz_flags.go index bf292796..0a6fb08d 100644 --- a/cmd/fuzz_flags.go +++ b/cmd/fuzz_flags.go @@ -45,9 +45,8 @@ func addFuzzFlags() error { fmt.Sprintf("order in which to deploy target contracts (unless a config file is provided, default is %v)", defaultConfig.Fuzzing.DeploymentOrder)) // Corpus directory - // TODO: Update description when we add "coverage reports" feature fuzzCmd.Flags().String("corpus-dir", "", - fmt.Sprintf("directory path for corpus items (unless a config file is provided, default is %q)", defaultConfig.Fuzzing.CorpusDirectory)) + fmt.Sprintf("directory path for corpus items and coverage reports (unless a config file is provided, default is %q)", defaultConfig.Fuzzing.CorpusDirectory)) // Senders fuzzCmd.Flags().StringSlice("senders", []string{}, diff --git a/fuzzing/config/config.go b/fuzzing/config/config.go index f394da2c..32ed9f87 100644 --- a/fuzzing/config/config.go +++ b/fuzzing/config/config.go @@ -106,14 +106,23 @@ type TestingConfig struct { // even if this option is not enabled. TraceAll bool `json:"traceAll"` - // TestViewMethods dictates whether constant/pure/view methods should be tested. - TestViewMethods bool `json:"testViewMethods"` + // AssertionTesting describes the configuration used for assertion testing. + AssertionTesting AssertionTestingConfig `json:"assertionTesting"` + + // PropertyTesting describes the configuration used for property testing. + PropertyTesting PropertyTestingConfig `json:"propertyTesting"` + + // OptimizationTesting describes the configuration used for optimization testing. + OptimizationTesting OptimizationTestingConfig `json:"optimizationTesting"` +} - // InvariantTestPrefixes dictates what method name prefixes will determine if a contract method is an invariant test. - InvariantTestPrefixes []string `json:"invariantTestPrefixes"` +// AssertionTestingConfig describes the configuration options used for assertion testing +type AssertionTestingConfig struct { + // Enabled describes whether testing is enabled. + Enabled bool `json:"enabled"` - // OptimizationTestPrefixes dictates what method name prefixes will determine if a contract method is an optimization test. - OptimizationTestPrefixes []string `json:"optimizationTestPrefixes"` + // TestViewMethods dictates whether constant/pure/view methods should be tested. + TestViewMethods bool `json:"testViewMethods"` // PanicCodeConfig describes the various panic codes that can be enabled and be treated as a "failing case" PanicCodeConfig PanicCodeConfig `json:"panicCodeConfig"` @@ -152,6 +161,24 @@ type PanicCodeConfig struct { FailOnCallUninitializedVariable bool `json:"failOnCallUninitializedVariable"` } +// PropertyTestingConfig describes the configuration options used for property testing +type PropertyTestingConfig struct { + // Enabled describes whether testing is enabled. + Enabled bool `json:"enabled"` + + // TestPrefixes dictates what method name prefixes will determine if a contract method is a property test. + TestPrefixes []string `json:"testPrefixes"` +} + +// OptimizationTestingConfig describes the configuration options used for optimization testing +type OptimizationTestingConfig struct { + // Enabled describes whether testing is enabled. + Enabled bool `json:"enabled"` + + // TestPrefixes dictates what method name prefixes will determine if a contract method is an optimization test. + TestPrefixes []string `json:"testPrefixes"` +} + // LoggingConfig describes the configuration options for logging to console and file type LoggingConfig struct { // Level describes whether logs of certain severity levels (eg info, warning, etc.) will be emitted or discarded. diff --git a/fuzzing/config/config_defaults.go b/fuzzing/config/config_defaults.go index f1ac72de..7ed02798 100644 --- a/fuzzing/config/config_defaults.go +++ b/fuzzing/config/config_defaults.go @@ -57,14 +57,24 @@ func GetDefaultProjectConfig(platform string) (*ProjectConfig, error) { StopOnNoTests: true, TestAllContracts: false, TraceAll: false, - InvariantTestPrefixes: []string{ - "invariant_", + AssertionTesting: AssertionTestingConfig{ + Enabled: true, + TestViewMethods: false, + PanicCodeConfig: PanicCodeConfig{ + FailOnAssertion: true, + }, }, - OptimizationTestPrefixes: []string{ - "optimize_", + PropertyTesting: PropertyTestingConfig{ + Enabled: true, + TestPrefixes: []string{ + "property_", + }, }, - PanicCodeConfig: PanicCodeConfig{ - FailOnAssertion: true, + OptimizationTesting: OptimizationTestingConfig{ + Enabled: true, + TestPrefixes: []string{ + "optimize_", + }, }, }, TestChainConfig: *chainConfig, diff --git a/fuzzing/fuzzer.go b/fuzzing/fuzzer.go index 160d34ad..3e609493 100644 --- a/fuzzing/fuzzer.go +++ b/fuzzing/fuzzer.go @@ -168,13 +168,19 @@ func NewFuzzer(config config.ProjectConfig) (*Fuzzer, error) { fuzzer.AddCompilationTargets(compilations) } - // Register the default testing providers - attachPropertyTestCaseProvider(fuzzer) - attachAssertionTestCaseProvider(fuzzer) + // Register any default providers if specified. + if fuzzer.config.Fuzzing.Testing.PropertyTesting.Enabled { + attachPropertyTestCaseProvider(fuzzer) + } + if fuzzer.config.Fuzzing.Testing.AssertionTesting.Enabled { + attachAssertionTestCaseProvider(fuzzer) + } + if fuzzer.config.Fuzzing.Testing.OptimizationTesting.Enabled { + // TODO: Remove this warning when call sequence shrinking is improved + fuzzer.logger.Warn("Currently, optimization mode's call sequence shrinking is inefficient; this may lead to minor performance issues") + attachOptimizationTestCaseProvider(fuzzer) + } - // TODO: Remove this warning when call sequence shrinking is improved - fuzzer.logger.Warn("Currently, optimization mode's call sequence shrinking is inefficient; this may lead to minor performance issues") - attachOptimizationTestCaseProvider(fuzzer) return fuzzer, nil } diff --git a/fuzzing/fuzzer_test.go b/fuzzing/fuzzer_test.go index 5e1f3cef..e110d87f 100644 --- a/fuzzing/fuzzer_test.go +++ b/fuzzing/fuzzer_test.go @@ -22,6 +22,8 @@ func TestFuzzerHooks(t *testing.T) { filePath: "testdata/contracts/assertions/assert_immediate.sol", configUpdates: func(config *config.ProjectConfig) { config.Fuzzing.DeploymentOrder = []string{"TestContract"} + config.Fuzzing.Testing.PropertyTesting.Enabled = false + config.Fuzzing.Testing.OptimizationTesting.Enabled = false }, method: func(f *fuzzerTestContext) { // Attach to fuzzer hooks which simply set a success state. @@ -75,15 +77,17 @@ func TestAssertionMode(t *testing.T) { filePath: filePath, configUpdates: func(config *config.ProjectConfig) { config.Fuzzing.DeploymentOrder = []string{"TestContract"} - config.Fuzzing.Testing.PanicCodeConfig.FailOnAssertion = true - config.Fuzzing.Testing.PanicCodeConfig.FailOnAllocateTooMuchMemory = true - config.Fuzzing.Testing.PanicCodeConfig.FailOnArithmeticUnderflow = true - config.Fuzzing.Testing.PanicCodeConfig.FailOnCallUninitializedVariable = true - config.Fuzzing.Testing.PanicCodeConfig.FailOnEnumTypeConversionOutOfBounds = true - config.Fuzzing.Testing.PanicCodeConfig.FailOnDivideByZero = true - config.Fuzzing.Testing.PanicCodeConfig.FailOnIncorrectStorageAccess = true - config.Fuzzing.Testing.PanicCodeConfig.FailOnOutOfBoundsArrayAccess = true - config.Fuzzing.Testing.PanicCodeConfig.FailOnPopEmptyArray = true + config.Fuzzing.Testing.AssertionTesting.PanicCodeConfig.FailOnAssertion = true + config.Fuzzing.Testing.AssertionTesting.PanicCodeConfig.FailOnAllocateTooMuchMemory = true + config.Fuzzing.Testing.AssertionTesting.PanicCodeConfig.FailOnArithmeticUnderflow = true + config.Fuzzing.Testing.AssertionTesting.PanicCodeConfig.FailOnCallUninitializedVariable = true + config.Fuzzing.Testing.AssertionTesting.PanicCodeConfig.FailOnEnumTypeConversionOutOfBounds = true + config.Fuzzing.Testing.AssertionTesting.PanicCodeConfig.FailOnDivideByZero = true + config.Fuzzing.Testing.AssertionTesting.PanicCodeConfig.FailOnIncorrectStorageAccess = true + config.Fuzzing.Testing.AssertionTesting.PanicCodeConfig.FailOnOutOfBoundsArrayAccess = true + config.Fuzzing.Testing.AssertionTesting.PanicCodeConfig.FailOnPopEmptyArray = true + config.Fuzzing.Testing.PropertyTesting.Enabled = false + config.Fuzzing.Testing.OptimizationTesting.Enabled = false }, method: func(f *fuzzerTestContext) { // Start the fuzzer @@ -105,6 +109,8 @@ func TestAssertionsNotRequire(t *testing.T) { configUpdates: func(config *config.ProjectConfig) { config.Fuzzing.DeploymentOrder = []string{"TestContract"} config.Fuzzing.TestLimit = 500 + config.Fuzzing.Testing.PropertyTesting.Enabled = false + config.Fuzzing.Testing.OptimizationTesting.Enabled = false }, method: func(f *fuzzerTestContext) { // Start the fuzzer @@ -126,6 +132,7 @@ func TestAssertionsAndProperties(t *testing.T) { config.Fuzzing.DeploymentOrder = []string{"TestContract"} config.Fuzzing.TestLimit = 500 config.Fuzzing.Testing.StopOnFailedTest = false + config.Fuzzing.Testing.OptimizationTesting.Enabled = false }, method: func(f *fuzzerTestContext) { // Start the fuzzer @@ -149,6 +156,8 @@ func TestOptimizationMode(t *testing.T) { configUpdates: func(config *config.ProjectConfig) { config.Fuzzing.DeploymentOrder = []string{"TestContract"} config.Fuzzing.TestLimit = 10_000 // this test should expose a failure quickly. + config.Fuzzing.Testing.PropertyTesting.Enabled = false + config.Fuzzing.Testing.AssertionTesting.Enabled = false }, method: func(f *fuzzerTestContext) { // Start the fuzzer @@ -178,6 +187,8 @@ func TestChainBehaviour(t *testing.T) { config.Fuzzing.TestLimit = uint64(config.Fuzzing.CallSequenceLength) // we just need a few oog txs to test config.Fuzzing.Timeout = 10 // to be safe, we set a 10s timeout config.Fuzzing.TransactionGasLimit = 500000 // we set this low, so contract execution runs out of gas earlier. + config.Fuzzing.Testing.AssertionTesting.Enabled = false + config.Fuzzing.Testing.OptimizationTesting.Enabled = false }, method: func(f *fuzzerTestContext) { // Start the fuzzer @@ -230,7 +241,8 @@ func TestCheatCodes(t *testing.T) { // Some tests require full sequence + revert to test fully config.Fuzzing.Workers = 3 config.Fuzzing.TestLimit = uint64(config.Fuzzing.CallSequenceLength*config.Fuzzing.Workers) * 3 - + config.Fuzzing.Testing.PropertyTesting.Enabled = false + config.Fuzzing.Testing.OptimizationTesting.Enabled = false config.Fuzzing.TestChainConfig.CheatCodeConfig.EnableFFI = true }, method: func(f *fuzzerTestContext) { @@ -266,6 +278,8 @@ func TestConsoleLog(t *testing.T) { configUpdates: func(config *config.ProjectConfig) { config.Fuzzing.DeploymentOrder = []string{"TestContract"} config.Fuzzing.TestLimit = 10000 + config.Fuzzing.Testing.PropertyTesting.Enabled = false + config.Fuzzing.Testing.OptimizationTesting.Enabled = false }, method: func(f *fuzzerTestContext) { // Start the fuzzer @@ -312,6 +326,8 @@ func TestDeploymentsInnerDeployments(t *testing.T) { config.Fuzzing.TestLimit = 1_000 // this test should expose a failure quickly. config.Fuzzing.Testing.StopOnFailedContractMatching = true config.Fuzzing.Testing.TestAllContracts = true // test dynamically deployed contracts + config.Fuzzing.Testing.AssertionTesting.Enabled = false + config.Fuzzing.Testing.OptimizationTesting.Enabled = false }, method: func(f *fuzzerTestContext) { // Start the fuzzer @@ -333,6 +349,8 @@ func TestDeploymentsInnerDeployments(t *testing.T) { config.Fuzzing.TestLimit = 1_000 // this test should expose a failure quickly. config.Fuzzing.Testing.StopOnFailedContractMatching = true config.Fuzzing.Testing.TestAllContracts = true // test dynamically deployed contracts + config.Fuzzing.Testing.AssertionTesting.Enabled = false + config.Fuzzing.Testing.OptimizationTesting.Enabled = false }, method: func(f *fuzzerTestContext) { // Start the fuzzer @@ -352,6 +370,8 @@ func TestDeploymentsInternalLibrary(t *testing.T) { configUpdates: func(config *config.ProjectConfig) { config.Fuzzing.DeploymentOrder = []string{"TestInternalLibrary"} config.Fuzzing.TestLimit = 100 // this test should expose a failure quickly. + config.Fuzzing.Testing.AssertionTesting.Enabled = false + config.Fuzzing.Testing.OptimizationTesting.Enabled = false }, method: func(f *fuzzerTestContext) { // Start the fuzzer @@ -380,6 +400,8 @@ func TestDeploymentsSelfDestruct(t *testing.T) { config.Fuzzing.DeploymentOrder = []string{"InnerDeploymentFactory"} config.Fuzzing.TestLimit = 500 // this test should expose a failure quickly. config.Fuzzing.Testing.StopOnNoTests = false + config.Fuzzing.Testing.AssertionTesting.Enabled = false + config.Fuzzing.Testing.OptimizationTesting.Enabled = false }, method: func(f *fuzzerTestContext) { // Subscribe to any mined block events globally. When receiving them, check contract changes for a @@ -424,6 +446,8 @@ func TestExecutionTraces(t *testing.T) { filePath: filePath, configUpdates: func(config *config.ProjectConfig) { config.Fuzzing.DeploymentOrder = []string{"TestContract"} + config.Fuzzing.Testing.PropertyTesting.Enabled = false + config.Fuzzing.Testing.OptimizationTesting.Enabled = false }, method: func(f *fuzzerTestContext) { // Start the fuzzer @@ -465,6 +489,7 @@ func TestTestingScope(t *testing.T) { config.Fuzzing.TestLimit = 1_000 // this test should expose a failure quickly. config.Fuzzing.Testing.TestAllContracts = testingAllContracts config.Fuzzing.Testing.StopOnFailedTest = false + config.Fuzzing.Testing.OptimizationTesting.Enabled = false }, method: func(f *fuzzerTestContext) { // Start the fuzzer @@ -510,6 +535,8 @@ func TestDeploymentsWithArgs(t *testing.T) { } config.Fuzzing.Testing.StopOnFailedTest = false config.Fuzzing.TestLimit = 500 // this test should expose a failure quickly. + config.Fuzzing.Testing.AssertionTesting.Enabled = false + config.Fuzzing.Testing.OptimizationTesting.Enabled = false }, method: func(f *fuzzerTestContext) { // Start the fuzzer @@ -529,6 +556,8 @@ func TestValueGenerationGenerateAllTypes(t *testing.T) { configUpdates: func(config *config.ProjectConfig) { config.Fuzzing.DeploymentOrder = []string{"GenerateAllTypes"} config.Fuzzing.TestLimit = 10_000 + config.Fuzzing.Testing.AssertionTesting.Enabled = false + config.Fuzzing.Testing.OptimizationTesting.Enabled = false }, method: func(f *fuzzerTestContext) { // Start the fuzzer @@ -561,6 +590,8 @@ func TestValueGenerationSolving(t *testing.T) { filePath: filePath, configUpdates: func(config *config.ProjectConfig) { config.Fuzzing.DeploymentOrder = []string{"TestContract"} + config.Fuzzing.Testing.AssertionTesting.Enabled = false + config.Fuzzing.Testing.OptimizationTesting.Enabled = false }, method: func(f *fuzzerTestContext) { // Start the fuzzer @@ -616,6 +647,8 @@ func TestASTValueExtraction(t *testing.T) { filePath: "testdata/contracts/value_generation/ast_value_extraction.sol", configUpdates: func(config *config.ProjectConfig) { config.Fuzzing.TestLimit = 1 // stop immediately to simply see what values were mined. + config.Fuzzing.Testing.PropertyTesting.Enabled = false + config.Fuzzing.Testing.OptimizationTesting.Enabled = false }, method: func(f *fuzzerTestContext) { // Start the fuzzer @@ -652,6 +685,8 @@ func TestVMCorrectness(t *testing.T) { config.Fuzzing.DeploymentOrder = []string{"TestContract"} config.Fuzzing.MaxBlockTimestampDelay = 1 // this contract require calls every block config.Fuzzing.MaxBlockNumberDelay = 1 // this contract require calls every block + config.Fuzzing.Testing.AssertionTesting.Enabled = false + config.Fuzzing.Testing.OptimizationTesting.Enabled = false }, method: func(f *fuzzerTestContext) { // Start the fuzzer @@ -714,6 +749,8 @@ func TestCorpusReplayability(t *testing.T) { configUpdates: func(config *config.ProjectConfig) { config.Fuzzing.DeploymentOrder = []string{"TestContract"} config.Fuzzing.CorpusDirectory = "corpus" + config.Fuzzing.Testing.AssertionTesting.Enabled = false + config.Fuzzing.Testing.OptimizationTesting.Enabled = false }, method: func(f *fuzzerTestContext) { // Setup checks for event emissions @@ -759,6 +796,8 @@ func TestDeploymentOrderWithCoverage(t *testing.T) { filePath: "testdata/contracts/deployments/deployment_order.sol", configUpdates: func(config *config.ProjectConfig) { config.Fuzzing.DeploymentOrder = []string{"InheritedFirstContract", "InheritedSecondContract"} + config.Fuzzing.Testing.AssertionTesting.Enabled = false + config.Fuzzing.Testing.OptimizationTesting.Enabled = false }, method: func(f *fuzzerTestContext) { // Setup checks for event emissions diff --git a/fuzzing/test_case_assertion_provider.go b/fuzzing/test_case_assertion_provider.go index d4ec95e9..d4390c4e 100644 --- a/fuzzing/test_case_assertion_provider.go +++ b/fuzzing/test_case_assertion_provider.go @@ -46,17 +46,17 @@ func attachAssertionTestCaseProvider(fuzzer *Fuzzer) *AssertionTestCaseProvider // Returns true if this target should be tested, false otherwise. func (t *AssertionTestCaseProvider) isTestableMethod(method abi.Method) bool { // Do not test optimization tests - if utils.IsOptimizationTest(method, t.fuzzer.config.Fuzzing.Testing.OptimizationTestPrefixes) { + if utils.IsOptimizationTest(method, t.fuzzer.config.Fuzzing.Testing.OptimizationTesting.TestPrefixes) { return false } // Do not test property tests - if utils.IsPropertyTest(method, t.fuzzer.config.Fuzzing.Testing.InvariantTestPrefixes) { + if utils.IsPropertyTest(method, t.fuzzer.config.Fuzzing.Testing.PropertyTesting.TestPrefixes) { return false } // Only test constant methods (pure/view) if we are configured to. - return !method.IsConstant() || t.fuzzer.config.Fuzzing.Testing.TestViewMethods + return !method.IsConstant() || t.fuzzer.config.Fuzzing.Testing.AssertionTesting.TestViewMethods } // checkAssertionFailures checks the results of the last call for assertion failures. @@ -84,7 +84,7 @@ func (t *AssertionTestCaseProvider) checkAssertionFailures(callSequence calls.Ca panicCode := abiutils.GetSolidityPanicCode(lastExecutionResult.Err, lastExecutionResult.ReturnData, true) failure := false if panicCode != nil { - failure = encounteredAssertionFailure(panicCode.Uint64(), t.fuzzer.config.Fuzzing.Testing.PanicCodeConfig) + failure = encounteredAssertionFailure(panicCode.Uint64(), t.fuzzer.config.Fuzzing.Testing.AssertionTesting.PanicCodeConfig) } return &methodId, failure, nil diff --git a/fuzzing/test_case_optimization_provider.go b/fuzzing/test_case_optimization_provider.go index 9bcaff83..85afc29c 100644 --- a/fuzzing/test_case_optimization_provider.go +++ b/fuzzing/test_case_optimization_provider.go @@ -44,11 +44,6 @@ type optimizationTestCaseProviderWorkerState struct { // attachOptimizationTestCaseProvider attaches a new OptimizationTestCaseProvider to the Fuzzer and returns it. func attachOptimizationTestCaseProvider(fuzzer *Fuzzer) *OptimizationTestCaseProvider { - // If there are no optimization test prefixes, don't create a test case provider and don't subscribe to any events - if len(fuzzer.config.Fuzzing.Testing.OptimizationTestPrefixes) == 0 { - return nil - } - // Create a test case provider t := &OptimizationTestCaseProvider{ fuzzer: fuzzer, @@ -132,7 +127,7 @@ func (t *OptimizationTestCaseProvider) onFuzzerStarting(event FuzzerStartingEven for _, contract := range t.fuzzer.ContractDefinitions() { for _, method := range contract.CompiledContract().Abi.Methods { // Verify this method is an optimization test method - if !utils.IsOptimizationTest(method, t.fuzzer.config.Fuzzing.Testing.OptimizationTestPrefixes) { + if !utils.IsOptimizationTest(method, t.fuzzer.config.Fuzzing.Testing.OptimizationTesting.TestPrefixes) { continue } // Create local variables to avoid pointer types in the loop being overridden. diff --git a/fuzzing/test_case_property_provider.go b/fuzzing/test_case_property_provider.go index 284d87b1..188dedca 100644 --- a/fuzzing/test_case_property_provider.go +++ b/fuzzing/test_case_property_provider.go @@ -46,11 +46,6 @@ type propertyTestCaseProviderWorkerState struct { // attachPropertyTestCaseProvider attaches a new PropertyTestCaseProvider to the Fuzzer and returns it. func attachPropertyTestCaseProvider(fuzzer *Fuzzer) *PropertyTestCaseProvider { - // If there are no property test prefixes, don't create a test case provider and don't subscribe to any events - if len(fuzzer.config.Fuzzing.Testing.InvariantTestPrefixes) == 0 { - return nil - } - // Create a test case provider t := &PropertyTestCaseProvider{ fuzzer: fuzzer, @@ -140,7 +135,7 @@ func (t *PropertyTestCaseProvider) onFuzzerStarting(event FuzzerStartingEvent) e for _, method := range contract.CompiledContract().Abi.Methods { // Verify this method is a property test method - if !utils.IsPropertyTest(method, t.fuzzer.config.Fuzzing.Testing.InvariantTestPrefixes) { + if !utils.IsPropertyTest(method, t.fuzzer.config.Fuzzing.Testing.PropertyTesting.TestPrefixes) { continue } diff --git a/fuzzing/testdata/contracts/assertions/assert_and_property_test.sol b/fuzzing/testdata/contracts/assertions/assert_and_property_test.sol index b7cdc1dc..03100042 100644 --- a/fuzzing/testdata/contracts/assertions/assert_and_property_test.sol +++ b/fuzzing/testdata/contracts/assertions/assert_and_property_test.sol @@ -5,7 +5,7 @@ contract TestContract { assert(false); } - function invariant_failing_property() public view returns (bool) { + function property_failing_property() public view returns (bool) { // ASSERTION: fail immediately. return false; } diff --git a/fuzzing/testdata/contracts/chain/tx_out_of_gas.sol b/fuzzing/testdata/contracts/chain/tx_out_of_gas.sol index ec623084..6054a7d8 100644 --- a/fuzzing/testdata/contracts/chain/tx_out_of_gas.sol +++ b/fuzzing/testdata/contracts/chain/tx_out_of_gas.sol @@ -10,7 +10,7 @@ contract TestContract { } } - function invariant_never_apply_state_when_oog() public view returns (bool) { + function property_never_apply_state_when_oog() public view returns (bool) { // ASSERTION: this state should never be applied, as our out of gas error should revert changes. return x == 0; } diff --git a/fuzzing/testdata/contracts/corpus_mutation/specific_call_sequence.sol b/fuzzing/testdata/contracts/corpus_mutation/specific_call_sequence.sol index 2ccc1cbe..b4356dac 100644 --- a/fuzzing/testdata/contracts/corpus_mutation/specific_call_sequence.sol +++ b/fuzzing/testdata/contracts/corpus_mutation/specific_call_sequence.sol @@ -29,7 +29,7 @@ contract TestContract { } } - function invariant_solve_me() public view returns (bool) { + function property_solve_me() public view returns (bool) { // ASSERTION: The fuzzer should be able to fail this test case and solve all challenges. return index < 7; } diff --git a/fuzzing/testdata/contracts/deployments/deployment_order.sol b/fuzzing/testdata/contracts/deployments/deployment_order.sol index e21c20a5..f7c6f3ed 100644 --- a/fuzzing/testdata/contracts/deployments/deployment_order.sol +++ b/fuzzing/testdata/contracts/deployments/deployment_order.sol @@ -15,7 +15,7 @@ contract InheritedFirstContract is FirstContract { y = value + 9; } - function invariant_never_specific_values() public view returns (bool) { + function property_never_specific_values() public view returns (bool) { // ASSERTION: x should never be 10 at the same time y is 80 return !(x == 10 && y == 80); } @@ -41,7 +41,7 @@ contract InheritedSecondContract is SecondContract { c = value + 7; } - function invariant_never_specific_values() public view returns (bool) { + function property_never_specific_values() public view returns (bool) { // ASSERTION: a should never be 10 at the same time b is 80 at the same time c is 14 return !(a == 10 && b == 80 && c == 14); } diff --git a/fuzzing/testdata/contracts/deployments/deployment_with_args.sol b/fuzzing/testdata/contracts/deployments/deployment_with_args.sol index 397e4b2a..05991266 100644 --- a/fuzzing/testdata/contracts/deployments/deployment_with_args.sol +++ b/fuzzing/testdata/contracts/deployments/deployment_with_args.sol @@ -15,15 +15,15 @@ contract DeploymentWithArgs { z = _z; } - function invariant_checkX() public returns (bool) { + function property_checkX() public returns (bool) { return x != 123456789; } - function invariant_checkY() public returns (bool) { + function property_checkY() public returns (bool) { return y != 0x5465; } - function invariant_checkZ() public returns (bool) { + function property_checkZ() public returns (bool) { return z.a != 0x4d2; } @@ -40,7 +40,7 @@ contract Dependent { deployed = _deployed; } - function invariant_checkDeployed() public returns (bool) { + function property_checkDeployed() public returns (bool) { return deployed == 0x0000000000000000000000000000000000000000; } diff --git a/fuzzing/testdata/contracts/deployments/inner_deployment.sol b/fuzzing/testdata/contracts/deployments/inner_deployment.sol index 46ccebe5..dc26b5b7 100644 --- a/fuzzing/testdata/contracts/deployments/inner_deployment.sol +++ b/fuzzing/testdata/contracts/deployments/inner_deployment.sol @@ -1,7 +1,7 @@ // InnerDeploymentFactory deploys InnerDeployment when a method is called after deployment, and verifies the fuzzer can // match bytecode and fail the test appropriately. contract InnerDeployment { - function invariant_inner_deployment() public view returns (bool) { + function property_inner_deployment() public view returns (bool) { // ASSERTION: Fail immediately. return false; } diff --git a/fuzzing/testdata/contracts/deployments/inner_deployment_on_construction.sol b/fuzzing/testdata/contracts/deployments/inner_deployment_on_construction.sol index 93f10d03..4e08a111 100644 --- a/fuzzing/testdata/contracts/deployments/inner_deployment_on_construction.sol +++ b/fuzzing/testdata/contracts/deployments/inner_deployment_on_construction.sol @@ -6,7 +6,7 @@ contract InnerDeployment { x = 7; } - function invariant_inner_deployment() public view returns (bool) { + function property_inner_deployment() public view returns (bool) { // ASSERTION: Fail immediately. return false; } diff --git a/fuzzing/testdata/contracts/deployments/inner_inner_deployment.sol b/fuzzing/testdata/contracts/deployments/inner_inner_deployment.sol index 9de74545..7718fc82 100644 --- a/fuzzing/testdata/contracts/deployments/inner_inner_deployment.sol +++ b/fuzzing/testdata/contracts/deployments/inner_inner_deployment.sol @@ -2,7 +2,7 @@ // deployed, a method can be used to deploy an InnerInnerDeployment. We verify we can violate an invariant // in a two-layer deep dynamic deployment. contract InnerInnerDeployment { - function invariant_inner_inner_deployment() public view returns (bool) { + function property_inner_inner_deployment() public view returns (bool) { // ASSERTION: Fail immediately. return false; } diff --git a/fuzzing/testdata/contracts/deployments/internal_library.sol b/fuzzing/testdata/contracts/deployments/internal_library.sol index 35f52a69..27201d43 100644 --- a/fuzzing/testdata/contracts/deployments/internal_library.sol +++ b/fuzzing/testdata/contracts/deployments/internal_library.sol @@ -28,7 +28,7 @@ contract TestInternalLibrary { return a + b; } - function invariant_library_linking_broken() public view returns (bool) { + function property_library_linking_broken() public view returns (bool) { // ASSERTION: We should always be able to compute correctly. return !failedTest; } diff --git a/fuzzing/testdata/contracts/deployments/testing_scope.sol b/fuzzing/testdata/contracts/deployments/testing_scope.sol index 113e650e..e98aef38 100644 --- a/fuzzing/testdata/contracts/deployments/testing_scope.sol +++ b/fuzzing/testdata/contracts/deployments/testing_scope.sol @@ -6,7 +6,7 @@ contract TestContractChild { assert(false); } - function invariant_failing_property_test_method_child() public view returns (bool) { + function property_failing_property_test_method_child() public view returns (bool) { return false; } } @@ -22,7 +22,7 @@ contract TestContract { assert(false); } - function invariant_failing_property_test_method() public view returns (bool) { + function property_failing_property_test_method() public view returns (bool) { return false; } } diff --git a/fuzzing/testdata/contracts/value_generation/generate_all_types.sol b/fuzzing/testdata/contracts/value_generation/generate_all_types.sol index 8e887963..e9a6fa75 100644 --- a/fuzzing/testdata/contracts/value_generation/generate_all_types.sol +++ b/fuzzing/testdata/contracts/value_generation/generate_all_types.sol @@ -24,7 +24,7 @@ contract GenerateAllTypes { s = ""; } - function invariant_never_fail() public view returns (bool) { + function property_never_fail() public view returns (bool) { // ASSERTION: never fail, to keep testing value generation return true; } diff --git a/fuzzing/testdata/contracts/value_generation/match_addr_contract.sol b/fuzzing/testdata/contracts/value_generation/match_addr_contract.sol index da3de1f4..9a5cdb7e 100644 --- a/fuzzing/testdata/contracts/value_generation/match_addr_contract.sol +++ b/fuzzing/testdata/contracts/value_generation/match_addr_contract.sol @@ -6,7 +6,7 @@ contract TestContract { a = value; } - function invariant_never_specific_values() public view returns (bool) { + function property_never_specific_values() public view returns (bool) { // ASSERTION: a should not be the contract's address itself. return !(a == address(this)); } diff --git a/fuzzing/testdata/contracts/value_generation/match_addr_exact.sol b/fuzzing/testdata/contracts/value_generation/match_addr_exact.sol index 89cf70e1..b46d594e 100644 --- a/fuzzing/testdata/contracts/value_generation/match_addr_exact.sol +++ b/fuzzing/testdata/contracts/value_generation/match_addr_exact.sol @@ -12,7 +12,7 @@ contract TestContract { y = value; } - function invariant_never_specific_values() public view returns (bool) { + function property_never_specific_values() public view returns (bool) { // ASSERTION: x and y should not equal the exact addresses below. return !(x == address(0x12345) && y == address(7)); } diff --git a/fuzzing/testdata/contracts/value_generation/match_addr_sender.sol b/fuzzing/testdata/contracts/value_generation/match_addr_sender.sol index 4720f2f8..b67d80e4 100644 --- a/fuzzing/testdata/contracts/value_generation/match_addr_sender.sol +++ b/fuzzing/testdata/contracts/value_generation/match_addr_sender.sol @@ -8,7 +8,7 @@ contract TestContract { sender = msg.sender; } - function invariant_never_specific_values() public view returns (bool) { + function property_never_specific_values() public view returns (bool) { // ASSERTION: a should not be sender's address who set it. return a != sender; } diff --git a/fuzzing/testdata/contracts/value_generation/match_ints_xy.sol b/fuzzing/testdata/contracts/value_generation/match_ints_xy.sol index 679d5387..3aeff78e 100644 --- a/fuzzing/testdata/contracts/value_generation/match_ints_xy.sol +++ b/fuzzing/testdata/contracts/value_generation/match_ints_xy.sol @@ -12,7 +12,7 @@ contract TestContract { } - function invariant_never_specific_values() public view returns (bool) { + function property_never_specific_values() public view returns (bool) { // ASSERTION: x should never be -10 at the same time y is -62 return !(x == -10 && y == -62); } diff --git a/fuzzing/testdata/contracts/value_generation/match_payable_xy.sol b/fuzzing/testdata/contracts/value_generation/match_payable_xy.sol index 74e34592..b885c42e 100644 --- a/fuzzing/testdata/contracts/value_generation/match_payable_xy.sol +++ b/fuzzing/testdata/contracts/value_generation/match_payable_xy.sol @@ -11,7 +11,7 @@ contract TestContract { paidAmount2 = msg.value; } - function invariant_never_pay_exact_amounts() public view returns (bool) { + function property_never_pay_exact_amounts() public view returns (bool) { // ASSERTION: paid amounts should never equal the exact numbers below. return !(paidAmount == 7777 && paidAmount2 == 8888); } diff --git a/fuzzing/testdata/contracts/value_generation/match_string_exact.sol b/fuzzing/testdata/contracts/value_generation/match_string_exact.sol index ad2799fc..80f15fae 100644 --- a/fuzzing/testdata/contracts/value_generation/match_string_exact.sol +++ b/fuzzing/testdata/contracts/value_generation/match_string_exact.sol @@ -6,7 +6,7 @@ contract TestContract { s = value; } - function invariant_never_specific_values() public view returns (bool) { + function property_never_specific_values() public view returns (bool) { // ASSERTION: s should not be the MAGIC_STRING return keccak256(abi.encodePacked((s))) != keccak256(abi.encodePacked((MAGIC_STRING))); } diff --git a/fuzzing/testdata/contracts/value_generation/match_structs_xy.sol b/fuzzing/testdata/contracts/value_generation/match_structs_xy.sol index 8c296b52..817e079b 100644 --- a/fuzzing/testdata/contracts/value_generation/match_structs_xy.sol +++ b/fuzzing/testdata/contracts/value_generation/match_structs_xy.sol @@ -23,7 +23,7 @@ contract TestContract { s = ts; } - function invariant_never_specific_values() public view returns (bool) { + function property_never_specific_values() public view returns (bool) { // ASSERTION: x should never be 10 at the same time y is 80 return !(s.x == 10 && s.i.y == 80); } diff --git a/fuzzing/testdata/contracts/value_generation/match_uints_xy.sol b/fuzzing/testdata/contracts/value_generation/match_uints_xy.sol index a955f8e3..d465708a 100644 --- a/fuzzing/testdata/contracts/value_generation/match_uints_xy.sol +++ b/fuzzing/testdata/contracts/value_generation/match_uints_xy.sol @@ -12,7 +12,7 @@ contract TestContract { } - function invariant_never_specific_values() public view returns (bool) { + function property_never_specific_values() public view returns (bool) { // ASSERTION: x should never be 10 at the same time y is 80 return !(x == 10 && y == 80); } diff --git a/fuzzing/testdata/contracts/vm_tests/block_hash_store_check.sol b/fuzzing/testdata/contracts/vm_tests/block_hash_store_check.sol index 47eb56d8..cbc757b8 100644 --- a/fuzzing/testdata/contracts/vm_tests/block_hash_store_check.sol +++ b/fuzzing/testdata/contracts/vm_tests/block_hash_store_check.sol @@ -49,7 +49,7 @@ contract TestContract { lastBlockNumber = block.number; } - function invariant_violate_block_hash_continuity() public view returns (bool) { + function property_violate_block_hash_continuity() public view returns (bool) { // ASSERTION: we fail if our blockHash works as expected so our fuzzer will catch it. return !failedTest; } diff --git a/fuzzing/testdata/contracts/vm_tests/block_number_increasing.sol b/fuzzing/testdata/contracts/vm_tests/block_number_increasing.sol index fee29dfc..b8a6450c 100644 --- a/fuzzing/testdata/contracts/vm_tests/block_number_increasing.sol +++ b/fuzzing/testdata/contracts/vm_tests/block_number_increasing.sol @@ -10,7 +10,7 @@ contract TestContract { // This method does nothing but is left exposed so it can be called by the fuzzer to advance block.number } - function invariant_increase_block_number_by_10() public view returns (bool) { + function property_increase_block_number_by_10() public view returns (bool) { // ASSERTION: block number should never increase more than 10 (we expect failure) return !(block.number - startingBlockNumber >= 10); } diff --git a/fuzzing/testdata/contracts/vm_tests/block_timestamp_increasing.sol b/fuzzing/testdata/contracts/vm_tests/block_timestamp_increasing.sol index 20abd19c..7d6b0961 100644 --- a/fuzzing/testdata/contracts/vm_tests/block_timestamp_increasing.sol +++ b/fuzzing/testdata/contracts/vm_tests/block_timestamp_increasing.sol @@ -10,7 +10,7 @@ contract TestContract { // This method does nothing but is left exposed so it can be called by the fuzzer to advance blocks/timestamps. } - function invariant_increase_block_timestamp() public view returns (bool) { + function property_increase_block_timestamp() public view returns (bool) { // ASSERTION: block timestamp should never increase more than 10 (we expect failure) return !(block.timestamp - startingBlockTimestamp >= 10); } diff --git a/utils/testing_provider_utils.go b/utils/testing_provider_utils.go index bd0aefea..1174246b 100644 --- a/utils/testing_provider_utils.go +++ b/utils/testing_provider_utils.go @@ -25,10 +25,9 @@ func IsOptimizationTest(method abi.Method, prefixes []string) bool { func IsPropertyTest(method abi.Method, prefixes []string) bool { // Loop through all enabled prefixes to find a match for _, prefix := range prefixes { - if strings.HasPrefix(method.Name, prefix) { - if len(method.Inputs) == 0 && len(method.Outputs) == 1 && method.Outputs[0].Type.T == abi.BoolTy { - return true - } + // The property test must simply have the right prefix and take no inputs + if strings.HasPrefix(method.Name, prefix) && len(method.Inputs) == 0 { + return true } } return false From 1a2c3e1c046e27e8ea91c4008b379b94e9eda5d5 Mon Sep 17 00:00:00 2001 From: Anish Naik Date: Wed, 30 Aug 2023 14:10:33 -0400 Subject: [PATCH 03/14] add verification for config --- fuzzing/config/config.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/fuzzing/config/config.go b/fuzzing/config/config.go index 32ed9f87..bed3277d 100644 --- a/fuzzing/config/config.go +++ b/fuzzing/config/config.go @@ -283,6 +283,22 @@ func (p *ProjectConfig) Validate() error { return errors.New("project configuration must specify only a well-formed deployer address") } + // Verify property testing fields. + if p.Fuzzing.Testing.PropertyTesting.Enabled { + // Test prefixes must be supplied if property testing is enabled. + if len(p.Fuzzing.Testing.PropertyTesting.TestPrefixes) == 0 { + return errors.New("project configuration must specify test name prefixes if property testing is enabled") + } + } + + // Verify optimization testing fields. + if p.Fuzzing.Testing.OptimizationTesting.Enabled { + // Test prefixes must be supplied if property testing is enabled. + if len(p.Fuzzing.Testing.OptimizationTesting.TestPrefixes) == 0 { + return errors.New("project configuration must specify test name prefixes if optimization testing is enabled") + } + } + // Ensure that the log level is a valid one if _, err := zerolog.ParseLevel(p.Logging.Level.String()); err != nil { return err From 795118c56781ed6653b36b6031cb03011afaff70 Mon Sep 17 00:00:00 2001 From: Anish Naik Date: Wed, 30 Aug 2023 15:18:06 -0400 Subject: [PATCH 04/14] improve config-related errors --- fuzzing/config/config.go | 57 ++++++++++++++++++++++++++++++++-------- fuzzing/fuzzer.go | 20 +++++++++----- 2 files changed, 59 insertions(+), 18 deletions(-) diff --git a/fuzzing/config/config.go b/fuzzing/config/config.go index bed3277d..0d03952a 100644 --- a/fuzzing/config/config.go +++ b/fuzzing/config/config.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "github.com/crytic/medusa/chain/config" + "github.com/crytic/medusa/logging" "github.com/rs/zerolog" "os" @@ -250,44 +251,75 @@ func (p *ProjectConfig) WriteToFile(path string) error { // Validate validates that the ProjectConfig meets certain requirements. // Returns an error if one occurs. func (p *ProjectConfig) Validate() error { + // Create logger instance if global logger is available + logger := logging.NewLogger(zerolog.Disabled) + if logging.GlobalLogger != nil { + logger = logging.GlobalLogger.NewSubLogger("module", "fuzzer config") + } + // Verify the worker count is a positive number. if p.Fuzzing.Workers <= 0 { - return errors.New("project configuration must specify a positive number for the worker count") + return errors.New("worker count must be a positive number (update fuzzing.workers in the project config " + + "or use the --workers CLI flag)") } // Verify that the sequence length is a positive number if p.Fuzzing.CallSequenceLength <= 0 { - return errors.New("project configuration must specify a positive number for the transaction sequence length") + return errors.New("call sequence length must be a positive number (update fuzzing.callSequenceLength in " + + "the project config or use the --seq-len CLI flag)") } // Verify the worker reset limit is a positive number if p.Fuzzing.WorkerResetLimit <= 0 { - return errors.New("project configuration must specify a positive number for the worker reset limit") + return errors.New("worker reset limit must be a positive number (update fuzzing.workerResetLimit in the " + + "project config)") + } + + // Verify timeout + if p.Fuzzing.Timeout < 0 { + return errors.New("timeout must be a positive number (update fuzzing.timeout in the project config or " + + "use the --timeout CLI flag)") } // Verify gas limits are appropriate if p.Fuzzing.BlockGasLimit < p.Fuzzing.TransactionGasLimit { - return errors.New("project configuration must specify a block gas limit which is not less than the transaction gas limit") + return errors.New("project config must specify a block gas limit which is not less than the transaction gas limit") } if p.Fuzzing.BlockGasLimit == 0 || p.Fuzzing.TransactionGasLimit == 0 { - return errors.New("project configuration must specify a block and transaction gas limit which is non-zero") + return errors.New("project config must specify a block and transaction gas limit which are non-zero") + } + + // Log warning if max block delay is zero + if p.Fuzzing.MaxBlockNumberDelay == 0 { + logger.Warn("The maximum block number delay is set to zero. Please be aware that transactions will " + + "always be fit in the same block until the block gas limit is reached and that the block number will always " + + "increment by one.") + } + + // Log warning if max timestamp delay is zero + if p.Fuzzing.MaxBlockTimestampDelay == 0 { + logger.Warn("The maximum timestamp delay is set to zero. Please be aware that block time jumps will " + + "always be exactly one.") } // Verify that senders are well-formed addresses if _, err := utils.HexStringsToAddresses(p.Fuzzing.SenderAddresses); err != nil { - return errors.New("project configuration must specify only well-formed sender address(es)") + return errors.New("sender address(es) must be well-formed (update fuzzing.senderAddresses in the project " + + "config or use the --senders CLI flag)") } // Verify that deployer is a well-formed address if _, err := utils.HexStringToAddress(p.Fuzzing.DeployerAddress); err != nil { - return errors.New("project configuration must specify only a well-formed deployer address") + return errors.New("deployer address must be well-formed (update fuzzing.deployerAddress in the project " + + "config or use the --deployer CLI flag)") } // Verify property testing fields. if p.Fuzzing.Testing.PropertyTesting.Enabled { // Test prefixes must be supplied if property testing is enabled. if len(p.Fuzzing.Testing.PropertyTesting.TestPrefixes) == 0 { - return errors.New("project configuration must specify test name prefixes if property testing is enabled") + return errors.New("project config must specify test prefixes if property testing is enabled " + + "(update fuzzing.testing.propertyTesting.testPrefixes)") } } @@ -295,13 +327,16 @@ func (p *ProjectConfig) Validate() error { if p.Fuzzing.Testing.OptimizationTesting.Enabled { // Test prefixes must be supplied if property testing is enabled. if len(p.Fuzzing.Testing.OptimizationTesting.TestPrefixes) == 0 { - return errors.New("project configuration must specify test name prefixes if optimization testing is enabled") + return errors.New("project config must specify test prefixes if optimization testing is enabled" + + "(update fuzzing.testing.optimizationTesting.testPrefixes)") } + } // Ensure that the log level is a valid one - if _, err := zerolog.ParseLevel(p.Logging.Level.String()); err != nil { - return err + level, err := zerolog.ParseLevel(p.Logging.Level.String()) + if err != nil || level == zerolog.FatalLevel { + return errors.New("project config must specify a valid log level (trace, debug, info, warn, error, or panic)") } return nil diff --git a/fuzzing/fuzzer.go b/fuzzing/fuzzer.go index 3e609493..f1ca6465 100644 --- a/fuzzing/fuzzer.go +++ b/fuzzing/fuzzer.go @@ -89,7 +89,8 @@ type Fuzzer struct { // while initializing the code. func NewFuzzer(config config.ProjectConfig) (*Fuzzer, error) { // Create the global logger and add stdout as an unstructured, colored output stream - logging.GlobalLogger = logging.NewLogger(config.Logging.Level) + // Note that we are not using the project config's log level because we have not validated it yet + logging.GlobalLogger = logging.NewLogger(zerolog.InfoLevel) logging.GlobalLogger.AddWriter(os.Stdout, logging.UNSTRUCTURED, true) // If the log directory is a non-empty string, create a file for unstructured, un-colorized file logging @@ -105,16 +106,19 @@ func NewFuzzer(config config.ProjectConfig) (*Fuzzer, error) { logging.GlobalLogger.AddWriter(file, logging.UNSTRUCTURED, false) } - // Get the fuzzer's custom sub-logger - logger := logging.GlobalLogger.NewSubLogger("module", "fuzzer") - // Validate our provided config err := config.Validate() if err != nil { - logger.Error("Invalid configuration", err) + logging.GlobalLogger.Error("Invalid configuration", err) return nil, err } + // Update the log level of the global logger now + logging.GlobalLogger.SetLevel(config.Logging.Level) + + // Get the fuzzer's custom sub-logger + logger := logging.GlobalLogger.NewSubLogger("module", "fuzzer") + // Parse the senders addresses from our account config. senders, err := utils.HexStringsToAddresses(config.Fuzzing.SenderAddresses) if err != nil { @@ -332,7 +336,8 @@ func chainSetupFromCompilations(fuzzer *Fuzzer, testChain *chain.TestChain) erro if len(fuzzer.contractDefinitions) == 1 { fuzzer.config.Fuzzing.DeploymentOrder = []string{fuzzer.contractDefinitions[0].Name()} } else { - return fmt.Errorf("you must specify a contract deployment order within your project configuration") + return fmt.Errorf("missing deployment order (update fuzzing.deploymentOrder in the project config " + + "or use the --deployment-order CLI flag)") } } @@ -406,7 +411,8 @@ func chainSetupFromCompilations(fuzzer *Fuzzer, testChain *chain.TestChain) erro // If we did not find a contract corresponding to this item in the deployment order, we throw an error. if !found { - return fmt.Errorf("DeploymentOrder specified a contract name which was not found in the compilation: %v\n", contractName) + return fmt.Errorf("%v was specified in the deployment order (see fuzzing.deploymentOrder in the "+ + "project config or the --deployment-order CLI flag) but was not found in the compilation artifacts", contractName) } } return nil From a757cbc27f1450a617bda608786341a3517466dd Mon Sep 17 00:00:00 2001 From: Anish Naik Date: Wed, 30 Aug 2023 16:11:00 -0400 Subject: [PATCH 05/14] update deploymentOrder to targetContracts --- cmd/fuzz_flags.go | 22 ++++----- cmd/init_flags.go | 10 ++-- fuzzing/config/config.go | 7 +-- fuzzing/config/config_defaults.go | 2 +- fuzzing/fuzzer.go | 20 ++++---- fuzzing/fuzzer_test.go | 55 +++++++++++----------- fuzzing/test_case_assertion_provider.go | 4 +- fuzzing/test_case_optimization_provider.go | 6 +++ fuzzing/test_case_property_provider.go | 4 +- 9 files changed, 69 insertions(+), 61 deletions(-) diff --git a/cmd/fuzz_flags.go b/cmd/fuzz_flags.go index 0a6fb08d..e9f2b859 100644 --- a/cmd/fuzz_flags.go +++ b/cmd/fuzz_flags.go @@ -21,8 +21,8 @@ func addFuzzFlags() error { // Config file fuzzCmd.Flags().String("config", "", "path to config file") - // Target - fuzzCmd.Flags().String("target", "", TargetFlagDescription) + // Compilation Target + fuzzCmd.Flags().String("compilation-target", "", TargetFlagDescription) // Number of workers fuzzCmd.Flags().Int("workers", 0, @@ -40,9 +40,9 @@ func addFuzzFlags() error { fuzzCmd.Flags().Int("seq-len", 0, fmt.Sprintf("maximum transactions to run in sequence (unless a config file is provided, default is %d)", defaultConfig.Fuzzing.CallSequenceLength)) - // Deployment order - fuzzCmd.Flags().StringSlice("deployment-order", []string{}, - fmt.Sprintf("order in which to deploy target contracts (unless a config file is provided, default is %v)", defaultConfig.Fuzzing.DeploymentOrder)) + // Target contracts + fuzzCmd.Flags().StringSlice("target-contracts", []string{}, + fmt.Sprintf("target contracts for fuzz testing (unless a config file is provided, default is %v)", defaultConfig.Fuzzing.TargetContracts)) // Corpus directory fuzzCmd.Flags().String("corpus-dir", "", @@ -66,10 +66,10 @@ func addFuzzFlags() error { func updateProjectConfigWithFuzzFlags(cmd *cobra.Command, projectConfig *config.ProjectConfig) error { var err error - // If --target was used - if cmd.Flags().Changed("target") { + // If --compilation-target was used + if cmd.Flags().Changed("compilation-target") { // Get the new target - newTarget, err := cmd.Flags().GetString("target") + newTarget, err := cmd.Flags().GetString("compilation-target") if err != nil { return err } @@ -112,9 +112,9 @@ func updateProjectConfigWithFuzzFlags(cmd *cobra.Command, projectConfig *config. } } - // Update deployment order - if cmd.Flags().Changed("deployment-order") { - projectConfig.Fuzzing.DeploymentOrder, err = cmd.Flags().GetStringSlice("deployment-order") + // Update target contracts + if cmd.Flags().Changed("target-contracts") { + projectConfig.Fuzzing.TargetContracts, err = cmd.Flags().GetStringSlice("target-contracts") if err != nil { return err } diff --git a/cmd/init_flags.go b/cmd/init_flags.go index 40c3499a..dfe6d8d6 100644 --- a/cmd/init_flags.go +++ b/cmd/init_flags.go @@ -10,18 +10,18 @@ func addInitFlags() error { // Output path for configuration initCmd.Flags().String("out", "", "output path for the new project configuration file") - // Target file / directory - initCmd.Flags().String("target", "", TargetFlagDescription) + // Target file / directory for compilation + initCmd.Flags().String("compilation-target", "", TargetFlagDescription) return nil } // updateProjectConfigWithInitFlags will update the given projectConfig with any CLI arguments that were provided to the init command func updateProjectConfigWithInitFlags(cmd *cobra.Command, projectConfig *config.ProjectConfig) error { - // If --target was used - if cmd.Flags().Changed("target") { + // If --compilation-target was used + if cmd.Flags().Changed("compilation-target") { // Get the new target - newTarget, err := cmd.Flags().GetString("target") + newTarget, err := cmd.Flags().GetString("compilation-target") if err != nil { return err } diff --git a/fuzzing/config/config.go b/fuzzing/config/config.go index 0d03952a..a8e0dde7 100644 --- a/fuzzing/config/config.go +++ b/fuzzing/config/config.go @@ -50,10 +50,11 @@ type FuzzingConfig struct { // CoverageEnabled describes whether to use coverage-guided fuzzing CoverageEnabled bool `json:"coverageEnabled"` - // DeploymentOrder determines the order in which the contracts should be deployed - DeploymentOrder []string `json:"deploymentOrder"` + // TargetContracts are the target contracts for fuzz testing + TargetContracts []string `json:"targetContracts"` - // Constructor arguments for contracts deployment. It is available only in init mode + // ConstructorArgs holds the constructor arguments for TargetContracts deployments. It is available via the project + // configuration ConstructorArgs map[string]map[string]any `json:"constructorArgs"` // DeployerAddress describe the account address to be used to deploy contracts. diff --git a/fuzzing/config/config_defaults.go b/fuzzing/config/config_defaults.go index 7ed02798..1ac0b425 100644 --- a/fuzzing/config/config_defaults.go +++ b/fuzzing/config/config_defaults.go @@ -37,7 +37,7 @@ func GetDefaultProjectConfig(platform string) (*ProjectConfig, error) { Timeout: 0, TestLimit: 0, CallSequenceLength: 100, - DeploymentOrder: []string{}, + TargetContracts: []string{}, ConstructorArgs: map[string]map[string]any{}, CorpusDirectory: "", CoverageEnabled: true, diff --git a/fuzzing/fuzzer.go b/fuzzing/fuzzer.go index f1ca6465..5930edc7 100644 --- a/fuzzing/fuzzer.go +++ b/fuzzing/fuzzer.go @@ -327,23 +327,23 @@ func (f *Fuzzer) createTestChain() (*chain.TestChain, error) { // chainSetupFromCompilations is a TestChainSetupFunc which sets up the base test chain state by deploying // all compiled contract definitions. This includes any successful compilations as a result of the Fuzzer.config -// definitions, as well as those added by Fuzzer.AddCompilationTargets. The contract deployment order is defined by +// definitions, as well as those added by Fuzzer.AddCompilationTargets. The target contracts is defined by // the Fuzzer.config. func chainSetupFromCompilations(fuzzer *Fuzzer, testChain *chain.TestChain) error { - // Verify contract deployment order is not empty. If it's empty, but we only have one contract definition, - // we can infer the deployment order. Otherwise, we report an error. - if len(fuzzer.config.Fuzzing.DeploymentOrder) == 0 { + // Verify that target contracts is not empty. If it's empty, but we only have one contract definition, + // we can infer the target contracts. Otherwise, we report an error. + if len(fuzzer.config.Fuzzing.TargetContracts) == 0 { if len(fuzzer.contractDefinitions) == 1 { - fuzzer.config.Fuzzing.DeploymentOrder = []string{fuzzer.contractDefinitions[0].Name()} + fuzzer.config.Fuzzing.TargetContracts = []string{fuzzer.contractDefinitions[0].Name()} } else { - return fmt.Errorf("missing deployment order (update fuzzing.deploymentOrder in the project config " + - "or use the --deployment-order CLI flag)") + return fmt.Errorf("missing target contracts (update fuzzing.targetContracts in the project config " + + "or use the --target-contracts CLI flag)") } } // Loop for all contracts to deploy deployedContractAddr := make(map[string]common.Address) - for _, contractName := range fuzzer.config.Fuzzing.DeploymentOrder { + for _, contractName := range fuzzer.config.Fuzzing.TargetContracts { // Look for a contract in our compiled contract definitions that matches this one found := false for _, contract := range fuzzer.contractDefinitions { @@ -411,8 +411,8 @@ func chainSetupFromCompilations(fuzzer *Fuzzer, testChain *chain.TestChain) erro // If we did not find a contract corresponding to this item in the deployment order, we throw an error. if !found { - return fmt.Errorf("%v was specified in the deployment order (see fuzzing.deploymentOrder in the "+ - "project config or the --deployment-order CLI flag) but was not found in the compilation artifacts", contractName) + return fmt.Errorf("%v was specified in the target contracts (see fuzzing.targetContracts in the "+ + "project config or the --target-contracts CLI flag) but was not found in the compilation artifacts", contractName) } } return nil diff --git a/fuzzing/fuzzer_test.go b/fuzzing/fuzzer_test.go index e110d87f..f1219875 100644 --- a/fuzzing/fuzzer_test.go +++ b/fuzzing/fuzzer_test.go @@ -21,7 +21,7 @@ func TestFuzzerHooks(t *testing.T) { runFuzzerTest(t, &fuzzerSolcFileTest{ filePath: "testdata/contracts/assertions/assert_immediate.sol", configUpdates: func(config *config.ProjectConfig) { - config.Fuzzing.DeploymentOrder = []string{"TestContract"} + config.Fuzzing.TargetContracts = []string{"TestContract"} config.Fuzzing.Testing.PropertyTesting.Enabled = false config.Fuzzing.Testing.OptimizationTesting.Enabled = false }, @@ -76,7 +76,7 @@ func TestAssertionMode(t *testing.T) { runFuzzerTest(t, &fuzzerSolcFileTest{ filePath: filePath, configUpdates: func(config *config.ProjectConfig) { - config.Fuzzing.DeploymentOrder = []string{"TestContract"} + config.Fuzzing.TargetContracts = []string{"TestContract"} config.Fuzzing.Testing.AssertionTesting.PanicCodeConfig.FailOnAssertion = true config.Fuzzing.Testing.AssertionTesting.PanicCodeConfig.FailOnAllocateTooMuchMemory = true config.Fuzzing.Testing.AssertionTesting.PanicCodeConfig.FailOnArithmeticUnderflow = true @@ -107,7 +107,7 @@ func TestAssertionsNotRequire(t *testing.T) { runFuzzerTest(t, &fuzzerSolcFileTest{ filePath: "testdata/contracts/assertions/assert_not_require.sol", configUpdates: func(config *config.ProjectConfig) { - config.Fuzzing.DeploymentOrder = []string{"TestContract"} + config.Fuzzing.TargetContracts = []string{"TestContract"} config.Fuzzing.TestLimit = 500 config.Fuzzing.Testing.PropertyTesting.Enabled = false config.Fuzzing.Testing.OptimizationTesting.Enabled = false @@ -129,7 +129,7 @@ func TestAssertionsAndProperties(t *testing.T) { runFuzzerTest(t, &fuzzerSolcFileTest{ filePath: "testdata/contracts/assertions/assert_and_property_test.sol", configUpdates: func(config *config.ProjectConfig) { - config.Fuzzing.DeploymentOrder = []string{"TestContract"} + config.Fuzzing.TargetContracts = []string{"TestContract"} config.Fuzzing.TestLimit = 500 config.Fuzzing.Testing.StopOnFailedTest = false config.Fuzzing.Testing.OptimizationTesting.Enabled = false @@ -154,7 +154,7 @@ func TestOptimizationMode(t *testing.T) { runFuzzerTest(t, &fuzzerSolcFileTest{ filePath: filePath, configUpdates: func(config *config.ProjectConfig) { - config.Fuzzing.DeploymentOrder = []string{"TestContract"} + config.Fuzzing.TargetContracts = []string{"TestContract"} config.Fuzzing.TestLimit = 10_000 // this test should expose a failure quickly. config.Fuzzing.Testing.PropertyTesting.Enabled = false config.Fuzzing.Testing.AssertionTesting.Enabled = false @@ -182,7 +182,7 @@ func TestChainBehaviour(t *testing.T) { runFuzzerTest(t, &fuzzerSolcFileTest{ filePath: "testdata/contracts/chain/tx_out_of_gas.sol", configUpdates: func(config *config.ProjectConfig) { - config.Fuzzing.DeploymentOrder = []string{"TestContract"} + config.Fuzzing.TargetContracts = []string{"TestContract"} config.Fuzzing.Workers = 1 config.Fuzzing.TestLimit = uint64(config.Fuzzing.CallSequenceLength) // we just need a few oog txs to test config.Fuzzing.Timeout = 10 // to be safe, we set a 10s timeout @@ -236,7 +236,7 @@ func TestCheatCodes(t *testing.T) { runFuzzerTest(t, &fuzzerSolcFileTest{ filePath: filePath, configUpdates: func(config *config.ProjectConfig) { - config.Fuzzing.DeploymentOrder = []string{"TestContract"} + config.Fuzzing.TargetContracts = []string{"TestContract"} // Some tests require full sequence + revert to test fully config.Fuzzing.Workers = 3 @@ -276,7 +276,7 @@ func TestConsoleLog(t *testing.T) { runFuzzerTest(t, &fuzzerSolcFileTest{ filePath: filePath, configUpdates: func(config *config.ProjectConfig) { - config.Fuzzing.DeploymentOrder = []string{"TestContract"} + config.Fuzzing.TargetContracts = []string{"TestContract"} config.Fuzzing.TestLimit = 10000 config.Fuzzing.Testing.PropertyTesting.Enabled = false config.Fuzzing.Testing.OptimizationTesting.Enabled = false @@ -322,7 +322,7 @@ func TestDeploymentsInnerDeployments(t *testing.T) { runFuzzerTest(t, &fuzzerSolcFileTest{ filePath: filePath, configUpdates: func(config *config.ProjectConfig) { - config.Fuzzing.DeploymentOrder = []string{"InnerDeploymentFactory"} + config.Fuzzing.TargetContracts = []string{"InnerDeploymentFactory"} config.Fuzzing.TestLimit = 1_000 // this test should expose a failure quickly. config.Fuzzing.Testing.StopOnFailedContractMatching = true config.Fuzzing.Testing.TestAllContracts = true // test dynamically deployed contracts @@ -345,7 +345,7 @@ func TestDeploymentsInnerDeployments(t *testing.T) { runFuzzerTest(t, &fuzzerSolcFileTest{ filePath: "testdata/contracts/deployments/inner_deployment_on_construction.sol", configUpdates: func(config *config.ProjectConfig) { - config.Fuzzing.DeploymentOrder = []string{"InnerDeploymentFactory"} + config.Fuzzing.TargetContracts = []string{"InnerDeploymentFactory"} config.Fuzzing.TestLimit = 1_000 // this test should expose a failure quickly. config.Fuzzing.Testing.StopOnFailedContractMatching = true config.Fuzzing.Testing.TestAllContracts = true // test dynamically deployed contracts @@ -368,7 +368,7 @@ func TestDeploymentsInternalLibrary(t *testing.T) { runFuzzerTest(t, &fuzzerSolcFileTest{ filePath: "testdata/contracts/deployments/internal_library.sol", configUpdates: func(config *config.ProjectConfig) { - config.Fuzzing.DeploymentOrder = []string{"TestInternalLibrary"} + config.Fuzzing.TargetContracts = []string{"TestInternalLibrary"} config.Fuzzing.TestLimit = 100 // this test should expose a failure quickly. config.Fuzzing.Testing.AssertionTesting.Enabled = false config.Fuzzing.Testing.OptimizationTesting.Enabled = false @@ -397,7 +397,7 @@ func TestDeploymentsSelfDestruct(t *testing.T) { runFuzzerTest(t, &fuzzerSolcFileTest{ filePath: filePath, configUpdates: func(config *config.ProjectConfig) { - config.Fuzzing.DeploymentOrder = []string{"InnerDeploymentFactory"} + config.Fuzzing.TargetContracts = []string{"InnerDeploymentFactory"} config.Fuzzing.TestLimit = 500 // this test should expose a failure quickly. config.Fuzzing.Testing.StopOnNoTests = false config.Fuzzing.Testing.AssertionTesting.Enabled = false @@ -445,7 +445,7 @@ func TestExecutionTraces(t *testing.T) { runFuzzerTest(t, &fuzzerSolcFileTest{ filePath: filePath, configUpdates: func(config *config.ProjectConfig) { - config.Fuzzing.DeploymentOrder = []string{"TestContract"} + config.Fuzzing.TargetContracts = []string{"TestContract"} config.Fuzzing.Testing.PropertyTesting.Enabled = false config.Fuzzing.Testing.OptimizationTesting.Enabled = false }, @@ -485,7 +485,7 @@ func TestTestingScope(t *testing.T) { runFuzzerTest(t, &fuzzerSolcFileTest{ filePath: "testdata/contracts/deployments/testing_scope.sol", configUpdates: func(config *config.ProjectConfig) { - config.Fuzzing.DeploymentOrder = []string{"TestContract"} + config.Fuzzing.TargetContracts = []string{"TestContract"} config.Fuzzing.TestLimit = 1_000 // this test should expose a failure quickly. config.Fuzzing.Testing.TestAllContracts = testingAllContracts config.Fuzzing.Testing.StopOnFailedTest = false @@ -519,7 +519,7 @@ func TestDeploymentsWithArgs(t *testing.T) { runFuzzerTest(t, &fuzzerSolcFileTest{ filePath: "testdata/contracts/deployments/deployment_with_args.sol", configUpdates: func(config *config.ProjectConfig) { - config.Fuzzing.DeploymentOrder = []string{"DeploymentWithArgs", "Dependent"} + config.Fuzzing.TargetContracts = []string{"DeploymentWithArgs", "Dependent"} config.Fuzzing.ConstructorArgs = map[string]map[string]any{ "DeploymentWithArgs": { "_x": "123456789", @@ -554,7 +554,7 @@ func TestValueGenerationGenerateAllTypes(t *testing.T) { runFuzzerTest(t, &fuzzerSolcFileTest{ filePath: "testdata/contracts/value_generation/generate_all_types.sol", configUpdates: func(config *config.ProjectConfig) { - config.Fuzzing.DeploymentOrder = []string{"GenerateAllTypes"} + config.Fuzzing.TargetContracts = []string{"GenerateAllTypes"} config.Fuzzing.TestLimit = 10_000 config.Fuzzing.Testing.AssertionTesting.Enabled = false config.Fuzzing.Testing.OptimizationTesting.Enabled = false @@ -589,7 +589,7 @@ func TestValueGenerationSolving(t *testing.T) { runFuzzerTest(t, &fuzzerSolcFileTest{ filePath: filePath, configUpdates: func(config *config.ProjectConfig) { - config.Fuzzing.DeploymentOrder = []string{"TestContract"} + config.Fuzzing.TargetContracts = []string{"TestContract"} config.Fuzzing.Testing.AssertionTesting.Enabled = false config.Fuzzing.Testing.OptimizationTesting.Enabled = false }, @@ -649,6 +649,7 @@ func TestASTValueExtraction(t *testing.T) { config.Fuzzing.TestLimit = 1 // stop immediately to simply see what values were mined. config.Fuzzing.Testing.PropertyTesting.Enabled = false config.Fuzzing.Testing.OptimizationTesting.Enabled = false + config.Fuzzing.TargetContracts = []string{"TestContract"} }, method: func(f *fuzzerTestContext) { // Start the fuzzer @@ -682,7 +683,7 @@ func TestVMCorrectness(t *testing.T) { runFuzzerTest(t, &fuzzerSolcFileTest{ filePath: "testdata/contracts/vm_tests/block_number_increasing.sol", configUpdates: func(config *config.ProjectConfig) { - config.Fuzzing.DeploymentOrder = []string{"TestContract"} + config.Fuzzing.TargetContracts = []string{"TestContract"} config.Fuzzing.MaxBlockTimestampDelay = 1 // this contract require calls every block config.Fuzzing.MaxBlockNumberDelay = 1 // this contract require calls every block config.Fuzzing.Testing.AssertionTesting.Enabled = false @@ -703,7 +704,7 @@ func TestVMCorrectness(t *testing.T) { runFuzzerTest(t, &fuzzerSolcFileTest{ filePath: "testdata/contracts/vm_tests/block_number_increasing.sol", configUpdates: func(config *config.ProjectConfig) { - config.Fuzzing.DeploymentOrder = []string{"TestContract"} + config.Fuzzing.TargetContracts = []string{"TestContract"} config.Fuzzing.MaxBlockTimestampDelay = 1 // this contract require calls every block config.Fuzzing.MaxBlockNumberDelay = 1 // this contract require calls every block }, @@ -722,7 +723,7 @@ func TestVMCorrectness(t *testing.T) { runFuzzerTest(t, &fuzzerSolcFileTest{ filePath: "testdata/contracts/vm_tests/block_hash_store_check.sol", configUpdates: func(config *config.ProjectConfig) { - config.Fuzzing.DeploymentOrder = []string{"TestContract"} + config.Fuzzing.TargetContracts = []string{"TestContract"} config.Fuzzing.TestLimit = 1_000 // this test should expose a failure quickly. config.Fuzzing.MaxBlockTimestampDelay = 1 // this contract require calls every block config.Fuzzing.MaxBlockNumberDelay = 1 // this contract require calls every block @@ -747,7 +748,7 @@ func TestCorpusReplayability(t *testing.T) { runFuzzerTest(t, &fuzzerSolcFileTest{ filePath: "testdata/contracts/value_generation/match_uints_xy.sol", configUpdates: func(config *config.ProjectConfig) { - config.Fuzzing.DeploymentOrder = []string{"TestContract"} + config.Fuzzing.TargetContracts = []string{"TestContract"} config.Fuzzing.CorpusDirectory = "corpus" config.Fuzzing.Testing.AssertionTesting.Enabled = false config.Fuzzing.Testing.OptimizationTesting.Enabled = false @@ -788,14 +789,14 @@ func TestCorpusReplayability(t *testing.T) { }) } -// TestDeploymentOrderWithCoverage will ensure that changing the deployment order does not lead to the same coverage -// This is also proof that changing the order changes the addresses of the contracts leading to the coverage not being -// useful. +// TestDeploymentOrderWithCoverage will ensure that changing the order of deployment for the target contracts does not +// lead to the same coverage. This is also proof that changing the order changes the addresses of the contracts leading +// to the coverage not being useful. func TestDeploymentOrderWithCoverage(t *testing.T) { runFuzzerTest(t, &fuzzerSolcFileTest{ filePath: "testdata/contracts/deployments/deployment_order.sol", configUpdates: func(config *config.ProjectConfig) { - config.Fuzzing.DeploymentOrder = []string{"InheritedFirstContract", "InheritedSecondContract"} + config.Fuzzing.TargetContracts = []string{"InheritedFirstContract", "InheritedSecondContract"} config.Fuzzing.Testing.AssertionTesting.Enabled = false config.Fuzzing.Testing.OptimizationTesting.Enabled = false }, @@ -820,8 +821,8 @@ func TestDeploymentOrderWithCoverage(t *testing.T) { return nil }) - // Update the deployment order - f.fuzzer.config.Fuzzing.DeploymentOrder = []string{"InheritedSecondContract", "InheritedFirstContract"} + // Update the order of target contracts + f.fuzzer.config.Fuzzing.TargetContracts = []string{"InheritedSecondContract", "InheritedFirstContract"} // Note that the fuzzer won't spin up any workers or fuzz anything. We just want to test that the coverage // maps don't populate due to deployment order changes diff --git a/fuzzing/test_case_assertion_provider.go b/fuzzing/test_case_assertion_provider.go index d4390c4e..839afb17 100644 --- a/fuzzing/test_case_assertion_provider.go +++ b/fuzzing/test_case_assertion_provider.go @@ -98,8 +98,8 @@ func (t *AssertionTestCaseProvider) onFuzzerStarting(event FuzzerStartingEvent) // Create a test case for every test method. for _, contract := range t.fuzzer.ContractDefinitions() { - // If we're not testing all contracts, verify the current contract is one we specified in our deployment order. - if !t.fuzzer.config.Fuzzing.Testing.TestAllContracts && !slices.Contains(t.fuzzer.config.Fuzzing.DeploymentOrder, contract.Name()) { + // If we're not testing all contracts, verify the current contract is one we specified in our target contracts + if !t.fuzzer.config.Fuzzing.Testing.TestAllContracts && !slices.Contains(t.fuzzer.config.Fuzzing.TargetContracts, contract.Name()) { continue } diff --git a/fuzzing/test_case_optimization_provider.go b/fuzzing/test_case_optimization_provider.go index 85afc29c..e58dc4dc 100644 --- a/fuzzing/test_case_optimization_provider.go +++ b/fuzzing/test_case_optimization_provider.go @@ -7,6 +7,7 @@ import ( "github.com/crytic/medusa/fuzzing/executiontracer" "github.com/crytic/medusa/utils" "github.com/ethereum/go-ethereum/core" + "golang.org/x/exp/slices" "math/big" "sync" ) @@ -125,6 +126,11 @@ func (t *OptimizationTestCaseProvider) onFuzzerStarting(event FuzzerStartingEven // Create a test case for every optimization test method. for _, contract := range t.fuzzer.ContractDefinitions() { + // If we're not testing all contracts, verify the current contract is one we specified in our target contracts + if !t.fuzzer.config.Fuzzing.Testing.TestAllContracts && !slices.Contains(t.fuzzer.config.Fuzzing.TargetContracts, contract.Name()) { + continue + } + for _, method := range contract.CompiledContract().Abi.Methods { // Verify this method is an optimization test method if !utils.IsOptimizationTest(method, t.fuzzer.config.Fuzzing.Testing.OptimizationTesting.TestPrefixes) { diff --git a/fuzzing/test_case_property_provider.go b/fuzzing/test_case_property_provider.go index 188dedca..d9902752 100644 --- a/fuzzing/test_case_property_provider.go +++ b/fuzzing/test_case_property_provider.go @@ -128,8 +128,8 @@ func (t *PropertyTestCaseProvider) onFuzzerStarting(event FuzzerStartingEvent) e // Create a test case for every property test method. for _, contract := range t.fuzzer.ContractDefinitions() { - // If we're not testing all contracts, verify the current contract is one we specified in our deployment order. - if !t.fuzzer.config.Fuzzing.Testing.TestAllContracts && !slices.Contains(t.fuzzer.config.Fuzzing.DeploymentOrder, contract.Name()) { + // If we're not testing all contracts, verify the current contract is one we specified in our target contracts. + if !t.fuzzer.config.Fuzzing.Testing.TestAllContracts && !slices.Contains(t.fuzzer.config.Fuzzing.TargetContracts, contract.Name()) { continue } From bb62d7050a9da09e8d0c2a148cff2b136681be45 Mon Sep 17 00:00:00 2001 From: Anish Naik Date: Wed, 30 Aug 2023 16:11:43 -0400 Subject: [PATCH 06/14] update edge case where coverage is 0/0 lines --- fuzzing/coverage/report_generation.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fuzzing/coverage/report_generation.go b/fuzzing/coverage/report_generation.go index 184038f7..c9bc2da1 100644 --- a/fuzzing/coverage/report_generation.go +++ b/fuzzing/coverage/report_generation.go @@ -62,9 +62,9 @@ func exportCoverageReport(sourceAnalysis *SourceAnalysis, outputPath string) err // Determine our precision string formatStr := "%." + strconv.Itoa(decimals) + "f" - // If no lines are active and none are covered, show 100% coverage + // If no lines are active and none are covered, show 0% coverage if x == 0 && y == 0 { - return fmt.Sprintf(formatStr, float64(100)) + return fmt.Sprintf(formatStr, float64(0)) } return fmt.Sprintf(formatStr, (float64(x)/float64(y))*100) }, From 4a20b57960d9aa5e200acd766f7a54c1b9488aa0 Mon Sep 17 00:00:00 2001 From: Anish Naik Date: Wed, 30 Aug 2023 17:21:16 -0400 Subject: [PATCH 07/14] add support for payable constructors --- fuzzing/config/config.go | 18 +++ fuzzing/config/gen_fuzzing_config.go | 148 ++++++++++++++++++ fuzzing/fuzzer.go | 10 +- fuzzing/fuzzer_test.go | 23 +++ .../deploy_payable_constructors.sol | 18 +++ go.mod | 4 + go.sum | 21 +++ logging/logger.go | 8 +- 8 files changed, 244 insertions(+), 6 deletions(-) create mode 100644 fuzzing/config/gen_fuzzing_config.go create mode 100644 fuzzing/testdata/contracts/deployments/deploy_payable_constructors.sol diff --git a/fuzzing/config/config.go b/fuzzing/config/config.go index a8e0dde7..4d1acad3 100644 --- a/fuzzing/config/config.go +++ b/fuzzing/config/config.go @@ -5,13 +5,20 @@ import ( "errors" "github.com/crytic/medusa/chain/config" "github.com/crytic/medusa/logging" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/rs/zerolog" + "math/big" "os" "github.com/crytic/medusa/compilation" "github.com/crytic/medusa/utils" ) +// The following directives will be picked up by the `go generate` command to generate JSON marshaling code from +// templates defined below. They should be preserved for re-use in case we change our structures. +//go:generate go get github.com/fjl/gencodec +//go:generate go run github.com/fjl/gencodec -type FuzzingConfig -field-override fuzzingConfigMarshaling -out gen_fuzzing_config.go + type ProjectConfig struct { // Fuzzing describes the configuration used in fuzzing campaigns. Fuzzing FuzzingConfig `json:"fuzzing"` @@ -53,6 +60,10 @@ type FuzzingConfig struct { // TargetContracts are the target contracts for fuzz testing TargetContracts []string `json:"targetContracts"` + // TargetContractsBalances holds the amount of wei that should be sent during deployment for one or more contracts in + // TargetContracts + TargetContractsBalances []*big.Int `json:"targetContractsBalances"` + // ConstructorArgs holds the constructor arguments for TargetContracts deployments. It is available via the project // configuration ConstructorArgs map[string]map[string]any `json:"constructorArgs"` @@ -86,6 +97,13 @@ type FuzzingConfig struct { TestChainConfig config.TestChainConfig `json:"chainConfig"` } +// fuzzingConfigMarshaling is a structure that overrides field types during JSON marshaling. It allows FuzzingConfig to +// have its custom marshaling methods auto-generated and will handle type conversions for serialization purposes. +// For example, this enables serialization of big.Int but specifying a different field type to control serialization. +type fuzzingConfigMarshaling struct { + TargetContractsBalances []*hexutil.Big +} + // TestingConfig describes the configuration options used for testing type TestingConfig struct { // StopOnFailedTest describes whether the fuzzing.Fuzzer should stop after detecting the first failed test. diff --git a/fuzzing/config/gen_fuzzing_config.go b/fuzzing/config/gen_fuzzing_config.go new file mode 100644 index 00000000..5f1de304 --- /dev/null +++ b/fuzzing/config/gen_fuzzing_config.go @@ -0,0 +1,148 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package config + +import ( + "encoding/json" + "math/big" + + "github.com/crytic/medusa/chain/config" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var _ = (*fuzzingConfigMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (f FuzzingConfig) MarshalJSON() ([]byte, error) { + type FuzzingConfig struct { + Workers int `json:"workers"` + WorkerResetLimit int `json:"workerResetLimit"` + Timeout int `json:"timeout"` + TestLimit uint64 `json:"testLimit"` + CallSequenceLength int `json:"callSequenceLength"` + CorpusDirectory string `json:"corpusDirectory"` + CoverageEnabled bool `json:"coverageEnabled"` + TargetContracts []string `json:"targetContracts"` + TargetContractsBalances []*hexutil.Big `json:"targetContractsBalances"` + ConstructorArgs map[string]map[string]any `json:"constructorArgs"` + DeployerAddress string `json:"deployerAddress"` + SenderAddresses []string `json:"senderAddresses"` + MaxBlockNumberDelay uint64 `json:"blockNumberDelayMax"` + MaxBlockTimestampDelay uint64 `json:"blockTimestampDelayMax"` + BlockGasLimit uint64 `json:"blockGasLimit"` + TransactionGasLimit uint64 `json:"transactionGasLimit"` + Testing TestingConfig `json:"testing"` + TestChainConfig config.TestChainConfig `json:"chainConfig"` + } + var enc FuzzingConfig + enc.Workers = f.Workers + enc.WorkerResetLimit = f.WorkerResetLimit + enc.Timeout = f.Timeout + enc.TestLimit = f.TestLimit + enc.CallSequenceLength = f.CallSequenceLength + enc.CorpusDirectory = f.CorpusDirectory + enc.CoverageEnabled = f.CoverageEnabled + enc.TargetContracts = f.TargetContracts + if f.TargetContractsBalances != nil { + enc.TargetContractsBalances = make([]*hexutil.Big, len(f.TargetContractsBalances)) + for k, v := range f.TargetContractsBalances { + enc.TargetContractsBalances[k] = (*hexutil.Big)(v) + } + } + enc.ConstructorArgs = f.ConstructorArgs + enc.DeployerAddress = f.DeployerAddress + enc.SenderAddresses = f.SenderAddresses + enc.MaxBlockNumberDelay = f.MaxBlockNumberDelay + enc.MaxBlockTimestampDelay = f.MaxBlockTimestampDelay + enc.BlockGasLimit = f.BlockGasLimit + enc.TransactionGasLimit = f.TransactionGasLimit + enc.Testing = f.Testing + enc.TestChainConfig = f.TestChainConfig + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (f *FuzzingConfig) UnmarshalJSON(input []byte) error { + type FuzzingConfig struct { + Workers *int `json:"workers"` + WorkerResetLimit *int `json:"workerResetLimit"` + Timeout *int `json:"timeout"` + TestLimit *uint64 `json:"testLimit"` + CallSequenceLength *int `json:"callSequenceLength"` + CorpusDirectory *string `json:"corpusDirectory"` + CoverageEnabled *bool `json:"coverageEnabled"` + TargetContracts []string `json:"targetContracts"` + TargetContractsBalances []*hexutil.Big `json:"targetContractsBalances"` + ConstructorArgs map[string]map[string]any `json:"constructorArgs"` + DeployerAddress *string `json:"deployerAddress"` + SenderAddresses []string `json:"senderAddresses"` + MaxBlockNumberDelay *uint64 `json:"blockNumberDelayMax"` + MaxBlockTimestampDelay *uint64 `json:"blockTimestampDelayMax"` + BlockGasLimit *uint64 `json:"blockGasLimit"` + TransactionGasLimit *uint64 `json:"transactionGasLimit"` + Testing *TestingConfig `json:"testing"` + TestChainConfig *config.TestChainConfig `json:"chainConfig"` + } + var dec FuzzingConfig + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Workers != nil { + f.Workers = *dec.Workers + } + if dec.WorkerResetLimit != nil { + f.WorkerResetLimit = *dec.WorkerResetLimit + } + if dec.Timeout != nil { + f.Timeout = *dec.Timeout + } + if dec.TestLimit != nil { + f.TestLimit = *dec.TestLimit + } + if dec.CallSequenceLength != nil { + f.CallSequenceLength = *dec.CallSequenceLength + } + if dec.CorpusDirectory != nil { + f.CorpusDirectory = *dec.CorpusDirectory + } + if dec.CoverageEnabled != nil { + f.CoverageEnabled = *dec.CoverageEnabled + } + if dec.TargetContracts != nil { + f.TargetContracts = dec.TargetContracts + } + if dec.TargetContractsBalances != nil { + f.TargetContractsBalances = make([]*big.Int, len(dec.TargetContractsBalances)) + for k, v := range dec.TargetContractsBalances { + f.TargetContractsBalances[k] = (*big.Int)(v) + } + } + if dec.ConstructorArgs != nil { + f.ConstructorArgs = dec.ConstructorArgs + } + if dec.DeployerAddress != nil { + f.DeployerAddress = *dec.DeployerAddress + } + if dec.SenderAddresses != nil { + f.SenderAddresses = dec.SenderAddresses + } + if dec.MaxBlockNumberDelay != nil { + f.MaxBlockNumberDelay = *dec.MaxBlockNumberDelay + } + if dec.MaxBlockTimestampDelay != nil { + f.MaxBlockTimestampDelay = *dec.MaxBlockTimestampDelay + } + if dec.BlockGasLimit != nil { + f.BlockGasLimit = *dec.BlockGasLimit + } + if dec.TransactionGasLimit != nil { + f.TransactionGasLimit = *dec.TransactionGasLimit + } + if dec.Testing != nil { + f.Testing = *dec.Testing + } + if dec.TestChainConfig != nil { + f.TestChainConfig = *dec.TestChainConfig + } + return nil +} diff --git a/fuzzing/fuzzer.go b/fuzzing/fuzzer.go index 5930edc7..25f81986 100644 --- a/fuzzing/fuzzer.go +++ b/fuzzing/fuzzer.go @@ -343,7 +343,7 @@ func chainSetupFromCompilations(fuzzer *Fuzzer, testChain *chain.TestChain) erro // Loop for all contracts to deploy deployedContractAddr := make(map[string]common.Address) - for _, contractName := range fuzzer.config.Fuzzing.TargetContracts { + for i, contractName := range fuzzer.config.Fuzzing.TargetContracts { // Look for a contract in our compiled contract definitions that matches this one found := false for _, contract := range fuzzer.contractDefinitions { @@ -369,9 +369,15 @@ func chainSetupFromCompilations(fuzzer *Fuzzer, testChain *chain.TestChain) erro return fmt.Errorf("initial contract deployment failed for contract \"%v\", error: %v", contractName, err) } + // If our project config has a non-zero balance for this target contract, retrieve it + contractBalance := big.NewInt(0) + if len(fuzzer.config.Fuzzing.TargetContractsBalances) > i { + contractBalance = new(big.Int).Set(fuzzer.config.Fuzzing.TargetContractsBalances[i]) + } + // Create a message to represent our contract deployment (we let deployments consume the whole block // gas limit rather than use tx gas limit) - msg := calls.NewCallMessage(fuzzer.deployer, nil, 0, big.NewInt(0), fuzzer.config.Fuzzing.BlockGasLimit, nil, nil, nil, msgData) + msg := calls.NewCallMessage(fuzzer.deployer, nil, 0, contractBalance, fuzzer.config.Fuzzing.BlockGasLimit, nil, nil, nil, msgData) msg.FillFromTestChainProperties(testChain) // Create a new pending block we'll commit to chain diff --git a/fuzzing/fuzzer_test.go b/fuzzing/fuzzer_test.go index f1219875..104592f8 100644 --- a/fuzzing/fuzzer_test.go +++ b/fuzzing/fuzzer_test.go @@ -385,6 +385,29 @@ func TestDeploymentsInternalLibrary(t *testing.T) { }) } +// TestDeploymentsWithPayableConstructor runs a test to ensure that we can send ether to payable constructors +func TestDeploymentsWithPayableConstructors(t *testing.T) { + runFuzzerTest(t, &fuzzerSolcFileTest{ + filePath: "testdata/contracts/deployments/deploy_payable_constructors.sol", + configUpdates: func(config *config.ProjectConfig) { + config.Fuzzing.TargetContracts = []string{"FirstContract", "SecondContract"} + config.Fuzzing.TargetContractsBalances = []*big.Int{big.NewInt(0), big.NewInt(1e18)} + config.Fuzzing.TestLimit = 1 // this should happen immediately + config.Fuzzing.Testing.AssertionTesting.Enabled = false + config.Fuzzing.Testing.OptimizationTesting.Enabled = false + }, + method: func(f *fuzzerTestContext) { + // Start the fuzzer + err := f.fuzzer.Start() + assert.NoError(t, err) + + // Check for any failed tests and verify coverage was captured + assertFailedTestsExpected(f, false) + assertCorpusCallSequencesCollected(f, true) + }, + }) +} + // TestDeploymentsInnerDeployments runs a test to ensure dynamically deployed contracts are detected by the Fuzzer and // their properties are tested appropriately. func TestDeploymentsSelfDestruct(t *testing.T) { diff --git a/fuzzing/testdata/contracts/deployments/deploy_payable_constructors.sol b/fuzzing/testdata/contracts/deployments/deploy_payable_constructors.sol new file mode 100644 index 00000000..223d85c4 --- /dev/null +++ b/fuzzing/testdata/contracts/deployments/deploy_payable_constructors.sol @@ -0,0 +1,18 @@ +// This source file provides two contracts to test whether we are able to send ether to payable constructors. FirstContract +// should get no ether and while SecondContract should receive 1 ether. +contract FirstContract { + constructor() payable {} + + function property_contract_has_no_balance() public returns(bool) { + return address(this).balance == 0; + } +} + + +contract SecondContract { + constructor() payable {} + + function property_contract_has_balance() public returns(bool) { + return address(this).balance == 1 ether; + } +} diff --git a/go.mod b/go.mod index 83b9d462..e0ba1dd1 100644 --- a/go.mod +++ b/go.mod @@ -32,6 +32,8 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/deckarep/golang-set/v2 v2.1.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect + github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e // indirect + github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 // indirect github.com/getsentry/sentry-go v0.18.0 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-stack/stack v1.8.1 // indirect @@ -64,7 +66,9 @@ require ( github.com/tklauser/numcpus v0.6.0 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect + golang.org/x/mod v0.9.0 // indirect golang.org/x/text v0.12.0 // indirect + golang.org/x/tools v0.7.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 7721ec5e..02663fb6 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,8 @@ github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqR github.com/VictoriaMetrics/fastcache v1.12.0 h1:vnVi/y9yKDcD9akmc4NqAoqgQhJrOwUF+j9LTgn4QDE= github.com/VictoriaMetrics/fastcache v1.12.0/go.mod h1:tjiYeEfYXCqacuvYw/7UoDIeJaNxq6132xHICNP77w8= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= @@ -38,6 +40,7 @@ github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoG github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811/go.mod h1:Nb5lgvnQ2+oGlE/EyZy4+2/CxRh9KfvCXnag1vtpxVM= github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= @@ -54,6 +57,7 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= @@ -67,11 +71,15 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e h1:bBLctRc7kr01YGvaDfgLbTwjFNW5jdp5y5rj8XXBHfY= +github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fxamacker/cbor v1.5.1 h1:XjQWBgdmQyqimslUh5r4tUGmoqzHmBFQOImkWGi2awg= github.com/fxamacker/cbor v1.5.1/go.mod h1:3aPGItF174ni7dDzd6JZ206H8cmr4GDNBGpPa971zsU= +github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 h1:IZqZOB2fydHte3kUgxrzK5E1fW7RQGeDwE8F/ZZnUYc= +github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8= github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= @@ -81,6 +89,7 @@ github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/ github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= @@ -152,9 +161,12 @@ github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/ github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g= github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8= github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE= @@ -207,7 +219,9 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= @@ -275,6 +289,7 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -334,6 +349,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -421,6 +438,8 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -452,6 +471,7 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -470,6 +490,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/logging/logger.go b/logging/logger.go index 48d5c539..7a6c09e4 100644 --- a/logging/logger.go +++ b/logging/logger.go @@ -5,7 +5,6 @@ import ( "github.com/crytic/medusa/logging/colors" "github.com/rs/zerolog" "io" - "os" "strings" ) @@ -57,11 +56,11 @@ type StructuredLogInfo map[string]any func NewLogger(level zerolog.Level) *Logger { return &Logger{ level: level, - structuredLogger: zerolog.New(os.Stdout).Level(zerolog.Disabled), + structuredLogger: zerolog.New(nil).Level(level), structuredWriters: make([]io.Writer, 0), - unstructuredLogger: zerolog.New(os.Stdout).Level(zerolog.Disabled), + unstructuredLogger: zerolog.New(nil).Level(level), unstructuredWriters: make([]io.Writer, 0), - unstructuredColorLogger: zerolog.New(os.Stdout).Level(zerolog.Disabled), + unstructuredColorLogger: zerolog.New(nil).Level(level), unstructuredColorWriters: make([]io.Writer, 0), } } @@ -206,6 +205,7 @@ func (l *Logger) SetLevel(level zerolog.Level) { l.structuredLogger = l.structuredLogger.Level(level) l.unstructuredColorLogger = l.unstructuredColorLogger.Level(level) l.unstructuredLogger = l.unstructuredLogger.Level(level) + } // Trace is a wrapper function that will log a trace event From 0005b8f68e6f2c4c0be7a6c5133369f063f0769a Mon Sep 17 00:00:00 2001 From: Anish Naik Date: Wed, 30 Aug 2023 17:26:45 -0400 Subject: [PATCH 08/14] linting --- fuzzing/test_case_optimization_provider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzzing/test_case_optimization_provider.go b/fuzzing/test_case_optimization_provider.go index e58dc4dc..05cbeb3c 100644 --- a/fuzzing/test_case_optimization_provider.go +++ b/fuzzing/test_case_optimization_provider.go @@ -130,7 +130,7 @@ func (t *OptimizationTestCaseProvider) onFuzzerStarting(event FuzzerStartingEven if !t.fuzzer.config.Fuzzing.Testing.TestAllContracts && !slices.Contains(t.fuzzer.config.Fuzzing.TargetContracts, contract.Name()) { continue } - + for _, method := range contract.CompiledContract().Abi.Methods { // Verify this method is an optimization test method if !utils.IsOptimizationTest(method, t.fuzzer.config.Fuzzing.Testing.OptimizationTesting.TestPrefixes) { From bb54d4e4af9fd62888fb4a51567bde513431edfc Mon Sep 17 00:00:00 2001 From: Anish Naik Date: Thu, 31 Aug 2023 11:52:20 -0400 Subject: [PATCH 09/14] fix bug --- fuzzing/config/config.go | 19 ------------------- fuzzing/test_case_optimization_provider.go | 5 +++++ fuzzing/test_case_property_provider.go | 5 +++++ 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/fuzzing/config/config.go b/fuzzing/config/config.go index 4d1acad3..1e95e749 100644 --- a/fuzzing/config/config.go +++ b/fuzzing/config/config.go @@ -333,25 +333,6 @@ func (p *ProjectConfig) Validate() error { "config or use the --deployer CLI flag)") } - // Verify property testing fields. - if p.Fuzzing.Testing.PropertyTesting.Enabled { - // Test prefixes must be supplied if property testing is enabled. - if len(p.Fuzzing.Testing.PropertyTesting.TestPrefixes) == 0 { - return errors.New("project config must specify test prefixes if property testing is enabled " + - "(update fuzzing.testing.propertyTesting.testPrefixes)") - } - } - - // Verify optimization testing fields. - if p.Fuzzing.Testing.OptimizationTesting.Enabled { - // Test prefixes must be supplied if property testing is enabled. - if len(p.Fuzzing.Testing.OptimizationTesting.TestPrefixes) == 0 { - return errors.New("project config must specify test prefixes if optimization testing is enabled" + - "(update fuzzing.testing.optimizationTesting.testPrefixes)") - } - - } - // Ensure that the log level is a valid one level, err := zerolog.ParseLevel(p.Logging.Level.String()) if err != nil || level == zerolog.FatalLevel { diff --git a/fuzzing/test_case_optimization_provider.go b/fuzzing/test_case_optimization_provider.go index 05cbeb3c..cfc47efe 100644 --- a/fuzzing/test_case_optimization_provider.go +++ b/fuzzing/test_case_optimization_provider.go @@ -45,6 +45,11 @@ type optimizationTestCaseProviderWorkerState struct { // attachOptimizationTestCaseProvider attaches a new OptimizationTestCaseProvider to the Fuzzer and returns it. func attachOptimizationTestCaseProvider(fuzzer *Fuzzer) *OptimizationTestCaseProvider { + // If there are no testing prefixes, then there is no reason to attach a test case provider and subscribe to events + if len(fuzzer.config.Fuzzing.Testing.OptimizationTesting.TestPrefixes) == 0 { + return nil + } + // Create a test case provider t := &OptimizationTestCaseProvider{ fuzzer: fuzzer, diff --git a/fuzzing/test_case_property_provider.go b/fuzzing/test_case_property_provider.go index d9902752..91184d55 100644 --- a/fuzzing/test_case_property_provider.go +++ b/fuzzing/test_case_property_provider.go @@ -46,6 +46,11 @@ type propertyTestCaseProviderWorkerState struct { // attachPropertyTestCaseProvider attaches a new PropertyTestCaseProvider to the Fuzzer and returns it. func attachPropertyTestCaseProvider(fuzzer *Fuzzer) *PropertyTestCaseProvider { + // If there are no testing prefixes, then there is no reason to attach a test case provider and subscribe to events + if len(fuzzer.config.Fuzzing.Testing.PropertyTesting.TestPrefixes) == 0 { + return nil + } + // Create a test case provider t := &PropertyTestCaseProvider{ fuzzer: fuzzer, From b42f05ebd6277a7884ae6fa21905aa8955cd80de Mon Sep 17 00:00:00 2001 From: Anish Naik Date: Fri, 1 Sep 2023 12:45:21 -0400 Subject: [PATCH 10/14] fix panic and improve return data printing in execution trace --- fuzzing/coverage/coverage_maps.go | 6 ++++-- fuzzing/executiontracer/execution_trace.go | 9 +++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/fuzzing/coverage/coverage_maps.go b/fuzzing/coverage/coverage_maps.go index 71ed9852..e9a1343c 100644 --- a/fuzzing/coverage/coverage_maps.go +++ b/fuzzing/coverage/coverage_maps.go @@ -2,7 +2,6 @@ package coverage import ( "bytes" - "fmt" compilationTypes "github.com/crytic/medusa/compilation/types" "github.com/crytic/medusa/utils" "github.com/ethereum/go-ethereum/common" @@ -373,5 +372,8 @@ func (cm *CoverageMapBytecodeData) setCoveredAt(codeSize int, pc uint64) (bool, } return false, nil } - return false, fmt.Errorf("tried to set coverage map out of bounds (pc: %d, code size %d)", pc, len(cm.executedFlags)) + + // Since it is possible that the program counter is larger than the code size (e.g., malformed bytecode), we will + // simply return false with no error + return false, nil } diff --git a/fuzzing/executiontracer/execution_trace.go b/fuzzing/executiontracer/execution_trace.go index 287ce52c..695f595b 100644 --- a/fuzzing/executiontracer/execution_trace.go +++ b/fuzzing/executiontracer/execution_trace.go @@ -195,14 +195,19 @@ func (t *ExecutionTrace) generateCallFrameExitElements(callFrame *CallFrame) []a // If we could not correctly obtain the unpacked arguments in a nice display string (due to not having a resolved // contract or method definition, or failure to unpack), we display as raw data in the worst case. - if outputArgumentsDisplayText == nil { + // TODO: Fix if return data is empty len byte array + if outputArgumentsDisplayText == nil && len(callFrame.ReturnData) > 0 { temp := fmt.Sprintf("return_data=%v", hex.EncodeToString(callFrame.ReturnData)) outputArgumentsDisplayText = &temp } // Wrap our return message and output it at the end. if callFrame.ReturnError == nil { - elements = append(elements, colors.GreenBold, fmt.Sprintf("[return (%v)]", *outputArgumentsDisplayText), colors.Reset, "\n") + if outputArgumentsDisplayText != nil { + elements = append(elements, colors.GreenBold, fmt.Sprintf("[return (%v)]", *outputArgumentsDisplayText), colors.Reset, "\n") + } else { + elements = append(elements, colors.GreenBold, "[return]", colors.Reset, "\n") + } return elements } From efa94f136563c8dbc4faabe86694e51ce64acbac Mon Sep 17 00:00:00 2001 From: Anish Naik Date: Fri, 1 Sep 2023 14:41:00 -0400 Subject: [PATCH 11/14] encode bytes and byteX as hex strings --- fuzzing/config/config_defaults.go | 20 +++++++++++--------- fuzzing/fuzzer_test.go | 4 ++-- fuzzing/valuegeneration/abi_values.go | 12 ++++-------- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/fuzzing/config/config_defaults.go b/fuzzing/config/config_defaults.go index 1ac0b425..41e0db9f 100644 --- a/fuzzing/config/config_defaults.go +++ b/fuzzing/config/config_defaults.go @@ -4,6 +4,7 @@ import ( testChainConfig "github.com/crytic/medusa/chain/config" "github.com/crytic/medusa/compilation" "github.com/rs/zerolog" + "math/big" ) // GetDefaultProjectConfig obtains a default configuration for a project. It populates a default compilation config @@ -32,15 +33,16 @@ func GetDefaultProjectConfig(platform string) (*ProjectConfig, error) { // Create a project configuration projectConfig := &ProjectConfig{ Fuzzing: FuzzingConfig{ - Workers: 10, - WorkerResetLimit: 50, - Timeout: 0, - TestLimit: 0, - CallSequenceLength: 100, - TargetContracts: []string{}, - ConstructorArgs: map[string]map[string]any{}, - CorpusDirectory: "", - CoverageEnabled: true, + Workers: 10, + WorkerResetLimit: 50, + Timeout: 0, + TestLimit: 0, + CallSequenceLength: 100, + TargetContracts: []string{}, + TargetContractsBalances: []*big.Int{}, + ConstructorArgs: map[string]map[string]any{}, + CorpusDirectory: "", + CoverageEnabled: true, SenderAddresses: []string{ "0x10000", "0x20000", diff --git a/fuzzing/fuzzer_test.go b/fuzzing/fuzzer_test.go index 104592f8..df6d7a43 100644 --- a/fuzzing/fuzzer_test.go +++ b/fuzzing/fuzzer_test.go @@ -263,8 +263,8 @@ func TestConsoleLog(t *testing.T) { // These are the logs that should show up in the execution trace expectedLogs := []string{ "2", - "hello world", - "byte", + "68656c6c6f20776f726c64", + "62797465", "i is 2", "% bool is true, addr is 0x0000000000000000000000000000000000000000, u is 100", } diff --git a/fuzzing/valuegeneration/abi_values.go b/fuzzing/valuegeneration/abi_values.go index 56232e9d..616c8305 100644 --- a/fuzzing/valuegeneration/abi_values.go +++ b/fuzzing/valuegeneration/abi_values.go @@ -443,21 +443,17 @@ func encodeABIArgumentToString(inputType *abi.Type, value any) (string, error) { } return strconv.QuoteToASCII(str), nil case abi.BytesTy: - // Prepare a byte array. Return as a string enclosed with "". The returned string uses Go escape - // sequences (\t, \n, \xFF, \u0100) for non-ASCII characters and non-printable characters. b, ok := value.([]byte) if !ok { return "", fmt.Errorf("could not encode dynamic-sized bytes as the value provided is not of the correct type") } - return strconv.QuoteToASCII(string(b)), nil + // Convert the fixed byte array to a hex string + return hex.EncodeToString(b), nil case abi.FixedBytesTy: - // Prepare a fixed-size byte array. Return as a string enclosed with "". The returned string uses Go escape - // sequences (\t, \n, \xFF, \u0100) for non-ASCII characters and non-printable characters. - // TODO: Error checking to ensure `value` is of the correct type. b := reflectionutils.ArrayToSlice(reflect.ValueOf(value)).([]byte) - // Convert the byte array to a string and use the QuoteToASCII method to format the string with Go escape sequences. - return strconv.QuoteToASCII(string(b)), nil + // Convert the byte array to a hex string + return hex.EncodeToString(b), nil case abi.ArrayTy: // Prepare an array. Return as a string enclosed with [], where specific elements are comma-separated. reflectedArray := reflect.ValueOf(value) From 0fc196c05be1f49af5516afe50aebdfe82f80bc1 Mon Sep 17 00:00:00 2001 From: Anish Naik Date: Fri, 9 Feb 2024 13:16:30 -0500 Subject: [PATCH 12/14] fix console --- fuzzing/fuzzer_test.go | 4 ++-- go.mod | 4 ++++ go.sum | 21 +++++++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/fuzzing/fuzzer_test.go b/fuzzing/fuzzer_test.go index 64de7498..9167b183 100644 --- a/fuzzing/fuzzer_test.go +++ b/fuzzing/fuzzer_test.go @@ -265,8 +265,8 @@ func TestConsoleLog(t *testing.T) { // These are the logs that should show up in the execution trace expectedLogs := []string{ "2", - "hello world", - "byte", + "68656c6c6f20776f726c64", // This is "hello world" in hex + "62797465", // This is "byte" in hex "i is 2", "% bool is true, addr is 0x0000000000000000000000000000000000000000, u is 100", } diff --git a/go.mod b/go.mod index 358eff17..3ab7524a 100644 --- a/go.mod +++ b/go.mod @@ -32,6 +32,8 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/deckarep/golang-set/v2 v2.1.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect + github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e // indirect + github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 // indirect github.com/getsentry/sentry-go v0.18.0 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-stack/stack v1.8.1 // indirect @@ -64,7 +66,9 @@ require ( github.com/tklauser/numcpus v0.6.0 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect + golang.org/x/mod v0.9.0 // indirect golang.org/x/text v0.13.0 // indirect + golang.org/x/tools v0.7.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 5fbe90ee..9a6d1eb2 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,8 @@ github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqR github.com/VictoriaMetrics/fastcache v1.12.0 h1:vnVi/y9yKDcD9akmc4NqAoqgQhJrOwUF+j9LTgn4QDE= github.com/VictoriaMetrics/fastcache v1.12.0/go.mod h1:tjiYeEfYXCqacuvYw/7UoDIeJaNxq6132xHICNP77w8= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= @@ -38,6 +40,7 @@ github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoG github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811/go.mod h1:Nb5lgvnQ2+oGlE/EyZy4+2/CxRh9KfvCXnag1vtpxVM= github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= @@ -54,6 +57,7 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= @@ -67,11 +71,15 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e h1:bBLctRc7kr01YGvaDfgLbTwjFNW5jdp5y5rj8XXBHfY= +github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fxamacker/cbor v1.5.1 h1:XjQWBgdmQyqimslUh5r4tUGmoqzHmBFQOImkWGi2awg= github.com/fxamacker/cbor v1.5.1/go.mod h1:3aPGItF174ni7dDzd6JZ206H8cmr4GDNBGpPa971zsU= +github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 h1:IZqZOB2fydHte3kUgxrzK5E1fW7RQGeDwE8F/ZZnUYc= +github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8= github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= @@ -81,6 +89,7 @@ github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/ github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= @@ -152,9 +161,12 @@ github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/ github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g= github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8= github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE= @@ -207,7 +219,9 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= @@ -275,6 +289,7 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -334,6 +349,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -423,6 +440,8 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -454,6 +473,7 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -472,6 +492,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From a2cab0f0ed9ddc46c9faa0cf72f570a06feaaa0e Mon Sep 17 00:00:00 2001 From: Anish Naik Date: Tue, 20 Feb 2024 08:33:31 -0500 Subject: [PATCH 13/14] updates from PR review --- fuzzing/config/config.go | 18 ++++++------------ fuzzing/test_case_assertion_provider.go | 2 +- fuzzing/test_case_optimization_provider.go | 2 +- fuzzing/test_case_property_provider.go | 2 +- .../utils/fuzz_method_utils.go | 0 5 files changed, 9 insertions(+), 15 deletions(-) rename utils/testing_provider_utils.go => fuzzing/utils/fuzz_method_utils.go (100%) diff --git a/fuzzing/config/config.go b/fuzzing/config/config.go index 54557923..79c445aa 100644 --- a/fuzzing/config/config.go +++ b/fuzzing/config/config.go @@ -280,26 +280,22 @@ func (p *ProjectConfig) Validate() error { // Verify the worker count is a positive number. if p.Fuzzing.Workers <= 0 { - return errors.New("worker count must be a positive number (update fuzzing.workers in the project config " + - "or use the --workers CLI flag)") + return errors.New("worker count must be a positive number") } // Verify that the sequence length is a positive number if p.Fuzzing.CallSequenceLength <= 0 { - return errors.New("call sequence length must be a positive number (update fuzzing.callSequenceLength in " + - "the project config or use the --seq-len CLI flag)") + return errors.New("call sequence length must be a positive number") } // Verify the worker reset limit is a positive number if p.Fuzzing.WorkerResetLimit <= 0 { - return errors.New("worker reset limit must be a positive number (update fuzzing.workerResetLimit in the " + - "project config)") + return errors.New("worker reset limit must be a positive number") } // Verify timeout if p.Fuzzing.Timeout < 0 { - return errors.New("timeout must be a positive number (update fuzzing.timeout in the project config or " + - "use the --timeout CLI flag)") + return errors.New("timeout must be a positive number") } // Verify gas limits are appropriate @@ -325,14 +321,12 @@ func (p *ProjectConfig) Validate() error { // Verify that senders are well-formed addresses if _, err := utils.HexStringsToAddresses(p.Fuzzing.SenderAddresses); err != nil { - return errors.New("sender address(es) must be well-formed (update fuzzing.senderAddresses in the project " + - "config or use the --senders CLI flag)") + return errors.New("sender address(es) must be well-formed") } // Verify that deployer is a well-formed address if _, err := utils.HexStringToAddress(p.Fuzzing.DeployerAddress); err != nil { - return errors.New("deployer address must be well-formed (update fuzzing.deployerAddress in the project " + - "config or use the --deployer CLI flag)") + return errors.New("deployer address must be well-formed") } // Ensure that the log level is a valid one diff --git a/fuzzing/test_case_assertion_provider.go b/fuzzing/test_case_assertion_provider.go index 839afb17..af1a0b47 100644 --- a/fuzzing/test_case_assertion_provider.go +++ b/fuzzing/test_case_assertion_provider.go @@ -5,7 +5,7 @@ import ( "github.com/crytic/medusa/fuzzing/calls" "github.com/crytic/medusa/fuzzing/config" "github.com/crytic/medusa/fuzzing/contracts" - "github.com/crytic/medusa/utils" + "github.com/crytic/medusa/fuzzing/utils" "github.com/ethereum/go-ethereum/accounts/abi" "golang.org/x/exp/slices" "sync" diff --git a/fuzzing/test_case_optimization_provider.go b/fuzzing/test_case_optimization_provider.go index cfc47efe..93c7e536 100644 --- a/fuzzing/test_case_optimization_provider.go +++ b/fuzzing/test_case_optimization_provider.go @@ -5,7 +5,7 @@ import ( "github.com/crytic/medusa/fuzzing/calls" "github.com/crytic/medusa/fuzzing/contracts" "github.com/crytic/medusa/fuzzing/executiontracer" - "github.com/crytic/medusa/utils" + "github.com/crytic/medusa/fuzzing/utils" "github.com/ethereum/go-ethereum/core" "golang.org/x/exp/slices" "math/big" diff --git a/fuzzing/test_case_property_provider.go b/fuzzing/test_case_property_provider.go index 91184d55..9f5d8277 100644 --- a/fuzzing/test_case_property_provider.go +++ b/fuzzing/test_case_property_provider.go @@ -5,7 +5,7 @@ import ( "github.com/crytic/medusa/fuzzing/calls" "github.com/crytic/medusa/fuzzing/contracts" "github.com/crytic/medusa/fuzzing/executiontracer" - "github.com/crytic/medusa/utils" + "github.com/crytic/medusa/fuzzing/utils" "github.com/ethereum/go-ethereum/core" "golang.org/x/exp/slices" "math/big" diff --git a/utils/testing_provider_utils.go b/fuzzing/utils/fuzz_method_utils.go similarity index 100% rename from utils/testing_provider_utils.go rename to fuzzing/utils/fuzz_method_utils.go From 6399bb0e9d15328bedc416ea183fd14bc2662a48 Mon Sep 17 00:00:00 2001 From: Anish Naik Date: Tue, 20 Feb 2024 08:43:59 -0500 Subject: [PATCH 14/14] change config language --- fuzzing/config/config.go | 16 ++++++++-------- fuzzing/fuzzer.go | 3 +-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/fuzzing/config/config.go b/fuzzing/config/config.go index 79c445aa..fd12bd53 100644 --- a/fuzzing/config/config.go +++ b/fuzzing/config/config.go @@ -280,30 +280,30 @@ func (p *ProjectConfig) Validate() error { // Verify the worker count is a positive number. if p.Fuzzing.Workers <= 0 { - return errors.New("worker count must be a positive number") + return errors.New("project configuration must specify a positive number for the worker count") } // Verify that the sequence length is a positive number if p.Fuzzing.CallSequenceLength <= 0 { - return errors.New("call sequence length must be a positive number") + return errors.New("project configuration must specify a positive number for the transaction sequence lengt") } // Verify the worker reset limit is a positive number if p.Fuzzing.WorkerResetLimit <= 0 { - return errors.New("worker reset limit must be a positive number") + return errors.New("project configuration must specify a positive number for the worker reset limit") } // Verify timeout if p.Fuzzing.Timeout < 0 { - return errors.New("timeout must be a positive number") + return errors.New("project configuration must specify a positive number for the timeout") } // Verify gas limits are appropriate if p.Fuzzing.BlockGasLimit < p.Fuzzing.TransactionGasLimit { - return errors.New("project config must specify a block gas limit which is not less than the transaction gas limit") + return errors.New("project configuration must specify a block gas limit which is not less than the transaction gas limit") } if p.Fuzzing.BlockGasLimit == 0 || p.Fuzzing.TransactionGasLimit == 0 { - return errors.New("project config must specify a block and transaction gas limit which are non-zero") + return errors.New("project configuration must specify a block and transaction gas limit which are non-zero") } // Log warning if max block delay is zero @@ -321,12 +321,12 @@ func (p *ProjectConfig) Validate() error { // Verify that senders are well-formed addresses if _, err := utils.HexStringsToAddresses(p.Fuzzing.SenderAddresses); err != nil { - return errors.New("sender address(es) must be well-formed") + return errors.New("project configuration must specify only well-formed sender address(es)") } // Verify that deployer is a well-formed address if _, err := utils.HexStringToAddress(p.Fuzzing.DeployerAddress); err != nil { - return errors.New("deployer address must be well-formed") + return errors.New("project configuration must specify only a well-formed deployer address") } // Ensure that the log level is a valid one diff --git a/fuzzing/fuzzer.go b/fuzzing/fuzzer.go index a73422cd..3b2da8fe 100644 --- a/fuzzing/fuzzer.go +++ b/fuzzing/fuzzer.go @@ -421,8 +421,7 @@ func chainSetupFromCompilations(fuzzer *Fuzzer, testChain *chain.TestChain) erro // If we did not find a contract corresponding to this item in the deployment order, we throw an error. if !found { - return fmt.Errorf("%v was specified in the target contracts (see fuzzing.targetContracts in the "+ - "project config or the --target-contracts CLI flag) but was not found in the compilation artifacts", contractName) + return fmt.Errorf("%v was specified in the target contracts but was not found in the compilation artifacts", contractName) } } return nil