简体   繁体   中英

Why can't I change contract state in Solidity?

I'm running into problems with a test that seems to indicate that Solidity can't change the value of a contract storage variable.

Here's the test in JavaScript:

const Mystery = artifacts.require ("Mystery");

contract ("Mystery", async accounts => {

    it ("Incrementing performs as intended", async () => {
        const subject = await Mystery.deployed ();

        const firstValue = (await subject.returnAndIncrement.call ()).toNumber ();
        const secondValue = (await subject.returnAndIncrement.call ()).toNumber ();
        const thirdValue = (await subject.returnAndIncrement.call ()).toNumber ();

        assert.equal (
            [firstValue, secondValue, thirdValue],
            [100, 101, 102]
        );
    });
});

Here's the Solidity code:

pragma solidity >=0.4.22 <0.9.0;

contract Mystery {

  uint32 private currentValue = 100;

  function returnAndIncrement () public returns (uint32 value) {
    value = currentValue;
    currentValue = currentValue + 1;
    return value;
  }
}

And here are the relevant portions of the output from the test runner:

  Contract: Mystery
    1) Incrementing performs as intended
    > No events were emitted


  0 passing (993ms)
  1 failing

  1) Contract: Mystery
       Incrementing performs as intended:

      AssertionError: expected [ 100, 100, 100 ] to equal [ 100, 101, 102 ]
      + expected - actual

       [
         100
      -  100
      -  100
      +  101
      +  102
       ]
      
      at Context.it (test/TestMystery.js:12:16)
      at process._tickCallback (internal/process/next_tick.js:68:7)

My first thought was that there was some kind of race condition: that all three invocations were grabbing the initial value before any of them had a chance to increment it. But my reading indicates that Ethereum serializes operations so that you can't get races inside a single contract. Also, I tried inserting five-second pauses between the calls to returnAndIncrement() in an attempt to break any existing races, but there was no effect on the results.

My second thought was that there was something fundamental wrong with the configuration of my test, so that I was just getting zeros back regardless of what was actually happening. So I started the currentValue at 100 instead of 0, as you see above; that's not the issue.

My third thought was that when I think I'm copying the value of currentValue into value , what I'm actually doing is making value a reference to the value of currentValue , so that when I increment currentValue I'm also incrementing value . But if that were the case, I'd be getting [101, 102, 103] back instead of [100, 100, 100] .

To change the state of a smart contract you need to send a transaction instead of call.

Change:

subject.returnAndIncrement.call ()

To:

  subject.returnAndIncrement.send({..}) // you can pass options such gas, account .. 

For more details look at the web3js doc

But the return of send transaction is not the value you are looking for, you may need to look in the logs to get the value;

Your value assigning is a bit mixed up, read the code comments:

uint32 private currentValue = 100;

function returnAndIncrement () public returns (uint32 value) {
    // 1. memory variable `value` is now 100
    value = currentValue;         

    // 2. storage variable `currentValue` is now 101
    currentValue = currentValue + 1;

    // 3. you're returning the `value` from memory (which has value 100)
    return value;                     
}

Guessing by the context, you probably wanted to return the incremented value from storage.

So easiest way to do that, is:

uint32 private currentValue = 100;

function returnAndIncrement () public returns (uint32) {
    currentValue++;
    return currentValue;
}

Edit: Or a bit of Solidity magic. :) This actually has a slightly cheaper gas cost (28432 opposed to 29284 in the example above) because there's less access to the (expensive) storage.

uint32 private currentValue = 100;

function returnAndIncrement () public returns (uint32 value) {
    value = ++currentValue;
}

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