I tried to create a function which could return me 2 different booleans after I call it two times in a row. I tried my best to use different random methods like storing block.timestamp in an uint, or using the keccak256 on different values and compare them, but i can't manage to get it to return once a true and once a false on a consistent manner. I have also have to say that the function is view only, so making a storage variable and increment it doesn't work, I have to work only with memory variables. If anyone have an idea on how can I get that done would be great.
-
1This post is straight up asking for the solution for an ongoing CTF (quillctf.super.site/challenges/quillctf-challenges/true-xor) , please don't answer it.– FoxxxeyJan 17, 2023 at 22:52
-
@Foxxxey why would I ever need a function like this? Or is this just a fun task to hone solidity skills?– SkyJan 17, 2023 at 22:55
-
1@Sky 2nd option, yeah. It's just a challenge– FoxxxeyJan 17, 2023 at 22:56
-
1You should consider things that change between calls. Time doesn't change, neither msg.sender.– Ismael ♦Jan 19, 2023 at 14:37
1 Answer
So, i know this is a very old topic but i was reviewing my raised flags and found that a mod suggested that i answer this question after the CTF was over (which is definitely the case by now). There's a few approaches to this one, the one i'm gonna present here is the one i went with. My code was
contract BoolGiver is IBoolGiver {
uint a = 1;
function giveBool() external view returns(bool) {
uint gasBefore = gasleft();
uint b = a;
uint gasAfter = gasleft();
return(gasBefore - gasAfter >= 2100);
}
}
Let's break it down and explain why it works.
The code simply records the gas it costs to access a storage variable (its value and what is done with is arent important here, only how much gas it costs) and returns true if it's 2100 or more, and false otherwise. This is gonna return true the first time the function is called in a transaction, and false for any subsequent calls.
So, why does that work? After the Berlin hard fork, the gas cost for the SLOAD opcode (reading a storage slot) became dynamic. If the slot is cold (i.e. It has never been accessed (read from or wrote to) during the transaction, SLOAD costs 2100 gas, if the slot is warm (= has been accessed before during the transaction), it costs 100 gas. So, leveraging that mechanic of the EVM to get a view function that returns 2 different bools when it's called 2 times in a row is trivial.
The official solution just uses the total amount of gas consumed by the transaction, and is arguably less good as it fully depends on the amount of gas sent with the transaction in the first place to work.
Relevant links https://www.evm.codes/#54?fork=shanghai / https://eips.ethereum.org/EIPS/eip-2929 / https://hackmd.io/@fvictorio/gas-costs-after-berlin