ganache
ganache copied to clipboard
`eth_estimateGas` doesn't set correct block header params for `timestamp` (when `timestampIncrement != clock`) and `difficulty`
Discussed in https://github.com/trufflesuite/ganache/discussions/3368
@davidmurdoch / @MicaiahReid ,
Following on from my original discussion post. I've stripped down my code to produce a less heavy/complex example and investigated further- see the snail_breeding example (GitHub link below). I believe the issue (intermittent revert of breed function) results from a combination of use of pseudo-randomness to determine how many new snail tokens to mint before actually minting them.
The Snail_breeding example uses an ERC721 token contract (SnailToken). Each token represents a snail. Two snails may be mated together, using the breed(snailA, snailB) function, with either mating snail possibly being fertilised/conceiving and giving birth to a new snail (ie. minting a new snail token). Hence, both, one, or neither of the two mating snails may give birth to a new snail. Or in other words, the breed function pseudo-randomly mints 0, 1, or 2 new snail tokens upon each call to it.
The issue is that sometimes breed() reverts for an unknown reason. This seems prevalent upon a second test call to breed, but sometimes it fails on the first call.
I've written other test functions (based on breed to help me investigate the issue), these: a) Only perform the pseudo-random calculation of which of the two mating snails are fertilised/conceive (not minting new snails) b) Only Mint a specific amount of new snails upon being called (ie. removing the randomness of conception) These test functions have their own test scripts and all tests pass (on every run) even when these functions are repeatedly called.
The issue therefore arises only in the original breed() function that does both of these actions together, ie. a) Pseudo-randomly determines which the 2 mating snails are fertilised - setting conception details for each b) Uses the conception details to mint 0, 1, or 2 new snail tokens.
Therefore I think that the issue lies with Ganache.
https://github.com/msuscens/Snail_Breeding
Please refer to the README file which gives fuller details.
Note: You'll see that in the breed() Truffle tests, I use ganache-time-traveler(after/before tests), to return the blockchain to the same state before executing the next test.
% truffle version Truffle v5.5.3 (core: 5.5.3) Ganache v7.0.1 => whilst it states 7.0.1, I'm actually using 7.3.2 Solidity - 0.8.13 (solc-js) Node v14.15.5 Web3.js v1.5.3 (I'm on an M1 MacBook Pro, with MacOs 12.4)
The readme is very hard to read without line formatting. Also, there are still a lot of files and tests in this repo. Can you strip out everything except the first failure?
You may want to try starting Ganache with a specific timestamp (ganache --time 123456), since your tests are time dependent.
I did some more digging. I think this is a bug in Ganache's eth_estimateGas implementation caused by not predicting the next block's timestamp. When we added the --miner.timestampIncrement we didn't add this same logic to gas estimation.
You can workaround ganache's estimation error by passing in the gas yourself. Example:
it("should allow suitable mates to breed", async () => {
await truffleAssert.passes(
snailToken.breed(
A_SNAIL_ID, //mateA
B_SNAIL_ID, //mateB
// specify a high gas limit to work around https://github.com/trufflesuite/ganache/issues/3425
{from: accounts[2], gas: "0xfffff"}),
"Snail owner was unable to breed their snails"
)
})
Thanks for helping debug this one!
Thank you @davidmurdoch that's great 🙏
I applied this to the snail's breed truffle test script and can confirm the workaround has fixed the issue. Also, I went on to stress test the workaround using another test script that initially builds a family tree of snails by repeatedly calling breed (15+ calls to breed) and this also works a treat!!
I can now get back to my main project and apply the workaround to that. Thanks again for all your time and effort.