PUSH1: or "Parsing EVM Bytecode"
Have you ever encountered a JUMPDEST opcode in Ethereum bytecode and wondered how the execution can jump there? Look at this delicious 5Bs, so many places the code can run from!A lot of 5B's - the JUMPDEST opcodesIf there is a 5B you can always jump there, right?… Wrong! And the answer has to do with something called instruction boundaries.What are instruction boundaries and why do they matter?When the Ethereum Virtual Machine (EVM) processes bytecode, it loads each octet and defines whe...
Making of HappyNewYear CTF Puzzle
Imagine there is a contract that can execute any code you send to it. And imagine it also holds some money. How easy would it be to hack it? It would be quite easy, especially if all the opcodes are available. That’s why I decided to create a puzzle like this, but with a few extra tricks to make it a bit harder to solve (even if the tricks are mostly “smoke and mirrors”). I announced the puzzle on Twitter two days before 2023, and it has already been solved: https://twitter.com/0x796/status/1...


PUSH1: or "Parsing EVM Bytecode"
Have you ever encountered a JUMPDEST opcode in Ethereum bytecode and wondered how the execution can jump there? Look at this delicious 5Bs, so many places the code can run from!A lot of 5B's - the JUMPDEST opcodesIf there is a 5B you can always jump there, right?… Wrong! And the answer has to do with something called instruction boundaries.What are instruction boundaries and why do they matter?When the Ethereum Virtual Machine (EVM) processes bytecode, it loads each octet and defines whe...
Making of HappyNewYear CTF Puzzle
Imagine there is a contract that can execute any code you send to it. And imagine it also holds some money. How easy would it be to hack it? It would be quite easy, especially if all the opcodes are available. That’s why I decided to create a puzzle like this, but with a few extra tricks to make it a bit harder to solve (even if the tricks are mostly “smoke and mirrors”). I announced the puzzle on Twitter two days before 2023, and it has already been solved: https://twitter.com/0x796/status/1...

Subscribe to Convergence Boy

Subscribe to Convergence Boy
Share Dialog
Share Dialog
<100 subscribers
<100 subscribers
Crypto-twitter likes puzzles. And gas-golfing. Any CTF puzzle gains huge success, although in the end it all comes down to “test-fitting”.
Let me review a recent example from RareSkills that blew up on CT a couple of days ago:

The idea is to optimize gas in the contract and run provided tests for it that have a threshold. By default, the gas is way over the target:

So the “rules” are: no messing with optimizer or solidity version, no messing with test files, use solidity (so no huff/yul/bytecode contracts), and don’t make functions payable (which is a weird rule, but ok).
Make sure you try to solve this puzzle yourself before reading further - it’s really fun and makes you learn stuff! But if you already solved it and want to read other ways of doing it, or just struggling and want to know what’s the trick - go ahead!
Let’s do the obvious things first:
Make vars immutable instead of storage (saves 10710 gas - because immutable vars are embedded in the contract bytecode as constants instead of reading from storage)
Replace transfer with send (saves 108 gas - cause send has no check if the transfer succeeded)
Replace currentTime with endTime (saves 69 gas - and do the time calculations in constructor)
That gives us 61066 gas which is already 10887 better than original, but still 4k above the target:

There’s a particular trick this puzzle should teach you. And it’s an old way of sending ETH to addresses by SELFDESTRUCT:

This trick alone saves you 4066 gas and meets the target!
Every sane optimization always have its insane evil-brother… Let’s see how far we can push this!
Making the contract self-destructable already feels like cheating - why would you have a contract like this that ceases to exist after the first Distribution call? But tests are green so I guess it’s… OKAY?
Let’s see what else can leave the tests green, but optimize for more gas:
Making contributors constant instead of immutable (saves 24 gas - cause Hardhat addresses are always the same, so why not?
Make amount constant 0.25 ETH instead of calculating from balance (saves 106 gas - cause again, in tests the amount is always the same, so why not?)
Use Assembly to do call instead of usual solidity address.send (saves 90 gas)

Putting addresses as bytes32 number directly in calls can save us 9 more gas units!

So as far as we’re gaming tests, let’s game them even further.
Why don’t we just return if the test is “Gas Target”?
Knowing that Hardhat always runs tests in the same blocks, we can just put this in the beginning of our distribute() function:
if (block.number == 5) return;
And it will always return on the “Gas Target” test without consuming much gas, but will still do the rest of the function on “Business Logic” test :)

This gives us an amazing 21207 gas! Which is unbelievable until you understand what’s going on here…
Yeah, and this space is often about cheating - MEV, exploits, code-is-law, and all other stuff: if you can do it - you do it. Remember the recent gas-CTF contests with best optimizooor getting an NFT? There nobody is solving the original problem anymore - everything’s about “cheating” the smart-contract to accept the required values and pass the required tests with the lowest bytecode and the lowest gas - and you get the #1 NFT.
No :) We just need better testing and better conditions. Even this gas-puzzle could still verify the business logic in the Gas Target test, and use randomized values and addresses - so there’s no incentive to write code that just makes the tests green.
If the tests are good and fuzzy - there’s no possibility of writing hacky code to just meet the minimal requirements.
Same with the CTF contests. If they tested their problem solutions many times (like 1024 times in a single transaction - all with different numbers) - first, there will be no simple block-hacking (waiting for the right randomized number to submit your solution in a particular block to pass), and also the gas-estimation would be better when using different input values.
I hope the next CTF’s and Puzzles would consider this, so the game becomes more about efficient algorithms, and not just clever ways to hack the system.
If not - I would have to write one myself :)
P.S. The solutions provided here are not optimal - there are still a lot of ways to optimize even further: I’ve seen lower numbers, different tricks (including bytecode replacement), but here I’m describing my own experience with the puzzle. And trying to raise an issue about test-fitting and how to make things better.
Cheers, and let’s discuss the tricks you discovered in CT!
Crypto-twitter likes puzzles. And gas-golfing. Any CTF puzzle gains huge success, although in the end it all comes down to “test-fitting”.
Let me review a recent example from RareSkills that blew up on CT a couple of days ago:

The idea is to optimize gas in the contract and run provided tests for it that have a threshold. By default, the gas is way over the target:

So the “rules” are: no messing with optimizer or solidity version, no messing with test files, use solidity (so no huff/yul/bytecode contracts), and don’t make functions payable (which is a weird rule, but ok).
Make sure you try to solve this puzzle yourself before reading further - it’s really fun and makes you learn stuff! But if you already solved it and want to read other ways of doing it, or just struggling and want to know what’s the trick - go ahead!
Let’s do the obvious things first:
Make vars immutable instead of storage (saves 10710 gas - because immutable vars are embedded in the contract bytecode as constants instead of reading from storage)
Replace transfer with send (saves 108 gas - cause send has no check if the transfer succeeded)
Replace currentTime with endTime (saves 69 gas - and do the time calculations in constructor)
That gives us 61066 gas which is already 10887 better than original, but still 4k above the target:

There’s a particular trick this puzzle should teach you. And it’s an old way of sending ETH to addresses by SELFDESTRUCT:

This trick alone saves you 4066 gas and meets the target!
Every sane optimization always have its insane evil-brother… Let’s see how far we can push this!
Making the contract self-destructable already feels like cheating - why would you have a contract like this that ceases to exist after the first Distribution call? But tests are green so I guess it’s… OKAY?
Let’s see what else can leave the tests green, but optimize for more gas:
Making contributors constant instead of immutable (saves 24 gas - cause Hardhat addresses are always the same, so why not?
Make amount constant 0.25 ETH instead of calculating from balance (saves 106 gas - cause again, in tests the amount is always the same, so why not?)
Use Assembly to do call instead of usual solidity address.send (saves 90 gas)

Putting addresses as bytes32 number directly in calls can save us 9 more gas units!

So as far as we’re gaming tests, let’s game them even further.
Why don’t we just return if the test is “Gas Target”?
Knowing that Hardhat always runs tests in the same blocks, we can just put this in the beginning of our distribute() function:
if (block.number == 5) return;
And it will always return on the “Gas Target” test without consuming much gas, but will still do the rest of the function on “Business Logic” test :)

This gives us an amazing 21207 gas! Which is unbelievable until you understand what’s going on here…
Yeah, and this space is often about cheating - MEV, exploits, code-is-law, and all other stuff: if you can do it - you do it. Remember the recent gas-CTF contests with best optimizooor getting an NFT? There nobody is solving the original problem anymore - everything’s about “cheating” the smart-contract to accept the required values and pass the required tests with the lowest bytecode and the lowest gas - and you get the #1 NFT.
No :) We just need better testing and better conditions. Even this gas-puzzle could still verify the business logic in the Gas Target test, and use randomized values and addresses - so there’s no incentive to write code that just makes the tests green.
If the tests are good and fuzzy - there’s no possibility of writing hacky code to just meet the minimal requirements.
Same with the CTF contests. If they tested their problem solutions many times (like 1024 times in a single transaction - all with different numbers) - first, there will be no simple block-hacking (waiting for the right randomized number to submit your solution in a particular block to pass), and also the gas-estimation would be better when using different input values.
I hope the next CTF’s and Puzzles would consider this, so the game becomes more about efficient algorithms, and not just clever ways to hack the system.
If not - I would have to write one myself :)
P.S. The solutions provided here are not optimal - there are still a lot of ways to optimize even further: I’ve seen lower numbers, different tricks (including bytecode replacement), but here I’m describing my own experience with the puzzle. And trying to raise an issue about test-fitting and how to make things better.
Cheers, and let’s discuss the tricks you discovered in CT!
No activity yet