简体   繁体   中英

Solidity - Truffle: Error: VM Exception while processing transaction: invalid opcode NOT because of revert

I am working on a smart contract, and I am testing it by deploying it on truffle. While it compiles fine, when I call the train() function, I get the following error:

Error: VM Exception while processing transaction: invalid opcode

After reading a bit on this, I understood it is usually caused after a revert has occurred, so I tried commenting out the 2 require functions I had just to see if it would behave differently, and it did not.

Checking out this question did not help me, or I did not see how it could.

Here is the train() function, as well as the mapping and struct type I am using in it. I should note that upon creation of a Developer, their wallet is set to 300 so I do not see how the first call of the train function by the owner could revert.

struct Developer {
    address owner;
    string name;
    bytes32 namehash;
    bytes32[] skills;
    uint256[] skill_levels;
    uint wallet;
}

mapping (bytes32=>Developer) public developers_all;

function train(string _name, bytes32 _skill) public {
    bytes32 h = keccak256(abi.encodePacked(_name));

    require(developers_all[h].owner == msg.sender, "Only the owner of the developer can train them");
    require(developers_all[h].wallet >= 150, "Insufficient funds");

    uint256 i = 0; 

    do {
        if (developers_all[h].skills[i] == _skill) {
            developers_all[h].skill_levels[i]++;
        } else if ((i == (developers_all[h].skills.length - 1)) || (developers_all[h].skills.length == 0)) {
            developers_all[h].skills.push(_skill);
            developers_all[h].skill_levels.push(1);
        }
        i++;
    } while (i < developers_all[h].skills.length);

    developers_all[h].wallet = developers_all[h].wallet - 150;
}

Thank you for any help.

This is most likely because you are trying to access the first entry of an empty array. You're using a do while loop, and you're trying to access developers_all[h].skills[i] before you're checking developers_all[h].skills.length == 0 , so it is possible that the array is empty at the first if statement in the do while.

You could rewrite the code to something like the following, to make sure you're never accessing an unassigned array slot.

bool foundSkill = false;

for (uint i = 0; i < developers_all[h].skills.length; i++) {
    if (developers_all[h].skills[i] == _skill) {
        developers_all[h].skill_levels[i]++;
        foundSkill = true;
        break;
    }
}

if (!foundSkill) {
    developers_all[h].skills.push(_skill);
    developers_all[h].skill_levels.push(1);
}

Do note that looping through the whole array and doing the comparisons is quite costly, and can become impossible if the array size gets too big. You might want consider changing the structure to something like:

struct Developer {
    address owner;
    string name;
    bytes32 namehash;
    mapping(bytes32 => uint) skill_levels;
    uint wallet;
}

That way you could just replace the whole thing with

developers_all[h].skill_levels[skill]++;

But you wouldn't be able to loop over the skills.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM