为什么我不能在 Solidity 中更改合约状态?

问题描述

我在测试中遇到问题,似乎表明 solidity 无法更改合约存储变量的值。

这是在 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]
        );
    });
});

这是 solidity 代码

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;
  }
}

这里是测试运行器输出的相关部分:

  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,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)

我的第一个想法是存在某种竞争条件:在它们中的任何一个有机会增加初始值之前,所有三个调用都在获取初始值。但是我的阅读表明以太坊将操作序列化,因此您无法在单个合约中进行竞争。此外,我尝试在对 returnAndIncrement()调用之间插入 5 秒的暂停,以试图打破任何现有的比赛,但对结果没有影响。

我的第二个想法是我的测试配置存在根本性的错误,所以不管实际发生了什么,我都只是得到零。所以我从 100 而不是 0 开始 currentValue,如上所示;这不是问题。

我的第三个想法是,当我认为我正在将 currentValue 的值复制到 value 中时,我实际上在做的是使 value 成为对 { 值的引用{1}},所以当我增加 currentValue 时,我也在增加 currentValue。但如果是这样的话,我会得到 value 而不是 [101,102,103]

解决方法

要更改智能合约的状态,您需要发送交易而不是调用。

变化:

subject.returnAndIncrement.call ()

致:

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

有关详细信息,请查看 web3js doc

但是send交易的返回值并不是你要找的值,你可能需要查看日志才能得到值;

,

你的赋值有点混乱,阅读代码注释:

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;                     
}

根据上下文猜测,您可能希望从存储中返回递增的值。

最简单的方法是:

uint32 private currentValue = 100;

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

编辑:或者一点 Solidity 魔法。 :) 这实际上具有稍微便宜的 gas 成本(28432 与上面示例中的 29284 相对),因为对(昂贵的)存储的访问较少。

uint32 private currentValue = 100;

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