为什么我不能在 Solidity 中更改合约状态?
Why can't I change contract state in 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]
。
你的赋值有点乱,看代码注释:
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;
}
根据上下文猜测,您可能想return存储中的增量值。
最简单的方法是:
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;
}
要更改智能合约的状态,您需要发送交易而不是调用。
变化:
subject.returnAndIncrement.call ()
收件人:
subject.returnAndIncrement.send({..}) // you can pass options such gas, account ..
有关详细信息,请查看 web3js doc
但是send transaction的return不是你要找的值,你可能需要查看日志才能得到这个值;
我 运行 遇到了一个测试问题,该测试似乎表明 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]
。
你的赋值有点乱,看代码注释:
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;
}
根据上下文猜测,您可能想return存储中的增量值。
最简单的方法是:
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;
}
要更改智能合约的状态,您需要发送交易而不是调用。
变化:
subject.returnAndIncrement.call ()
收件人:
subject.returnAndIncrement.send({..}) // you can pass options such gas, account ..
有关详细信息,请查看 web3js doc
但是send transaction的return不是你要找的值,你可能需要查看日志才能得到这个值;