-
Notifications
You must be signed in to change notification settings - Fork 74
Allow Medusa to run init function (ex: setUp) once per fuzz run #618
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
c6136a8
b7f6b61
c8a5144
5b69f15
346c172
7ac775a
4834a80
d780d3c
e40797d
da18034
31b5218
1e2c1f6
0331b56
e7adb94
6471129
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -527,11 +527,14 @@ | |
| // while still being able to use the contract address overrides | ||
| contractsToDeploy := make([]string, 0) | ||
| balances := make([]*config.ContractBalance, 0) | ||
| initFunctions := make([]string, 0) | ||
|
|
||
| for contractName := range fuzzer.config.Fuzzing.PredeployedContracts { | ||
| contractsToDeploy = append(contractsToDeploy, contractName) | ||
| // Preserve index of target contract balances | ||
| balances = append(balances, &config.ContractBalance{Int: *big.NewInt(0)}) | ||
| // Set default empty init function for predeployed contracts | ||
| initFunctions = append(initFunctions, "") | ||
| } | ||
|
|
||
| if len(fuzzer.deploymentOrder) > 0 { | ||
|
|
@@ -567,6 +570,26 @@ | |
| balances = append(balances, fuzzer.config.Fuzzing.TargetContractsBalances...) | ||
| } | ||
|
|
||
| // Process target contracts init functions | ||
| targetContractsCount := len(fuzzer.config.Fuzzing.TargetContracts) | ||
| initConfigCount := len(fuzzer.config.Fuzzing.TargetContractsInitFunctions) | ||
|
|
||
| // Add initialization functions for target contracts | ||
| for i := 0; i < targetContractsCount; i++ { | ||
| initFunction := "" // Default: no initialization | ||
|
|
||
| if fuzzer.config.Fuzzing.UseInitFunctions { | ||
| if i < initConfigCount && fuzzer.config.Fuzzing.TargetContractsInitFunctions[i] != "" { | ||
| // Use explicit per-contract config | ||
| initFunction = fuzzer.config.Fuzzing.TargetContractsInitFunctions[i] | ||
| } else if len(fuzzer.config.Fuzzing.TargetContractsInitFunctions) == 1 { | ||
| // If only one init function specified (like "setUp") apply it to all contracts | ||
| initFunction = fuzzer.config.Fuzzing.TargetContractsInitFunctions[0] | ||
| } | ||
| } | ||
| initFunctions = append(initFunctions, initFunction) | ||
| } | ||
|
|
||
| deployedContractAddr := make(map[string]common.Address) | ||
| // Loop for all contracts to deploy | ||
| for i, contractName := range contractsToDeploy { | ||
|
|
@@ -613,10 +636,116 @@ | |
| } | ||
| // Record our deployed contract so the next config-specified constructor args can reference this | ||
| // contract by name. | ||
|
|
||
| deployedContractAddr[contractName] = result.(common.Address) | ||
| contractAddr := deployedContractAddr[contractName] | ||
|
|
||
| // Get the initialization function name if exists and feature is enabled | ||
| if fuzzer.config.Fuzzing.UseInitFunctions && i < len(initFunctions) && initFunctions[i] != "" { | ||
| initFunction := initFunctions[i] | ||
| fuzzer.logger.Info(fmt.Sprintf("Checking if init function %s on %s exists", initFunction, contractName)) | ||
|
|
||
| // Check if the initialization function exists | ||
| contractABI := contract.CompiledContract().Abi | ||
| if method, exists := contractABI.Methods[initFunction]; !exists { | ||
| fuzzer.logger.Info(fmt.Sprintf("Init function %s not found on %s, skipping", initFunction, contractName)) | ||
| } else { | ||
| // Initialization function exists, proceed with calling it | ||
| fuzzer.logger.Info(fmt.Sprintf("Found init function %s with %d inputs", initFunction, len(method.Inputs))) | ||
|
|
||
| // Check if the init function accepts parameters and process them if needed | ||
| var args []any | ||
| if len(method.Inputs) > 0 { | ||
| // Verify InitializationArgs map exists | ||
| if fuzzer.config.Fuzzing.InitializationArgs == nil { | ||
| fuzzer.logger.Error(fmt.Errorf("initialization args map is nil but function requires args")) | ||
| continue | ||
| } | ||
|
|
||
| // Look for initialization arguments in the config | ||
| jsonArgs, ok := fuzzer.config.Fuzzing.InitializationArgs[contractName] | ||
| if !ok { | ||
| fuzzer.logger.Error(fmt.Errorf("initialization arguments for contract %s not provided", contractName)) | ||
| continue | ||
| } | ||
|
|
||
| // Debug what args we found | ||
| fuzzer.logger.Info(fmt.Sprintf("Found args for %s: %+v", contractName, jsonArgs)) | ||
|
|
||
| // Decode the arguments | ||
| decoded, err := valuegeneration.DecodeJSONArgumentsFromMap(method.Inputs, | ||
| jsonArgs, deployedContractAddr) | ||
| if err != nil { | ||
| fuzzer.logger.Error(fmt.Errorf("decoding failed for initialization arguments for contract %s: %v", | ||
| contractName, err)) | ||
| continue | ||
| } | ||
|
|
||
| args = decoded | ||
| fuzzer.logger.Info(fmt.Sprintf("Decoded %d args for %s function %s", | ||
| len(args), contractName, initFunction)) | ||
| } | ||
|
|
||
| // Log before packing | ||
| fuzzer.logger.Info(fmt.Sprintf("About to call initialization function %s on contract %s with %d args", | ||
| initFunction, contractName, len(args))) | ||
|
|
||
| // Pack the function call data with arguments | ||
| callData, err := contractABI.Pack(initFunction, args...) | ||
| if err != nil { | ||
| fuzzer.logger.Error(fmt.Errorf("failed to encode init call to %s: %v", initFunction, err)) | ||
| continue | ||
| } | ||
|
|
||
| // Create and send the transaction | ||
| destAddr := contractAddr | ||
| msg := calls.NewCallMessage(fuzzer.deployer, &destAddr, 0, big.NewInt(0), | ||
| fuzzer.config.Fuzzing.BlockGasLimit, nil, nil, nil, callData) | ||
|
Check failure on line 703 in fuzzing/fuzzer.go
|
||
| msg.FillFromTestChainProperties(testChain) | ||
|
Comment on lines
+700
to
+704
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Useful? React with 👍 / 👎. |
||
|
|
||
| // Debug log after creating the message | ||
| fuzzer.logger.Info(fmt.Sprintf("Created message for init function call to %s", initFunction)) | ||
|
|
||
| // Create and commit a block with the transaction | ||
| block, err := testChain.PendingBlockCreate() | ||
| if err != nil { | ||
| fuzzer.logger.Error(fmt.Errorf("failed to create pending block for init call: %v", err)) | ||
| continue | ||
| } | ||
|
|
||
| if err = testChain.PendingBlockAddTx(msg.ToCoreMessage()); err != nil { | ||
| fuzzer.logger.Error(fmt.Errorf("failed to add initialization transaction for function %s on contract %s to pending block: %v", | ||
| initFunction, contractName, err)) | ||
| continue | ||
| } | ||
|
|
||
| if err = testChain.PendingBlockCommit(); err != nil { | ||
| fuzzer.logger.Error(fmt.Errorf("failed to commit block containing initialization call to function %s on contract %s: %v", | ||
| initFunction, contractName, err)) | ||
| continue | ||
| } | ||
|
|
||
| // Check if the call succeeded | ||
| if block.MessageResults[0].Receipt.Status != types.ReceiptStatusSuccessful { | ||
| // Create a call sequence element for the trace | ||
| cse := calls.NewCallSequenceElement(nil, msg, 0, 0) | ||
| cse.ChainReference = &calls.CallSequenceElementChainReference{ | ||
| Block: block, | ||
| TransactionIndex: len(block.Messages) - 1, | ||
| } | ||
|
|
||
| fuzzer.logger.Error(fmt.Errorf("init function %s call failed on %s: %v", | ||
| initFunction, contractName, | ||
| block.MessageResults[0].ExecutionResult.Err)) | ||
| } else { | ||
| fuzzer.logger.Info(fmt.Sprintf("Successfully called %s on %s with %d args", | ||
| initFunction, contractName, len(args))) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Flag that we found a matching compiled contract definition and deployed it, then exit out of this | ||
| // inner loop to process the next contract to deploy in the outer loop. | ||
| // Flag that we found a matching compiled contract definition, deployed it and called available init functions if any, | ||
| // then exit out of this inner loop to process the next contract to deploy in the outer loop. | ||
| found = true | ||
| break | ||
| } | ||
|
|
@@ -627,6 +756,7 @@ | |
| return nil, fmt.Errorf("%v was specified in the target contracts but was not found in the compilation artifacts", contractName) | ||
| } | ||
| } | ||
|
|
||
| return nil, nil | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The list of init functions is appended in the original
TargetContractsorder, but contracts are deployed indeploymentOrderwhen that list exists. When the order of deployments differs from the config array (e.g., because of library dependencies),initFunctions[i]will be applied to the wrong contract. This can cause init calls to target the wrong ABI or revert unnecessarily. Map functions by contract name or buildinitFunctionsin the same order ascontractsToDeploy.Useful? React with 👍 / 👎.