为什么我无法在 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, 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)

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

我的第二个想法是,我的测试配置有一些根本性的错误,所以我只是得到零,而不管实际发生了什么。所以我把currentValue设为100,而不是0,正如你上面看到的;这不是问题所在。

我的第三个想法是,当我认为我把currentValue的值复制到value时,我实际上是在让value引用currentValue的值,这样当我增加currentValue时,我也增加了value。但如果是这样的话,我会得到[101, 102, 103]而不是[100, 100, 100]

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

改变:

subject.returnAndIncrement.call ()

:

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

更多细节请看web3js文档

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

编辑:或者一点固体魔法。:)这实际上有一个稍微便宜的gas成本(28432相对于29284在上面的例子中),因为有更少的访问(昂贵的)存储。

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

最新更新