Skip to content

Conversation

@yiweichi
Copy link
Member

@yiweichi yiweichi commented Jan 12, 2026

Problem

Malicious peers can DoS the node by sending old blocks.

Solution

Two-layer defense:

  1. Finalized Block Protection
    if block_number <= finalized_height { penalize(peer); // Immediately reject}
  2. Duplicate Block Detection
    // Cache recent blocks per peerif peer_already_sent_this_block { penalize(peer); // DoS detected }

Challenge

Multi-Protocol Handling
Problem: Nodes use both scroll-wire and eth-wire protocols. Same peer may legitimately send same block via both protocols.
Solution: Track duplicates per protocol

struct PeerBlockState {
    scroll_wire_received: LruCache<B256>,  // scroll-wire duplicates
    eth_wire_received: LruCache<B256>,     // eth-wire duplicates
}

// Only penalize duplicates within SAME protocol
if peer_sent_via_scroll_wire && receives_again_via_scroll_wire {
    penalize();  // DoS attack
}

if peer_sent_via_scroll_wire && receives_via_eth_wire {
    accept();    // Different protocol, OK
}

Fixes #307

@codspeed-hq
Copy link

codspeed-hq bot commented Jan 12, 2026

Merging this PR will not alter performance

✅ 2 untouched benchmarks


Comparing feat-penalize-peer-on-duplicate-block (664f555) with main (c3ec1a2)

Open in CodSpeed

@yiweichi yiweichi changed the title feat: penalize peer on duplicate block feat: handle DoS node by sending block Jan 19, 2026
@yiweichi yiweichi requested a review from frisitano January 19, 2026 08:46
Copy link
Collaborator

@frisitano frisitano left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Added a few comments inline. Can you add some test cases, please?

I'm also wondering if we can simplify the logic a bit. The idea would be that if we have a connection with a peer over scroll-wire, then we should only gossip on scroll-wire. If we don't have a scroll-wire connection, then we use eth-wire. I think this should allow us to consolidate the block cache per peer into a single cache.

} else {
None
}
})
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if we should change the logic below.

If we have a connection with a peer via scroll wire - only send via scroll wire. If we don't have a connection with a peer via scroll wire then send only via eth wire.

What do you think?

Copy link
Member Author

@yiweichi yiweichi Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's a good idea! The only thing is about implementation.

If we don't have a connection with a peer via scroll wire then send only via eth wire.

The implementation in my mind is that, we will need to save all none-scroll-wire peerIds here, so we can send block directly to the peer only via eth wire by inner_network_handle. But seems the trait of inner_network_handle does export method send_eth_message.

And also I'm not sure how to maintain the none-scroll-wire peerlist on RN side, how do we handle peer disconnect.

Do you have a viable implementation in your mind?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modify this trait to implement it https://github.com/scroll-tech/reth/blob/079764e909be74219f32796342c4dd911fdd30a9/crates/net/network-api/src/block.rs#L15-L26

/// Provides a listener for new blocks on the eth wire protocol.
pub trait EthWireProvider<N: NetworkPrimitives> {
    /// Create a new eth wire block listener.
    fn eth_wire_block_listener(
        &self,
    ) -> impl Future<
        Output = Result<EventStream<NewBlockWithPeer<N::Block>>, oneshot::error::RecvError>,
    > + Send;

    /// Announce a new block to the network over the eth wire protocol.
    fn eth_wire_announce_block(&self, block: N::NewBlockPayload, hash: B256);
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking you could use something like this

        let peers = self.inner_network_handle.get_all_peers().await?;
        for peer in peers {
            if peer.capabilities.capabilities().iter().any(|cap| matches!(cap, ScrollWire)) {
                todo!("send via scroll wire")
            } else {
                todo!("send via eth wire")
            };
        }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Network] Penalize peer if they send us duplicate block

3 participants