Skip to main content

7 posts tagged with "Security"

View All Tags

The Problem

Today, securing Ethereum costs 32 ETH—locking out most users. Proof-of-Work and Proof-of-Stake make influence expensive. This ensures Sybil resistance but hurts decentralization.

To build a truly open network, anyone should be able to help secure it cheaply, without compromising safety.

The Idea: Lightweight Validators + Data Nodes

Instead of making every participant store all data or stake huge sums, split responsibilities:

Data Nodes

  • Store and serve blockchain data.
  • Require high bandwidth/CPU.
  • Open, replicated network. Validators audit them, so strict Sybil resistance is less critical.

Validator Nodes (Phones, Laptops)

  • Run lightweight validation tasks.
  • Sign votes, challenge data nodes, contribute entropy.
  • Participation measured by uniqueness and uptime, not capital or compute.

Result: anyone with a phone or laptop can secure the network indirectly.

How It Could Work

  • Random committee selection: Validators are sampled each round. Fake identities are ineffective.
  • Proof of unicity: Validator identity tied to a unique human or device using VDFs, hardware attestation, social proofs, or zkPassport-style zero-knowledge proofs.
  • Data availability sampling: Validators check small, random slices of the blockchain. Full verification remains with full nodes.
  • Layered participation: Light nodes vote and sign for integrity; heavy tasks stay with full nodes.

Why It Matters

  • Security: Sybil resistance comes from uniqueness and randomness, not money or energy.
  • Low bandwidth: Light validators handle minimal data; replication handled by data nodes.
  • Inclusivity: Anyone can join consensus and contribute to network safety.
  • Churn tolerance: Randomized selection and fraud proofs allow nodes to join/leave safely.

Challenges

  • Proofs of uniqueness without breaking privacy. Projects like zkPassport on Rarimo show that zero-knowledge proofs can verify uniqueness without revealing personal identity, potentially solving this.
  • Malicious data nodes vs. lightweight bandwidth limits: Light validators only fetch small slices of blockchain data to save bandwidth. Malicious nodes could serve incorrect or incomplete data, and validators might miss it. Protocol design must ensure fraud detection is reliable despite limited visibility. This is similar to Polkadot’s fishermen, who audit network behavior without producing blocks and are rewarded for catching misbehavior.
  • Incentives: Light participants need rewards without large deposits.

Takeaway

Decentralization means rethinking Sybil resistance: separate data storage from validation power, leverage human uniqueness and randomness, and enable lightweight participation.

The vision: securing a blockchain should be as easy as running a simple app on your phone.

The Challenge

This week, I got my hands on a fascinating puzzle. At first glance, it seemed simple:

Deploy a contract to Sepolia and make its flag() function return my wallet address.

But as I dug deeper, I realized this wasn't your typical smart contract challenge. It would push me to combine my bytecode analysis skills with one of Ethereum's newest features - something I had been eager to try out in a real-world scenario.

The Challenge Walkthrough

Step 1: Understanding Deployment vs Runtime Bytecode

The challenge started with a blob of deployment bytecode. After deploying it to Sepolia, I decompiled the deployment stub which revealed:

function __function_selector__() public payable {
require(msg.value == 0);
return RUNTIME_BYTECODE;
}

This confirmed it was just a wrapper that returns the runtime bytecode to be installed on-chain.

Step 2: Decompiling the Runtime Bytecode

Using decompilation tools, I discovered two key functions in the runtime bytecode:

function flag() public view returns (address) {
return _flag;
}

function fallback() external payable {
// forward call with selector 0xe8c21a3
(bool ok, bytes memory data) = msg.sender.call(abi.encodeWithSelector(0xe8c21a3));
require(ok, "Call failed");
require(abi.decode(data, (bool)), "Wrong answer");

_flag = msg.sender;
}

The logic was elegantly simple:

  • flag() just returns _flag.
  • _flag is only updated if the fallback() succeeds.
  • The fallback() does a msg.sender.call(...) expecting a truthy return.

Step 3: The Key Insight

Upon closer inspection, I noticed something interesting: the contract doesn't call itself back. Instead, it calls msg.sender with a fixed selector 0xe8c21a3. This led to two important observations:

  • If msg.sender is an EOA (Externally Owned Account) → No code exists, so the call fails
  • If msg.sender is a contract with a fallback → It can handle the selector and return true

This meant the contract required its caller to be a contract that could respond appropriately to this challenge.

Step 4: The EOA Challenge

Here's where it got interesting: the challenge specifically required my EOA address to be stored in _flag.

Historically, this would have been impossible since EOAs cannot contain code. However, EIP-7702 introduced a game-changing capability: EOAs can now temporarily deploy code to themselves through signed transactions.

This was the trick:

  • Turn the EOA into a contract with a permissive fallback() that returns true.
  • Trigger the original contract’s fallback().
  • _flag = msg.sender → my EOA.

Putting It All Together

After understanding the challenge's mechanics, I knew I'd need my full toolkit for this one. First stop: Dedaub for bytecode analysis. I've always loved how it peels back the layers of compiled contracts, and this time it didn't disappoint.

With the bytecode decompiled, I set up my development environment. Foundry has been my go-to for contract deployment lately, but I had a feeling I'd need more than just that. The tricky part was going to be that EOA transaction - this is where ethers.js came into play.

Here's what my solution looked like:

const auth = ether_signer.authorizeSync({
address: IMPLEMENTATION_CONTRACT_ADDRESS,
chainId: CHAIN_ID,
nonce: nonce,
});

I'll admit, crafting that payload took some trial and error. EIP-7702 is cutting-edge stuff, and there aren't many examples out there yet. But that's what made it exciting - I was pushing the boundaries of what's possible with EOAs.

To make sure everything worked:

# The moment of truth
cast call $CONTRACT_ADDRESS "flag()(address)"

When I saw my address pop up, I couldn't help but smile. It worked perfectly - I had turned my regular wallet into a smart contract just long enough to pass the challenge.

Lessons Learned

  • Decompilation first: understanding runtime vs deployment bytecode is critical.
  • Contracts can ask EOAs to act like contracts; with EIP-7702, this is possible.
  • Tooling gap: Foundry wasn’t enough; raw transaction signing in ethers.js was needed.

This was a neat mix of bytecode archaeology and bleeding-edge Ethereum features.

Looking Back

This challenge reminded me why I love working with Ethereum. It's not just about writing smart contracts - it's about understanding the entire stack, from raw bytecode to the latest protocol features. I got to use everything in my toolbox: bytecode analysis, contract development, and some creative problem-solving with EIP-7702.

What really made this interesting was how it pushed me to think differently about EOAs and smart contracts. We often treat them as completely separate things, but with new features like EIP-7702, the lines are starting to blur in fascinating ways.

Most importantly, it showed me that staying curious about new protocol features pays off. EIP-7702 had caught my eye when it was first proposed, and being able to use it in a practical scenario was incredibly satisfying.

I'm looking forward to my next challenge - there's always something new to learn in this space!

Smart contract proxies enable upgradeability by separating contract logic from storage. This is powerful, but dangerously easy to get wrong. Misconfigurations or misunderstandings around storage layout, access control, or delegate calls often lead to catastrophic exploits.

Let's walk through the most common vulnerabilities in proxy contracts and practical strategies for hardening your upgradeable deployments.

You don’t need to memorize this list, only build a habit of asking the right questions when auditing any system. With proxies, for example, think about:

  • Upgrade control: who can change the logic and how?
  • Storage layout collisions
  • Delegatecall abuse

Also look at how upgrades are monitored, tested, and deployed. So rather than memorizing vulnerabilities, Rely on a structured investigation and pattern recognition.

Auditing smart contracts isn’t just about finding bugs; it’s about cultivating paranoia—the healthy kind. You learn to ask: What assumptions is this code making? What happens if they break? Who gains if something goes wrong?

Here’s a peek into how I approached a sample audit, step-by-step, and turned raw code into a structured security review.

Even the best hospitals can’t stop every illness. But they’re ready to respond quickly and treat what they can

Recently, during a meeting, I mentioned this fact, that made the room go silent!

They were curious about the recent Bybit $1 billion hack. I broke down the possible attack vectors and finished with a blunt truth:

"There are risks and attack surfaces we simply cannot do anything about."

Their reaction? Shock.

"I don’t expect to hear that from a security expert!"