简体   繁体   English

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

[英]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.我遇到了一个测试问题,该测试似乎表明 Solidity 无法更改合约存储变量的值。

Here's the test in JavaScript:这是 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:这是 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;
  }
}

And here are the relevant portions of the output from the test runner:以下是来自测试运行器的 output 的相关部分:

  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.此外,我尝试在对returnAndIncrement()的调用之间插入 5 秒的暂停,以试图打破任何现有的比赛,但对结果没有影响。

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;所以我从 100 而不是 0 开始currentValue ,如上所示; 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 .我的第三个想法是,当我认为我将currentValue的值复制到value中时,我实际上正在做的是让value成为对currentValue的值的引用,这样当我增加currentValue时,我也在增加value But if that were the case, I'd be getting [101, 102, 103] back instead of [100, 100, 100] .但如果是这样的话,我会得到[101, 102, 103]而不是[100, 100, 100]

To change the state of a smart contract you need to send a transaction instead of call.要更改智能合约的 state,您需要发送交易而不是调用。

Change:改变:

subject.returnAndIncrement.call ()

To:至:

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

For more details look at the web3js doc有关更多详细信息,请查看 web3js 文档

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;但是 send transaction 的返回值并不是你要找的值,你可能需要查看日志才能得到值;

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.编辑:或者一点 Solidity 魔法。 :) 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. :) 这实际上具有稍微便宜的 gas 成本(28432 与上面示例中的 29284 相比),因为访问(昂贵的)存储的机会较少。

uint32 private currentValue = 100;

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 无法访问状态时如何测试对象? - How to test an object when I can't access state? 为什么我找不到XCTest / XCTest.h? - Why can't I find XCTest/XCTest.h? 为什么我不能测试是否解决了正确的通用视图? - Why can't I test if correct generic view is resolved? 在调试检查相等性的合同违规时如何获取更多信息 - How can I get more information when debugging a contract violation that checks for equality 当同事可以时,为什么不能从Visual Studio运行Nunit测试? - Why can't I run Nunit tests from Visual Studio when colleague can? 我可以将此功能测试更改为单元测试吗? - Can I change this feature test to a unit test? 如何对依赖另一个类的类进行单元测试以更改某些状态? - How do I unit-test a class that depends on another class to change the state of something? moq-为什么不能使用默认参数操作功能 - moq- why can't operate function with default parameter 我是否可以更改私有方法可见性以对其进行单元测试 - Can I change a private methods visibility in order to unit test them 为什么Microsoft.VisualStudio.TestTools.UnitTesting无法获得DbSet <T> 从数据库返回 - Why can't Microsoft.VisualStudio.TestTools.UnitTesting get the DbSet<T> returned from database
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM