According to the documentation, block.timestamp should be the timestamp of the last mined block.
However, in truffle test environment, block.timestamp keeps increasing(seemingly the current timestamp) when returned in a view function.
Calling the function manually through web3 interface, it returns the expected value(unchanged timestamp of last mined block).
Fork https://github.com/vzts/block-timestamp-test, setup local mining node(e.g. ganache), run truffle test.
Below is the minimum example code:
(in a solidity contract):
function test() public view returns(uint, uint) {
return (block.timestamp, block.number);
}
(in a truffle js test case):
it("test", async function () {
for (let i = 0; i < 100; i++) {
console.log(await contractInstance.test())
sleep(1000) // pseudo code for sleep
}
})
Should return the same timestamp and the number for the last mined block upon every request (in an environment where no new transaction is executed(no new block is mined))
Sample output:
["1524690793", "157"]
["1524690793", "157"]
["1524690793", "157"]
["1524690793", "157"]
["1524690793", "157"]
["1524690793", "157"]
["1524690793", "157"]
Returning the "current"(not sure what is happening, but it seems so) timestamp.
Sample output:
["1524690799", "157"]
["1524690800", "157"]
["1524690801", "157"]
["1524690802", "157"]
["1524690803", "157"]
["1524690804", "157"]
["1524690805", "157"]
truffle version): 4.1.7node --version): 8.4.0npm --version): 6.0.0@vzts Great issue! Thanks for such a clear description. Will look into this and see what's going on.
@cgewecke Thanks for looking into it! I've just tested this with other ethereum client(testrpc), and it looks like the issue with ganache-cli! In testrpc(the fork before ganache-cli was rebranded), it worked as expected(constant timestamp of last mined block), however in ganache-cli it yielded the increasing timestamp.
After doing some research, I found it may have something to do with this issue(https://github.com/trufflesuite/ganache-cli/issues/336). I guess there is something messed up in interface layer inside ganache-cli, since accessing it with truffle test and with web3 interface are yielding two different results.
According to this old Q&A, Gav(I guess Gavin Wood, the guy who wrote the yellow paper) said:
_The return of block.timestamp can only be given as a best effort as will all other external execution environment parameters. Fundamentally, they're impossible to "know", except on the final mine, and since constant functions are never mined, they can never be properly defined. In cpp they take on values as they would if the client were about to mine them. It's a misunderstanding of the execution environment to believe 'now' would be wall time; all language is taken from the perspective of the contract executing, not from the user authoring/running._
So basically I think the people who design the client needs to define how block.timestamp behaves for constant functions(now a view or pure function), but they need to be consistent in how they want to define it. Right now I'm not sure how the key contributers in ganache-cli want to define it, but it's working inconsistently as far as I tested. It would be great if we can set a consensus on how block.timestamp(a.k.a now) should be defined in eth_calls and make it consistent over all interfaces.
Personally I think the block.timestamp should be the timestamp of last mined block since it's clear that calls are never mined. Or I would suggest to see how real-rpcs(parity or geth), has defined it.
Thanks a lot for maintaining this great project! If you think it's more proper to move the issue to ganache-cli repo, please feel free to do so.
@vzts Will point @benjamincburns here and get his sense of this. Thank you so much for investigating. Are you using web3 1.0 for the comparison?
I tested with both web3 1.0 beta, and [web3 0.x + truffle-contract] for comparison. The outcome was the same with consistent timestamp of last mined block.
To add one more finding, it seems that web3 interactions of view function in ganache-cli wasn't even logged as eth_call, both in web3 1.0 and 0.x. Maybe that's why it isn't increasing the time.
@vzts Yes, it looks to me like this might need to be handled at ganache-core. calls there are executed by creating a (fake) block whose timestamp is set here. It should probably be reset to the correct value (e.g. the latest block value) in processCall.
@vzts Update, just had a chat with @benjamincburns, the engineer who writes ganache. He suggested there's enough ambiguity about correct behavior here that we should really look at how geth and parity handle this. If they both let the clock tick, ganache should tick. If only one lets the clock tick, ganache should side with keeping block.timestamp fixed at the latest block because that's more intuitive (and more useful for tests ).
@cgewecke Thanks for the update! Absolutely agree on benjamin's opinion of how we should define "correct behavior". Since ganache is a test node, it is preferable to emulate well how real node behaves in such issues, so that users don't get confused in the wild when they actually deploy. So much for today, I will let you know if I have any new observation. Cheers!馃嵒
@vzts Update: Have run your test against both geth dev and a Parity POA network and both report a static time within the block, so Ganache's behavior is non-normative. Will report back to @benjamincburns and open a PR over there fixing this.
Hope you don't mind but I'm going to close this and open a referral issue over there. Thanks again for discovering this.
@cgewecke That's great! I really appreciate your hard work!
Most helpful comment
@vzts Update, just had a chat with @benjamincburns, the engineer who writes ganache. He suggested there's enough ambiguity about correct behavior here that we should really look at how geth and parity handle this. If they both let the clock tick, ganache should tick. If only one lets the clock tick, ganache should side with keeping
block.timestampfixed at the latest block because that's more intuitive (and more useful for tests ).