Search code examples
ethereumsoliditysmartcontracts

Smart contracts: If i call a function that is using a public variable, can it be modified by the call of a function of other user?


I am making a decentralized crowd funding smart contract in solidity to learn more about the language.

The contract is able to store many crowdfunding projects in a mapping that looks like this

struct CrowdFundingProject{
    address author,
    string description,
    string title,
    uint goal,
    bool exists
}
mapping(uint, CrowdFundingProject) public projects;
uint lastProject = 0;

the key of the mapping is an autoincremented number. When you create a project it looks like this:

function createProject(string title, string desc, uint goal) public{
    CrowdFundingProject newProject;
    newProject.author = msg.sender;
    newProject.title = title;
    newProject.description = desc;
    newProject.goal = goal;
    newProject.exists = true;
    lastProject += 1;
    projects[lastProject] = newProject;
}

But i belive that this could cause problems, for example, if user A ran the function createProject and just when the EVM is executing lastProject += 1; User B runs the function too, making that if lastProject was equal to 100 it is now 101 at the same time that projects[lastProject] = newProject; wasn't done executing for user A, both would have a project with the ID 101. which would overwrite the project for user A and just leave the one for user B.

Is this correct? If so, my other approach is instead of directly adding the project as project[lastProject] it would be better to do something like

 lastProject += 1;
 uint myProjectsId = lastProject - 1;
 projects[myProjectsId] =  newProject;

I don't know if this approach would cause any sort of error, or if the overwriting error is even possible, so i want to make sure:

Can an error like the overlapping IDs happen? If so: Would the second approach that I proposed come with any weird behavior?


Solution

  • if user A ran the function createProject and just when the EVM is executing lastProject += 1; User B runs the function too

    The EVM executes transactions in series, not in parallel.

    Transactions in each block are ordered in a way that the block miner chooses. Most miners simply order from largest gasPrice to smallest.

    So based on the provided code, there's no way to overwrite the projects key, even if two separate transactions execute the createProject() function in the same block: The transaction with larger gasPrice will get project ID 100, and the other will execute milliseconds later and will get project ID 101.


    There's an attack vector called frontrunning, related to ordering transactions in the block based on their gasPrice. It might be useful to read up about it, for example there's a great article describing the basics.

    Your code is vulnerable to this vector, but it practically doesn't matter because the attacker could only frontrun someone and steal their desired project ID (leaving the original project creator with ID higher by 1). Usual incentives for frontrunners are stealing ETH and tokens (and your code doesn't show working with ETH nor tokens).