[英]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.