diff --git a/fuzzing/config/config.go b/fuzzing/config/config.go index eac6c371..b5e48d6e 100644 --- a/fuzzing/config/config.go +++ b/fuzzing/config/config.go @@ -63,6 +63,9 @@ type FuzzingConfig struct { // campaigns. SenderAddresses []string `json:"senderAddresses"` + // ContractStartingBalance describes the starting balance that should be allocated to deployed contracts + ContractStartingBalance uint64 `json:"contractStartingBalance"` + // MaxBlockNumberDelay describes the maximum distance in block numbers the fuzzer will use when generating blocks // compared to the previous. MaxBlockNumberDelay uint64 `json:"blockNumberDelayMax"` diff --git a/fuzzing/config/config_defaults.go b/fuzzing/config/config_defaults.go index 74cd76f4..d7b8c11f 100644 --- a/fuzzing/config/config_defaults.go +++ b/fuzzing/config/config_defaults.go @@ -46,11 +46,12 @@ func GetDefaultProjectConfig(platform string) (*ProjectConfig, error) { "0x20000", "0x30000", }, - DeployerAddress: "0x30000", - MaxBlockNumberDelay: 60480, - MaxBlockTimestampDelay: 604800, - BlockGasLimit: 125_000_000, - TransactionGasLimit: 12_500_000, + DeployerAddress: "0x30000", + ContractStartingBalance: 0, + MaxBlockNumberDelay: 60480, + MaxBlockTimestampDelay: 604800, + BlockGasLimit: 125_000_000, + TransactionGasLimit: 12_500_000, Testing: TestingConfig{ StopOnFailedTest: true, StopOnFailedContractMatching: false, diff --git a/fuzzing/fuzzer.go b/fuzzing/fuzzer.go index 4686eba8..54c541ee 100644 --- a/fuzzing/fuzzer.go +++ b/fuzzing/fuzzer.go @@ -396,10 +396,40 @@ func chainSetupFromCompilations(fuzzer *Fuzzer, testChain *chain.TestChain) erro if block.MessageResults[0].Receipt.Status != types.ReceiptStatusSuccessful { return fmt.Errorf("contract deployment tx returned a failed status: %v", block.MessageResults[0].ExecutionResult.Err) } + // Obtain deployed contract address + contractAddress := block.MessageResults[0].Receipt.ContractAddress + if fuzzer.config.Fuzzing.ContractStartingBalance > 0 { + // Create a new call message to give the contract starting balance + msg = calls.NewCallMessage(fuzzer.deployer, &contractAddress, 0, big.NewInt(int64(fuzzer.config.Fuzzing.ContractStartingBalance)), fuzzer.config.Fuzzing.BlockGasLimit, nil, nil, nil, nil) + msg.FillFromTestChainProperties(testChain) + + // Create a new pending block we'll commit to chain + block, err = testChain.PendingBlockCreate() + if err != nil { + return err + } + + // Add transaction to the block + err = testChain.PendingBlockAddTx(msg.ToCoreMessage()) + if err != nil { + return err + } + + // Commit the pending block to the chain, so it becomes the new head. + err = testChain.PendingBlockCommit() + if err != nil { + return err + } + + // Ensure our transaction succeeded + if block.MessageResults[0].Receipt.Status != types.ReceiptStatusSuccessful { + return fmt.Errorf("tx to increase contract balance returned a failed status: %v. Do you have a receive function in your contract? ", block.MessageResults[0].ExecutionResult.Err) + } + } // Record our deployed contract so the next config-specified constructor args can reference this // contract by name. - deployedContractAddr[contractName] = block.MessageResults[0].Receipt.ContractAddress + deployedContractAddr[contractName] = contractAddress // 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. diff --git a/fuzzing/fuzzer_test.go b/fuzzing/fuzzer_test.go index 7fdc4f47..b0f3aaa4 100644 --- a/fuzzing/fuzzer_test.go +++ b/fuzzing/fuzzer_test.go @@ -426,6 +426,33 @@ func TestDeploymentsSelfDestruct(t *testing.T) { } } +// TestDeployedContractStartingBalance runs tests to ensure deployed contracts can be given a starting balance. +func TestDeployedContractStartingBalance(t *testing.T) { + filePaths := []string{ + "testdata/contracts/deployments/starting_balance.sol", + } + for _, filePath := range filePaths { + runFuzzerTest(t, &fuzzerSolcFileTest{ + filePath: filePath, + configUpdates: func(config *config.ProjectConfig) { + config.Fuzzing.DeploymentOrder = []string{"DeployedContractStartingBalance"} + config.Fuzzing.Testing.AssertionTesting.Enabled = true + config.Fuzzing.ContractStartingBalance = 3_000 + config.Fuzzing.TestLimit = 1_000 + config.Fuzzing.Testing.StopOnFailedContractMatching = true + }, + method: func(f *fuzzerTestContext) { + // Start the fuzzer + err := f.fuzzer.Start() + assert.NoError(t, err) + + // Check for any failed tests + assertFailedTestsExpected(f, false) + }, + }) + } +} + // TestExecutionTraces runs tests to ensure that execution traces capture information // regarding assertion failures, revert reasons, etc. func TestExecutionTraces(t *testing.T) { diff --git a/fuzzing/testdata/contracts/deployments/starting_balance.sol b/fuzzing/testdata/contracts/deployments/starting_balance.sol new file mode 100644 index 00000000..da327adb --- /dev/null +++ b/fuzzing/testdata/contracts/deployments/starting_balance.sol @@ -0,0 +1,8 @@ +// DeployedContractStartingBalance checks the balance of a contract after deployment to ensure the starting balance was properly set +contract DeployedContractStartingBalance { + receive() external payable {} + + function checkBalance() public { + assert(address(this).balance == 3000); + } +} \ No newline at end of file