[Ethereum] VM error: revert.The called function should be payable if you send value and the value you send should be less than your current balance

accesscontract-debuggingcontract-designcontract-developmentsolidity

There is already a similar question, but it doesn't help me. So I ask this question again and expect something useful information.

Two contracts named AccessControl.sol and Judge.sol. I've removed all the extraneous code, leaving only the wrong functions and get same error when call it.

First I deploy Judge contract and get its contract address. And then I deploy AccessControl contract, passed Judge contract address.

Finally I call emitError function in AccessControl.sol and see the revert error

transact to AccessControl.emitError errored: VM error: revert.
revert The transaction has been reverted to the initial state.
Note: The called function should be payable if you send value and the value you send should be less than your current balance. Debug the transaction to get more information.

I have no idea about why it happened. Here is the simplified code。

AccessControl.sol

pragma solidity >=0.4.22 <0.6.0;

contract AccessControl{
    address   public owner;
    Judge     public jc;

    constructor(address _jc) public {
        owner = msg.sender;
        jc = Judge(_jc);
    }

    function emitError(address subject) public returns (uint penalty) {
        penalty = jc.misbehaviorJudge(subject, owner, "data", "read", "Too frequent access!", now);
    }

}


contract Judge {
    function misbehaviorJudge(address _subject, address _object, string memory _resource, string memory _action, string memory _misbehavior, uint _time) public returns (uint);
}

Judge.sol

 pragma solidity >=0.4.22 <0.6.0;

contract Judge {

    struct Misbehavior{
        address subject;   //subject who performed the misbehavior 
        address device;
        string resource;
        string action;   //action (e.g., "read","write","execute") of the misbehavior
        string misbehavior;
        uint time;   //block timestamp of the Misbehavior ocured
        uint penalty;   //penalty (number of minitues blocked)
    }

    mapping (address => Misbehavior[]) public MisbehaviorList;

    function misbeaviorJudge(
        address _subject, 
        address  _device, 
        string memory _resource,
        string memory _action,
        string memory _misbehavior,
        uint  _time) 
        public returns (uint  penalty) 
    {
        penalty = MisbehaviorList[_subject].length;
        MisbehaviorList[_subject].push(Misbehavior(_subject, _device, _resource, _action, _misbehavior, _time, penalty));
    }

    function getLatestMisbehavior(address _requester) public view 
        returns (address _subject, address _device, string memory _resource, string memory _action, string memory _misbehavior, uint _time)
    {
        uint latest = MisbehaviorList[_requester].length  - 1;
        _subject = MisbehaviorList[_requester][latest].subject;
        _device = MisbehaviorList[_requester][latest].device;
        _resource = MisbehaviorList[_requester][latest].resource;
        _action = MisbehaviorList[_requester][latest].action;
        _misbehavior = MisbehaviorList[_requester][latest].misbehavior;
        _time = MisbehaviorList[_requester][latest].time;
    }
}

Best Answer

You've declared function misbehaviorJudge.

But you've implemented function misbeaviorJudge.

So the way I see it, you are trying to invoke an unimplemented function.


A couple of ways for you to avoid a similar issue next time (more precisely, convert it from a hard-to-investigate runtime problem into an easy-to-investigate compilation problem):

Option #1 - if you don't need to call function misbehaviorJudge inside contract Judge:

File IJudge.sol:

interface IJudge {
    function misbehaviorJudge(...) external returns (uint);

File Judge.sol:

import "./IJudge.sol";

contract Judge is IJudge {
    ...
}

Option #2 - if you do need to call function misbehaviorJudge inside contract Judge:

File IJudge.sol:

contract IJudge {
    function misbehaviorJudge(...) public returns (uint);

File Judge.sol:

import "./IJudge.sol";

contract Judge is IJudge {
    ...
}

Then (for both options), in file AccessControl.sol, simply use IJudge instead of Judge:

import "./IJudge.sol";

contract AccessControl {
    IJudge public jc;
    ...
}
Related Topic