Skip to content

Weber P2P Networking Interface

Notice: This document is a work-in-progress for researchers and implementers of the Weber protocol.

Table of contents

Introduction

This document specifies the peer-to-peer (P2P) networking layer for the Weber protocol. The P2P network enables nodes to discover each other, exchange blockchain data, propagate blocks and attestations, and maintain consensus across the network.

Weber builds upon libp2p as its foundational networking stack, with several protocol-specific enhancements designed to optimize for Weber's unique consensus mechanisms:

  1. Reputation-aware message propagation to prioritize data from reliable validators
  2. Quick finality subnets for accelerated block finalization
  3. Optimized attestation aggregation techniques
  4. Performance-based peer selection for improved network resilience

This specification defines the protocols, message formats, and behaviors that all Weber client implementations must follow to ensure interoperability.

Constants

Network

Name Value Description
TARGET_PEERS_COUNT 60 Target number of peers to maintain
MAX_PEERS_COUNT 100 Maximum allowed number of peers
MINIMUM_VIABLE_PEERS 20 Minimum peers required for adequate network participation
GOSSIP_MAX_SIZE 1 MiB Maximum allowed size of uncompressed gossip messages
MAX_REQUEST_BLOCKS 1024 Maximum number of blocks in a single request
SUBNET_COUNT 64 Number of attestation subnets
SYNC_COMMITTEE_SUBNET_COUNT 4 Number of sync committee subnets
WEBER_FINALITY_SUBNET_COUNT 2 Number of Weber quick finality subnets
ATTESTATION_SUBNET_EXTRA_BITS 0 Amount of extra bits of randomness to add when determining a persistent attestation subnet
GOSSIP_MAX_RETRIES 3 Maximum number of gossip message retransmissions

Gossip

Name Value Description
ATTESTATION_SUBNET_FRACTION 4 1/N of all validators are in each subnet
ATTESTATION_SUBNET_COUNT 64 Number of attestation subnets
FINALITY_SUBNET_FRACTION 8 1/N of high-reputation validators are in the finality subnet
GOSSIP_SCORING_THRESHOLD 50 Score below which a peer is considered malicious
GOSSIP_SCORING_MAX 500 Maximum score a peer can achieve
ATTESTATION_PROPAGATION_SLOT_RANGE 32 Maximum number of slots during which an attestation can be propagated

Resource Limits

Name Value Description
REQUEST_RESPONSE_TIMEOUT 10s Timeout for response from a request
RESP_TIMEOUT 10s Timeout for full response
MAX_CHUNK_SIZE 1 MiB Maximum allowed size of uncompressed req/resp chunks
TTFB_TIMEOUT 5s Maximum time to wait for first byte of request response
WEBER_PRIORITY_SLOTS 4 Number of slots for which newly produced blocks receive network priority
MAX_EPOCHS_BEHIND 2 Maximum epochs a peer can be behind before being disconnected

Transport

Weber's P2P network uses libp2p as its transport layer with several configurations and extensions specific to the protocol's needs.

ENR Structure

Weber nodes use Ethereum Node Records (ENR) for discovery with the following fields:

Name ENR Value SSZ Equivalent
eth2 ssz_snappy(ENRForkID) N/A
attnets bitvector[64] Bitvector[ATTESTATION_SUBNET_COUNT]
syncnets bitvector[4] Bitvector[SYNC_COMMITTEE_SUBNET_COUNT]
webernets bitvector[2] Bitvector[WEBER_FINALITY_SUBNET_COUNT]
1
2
3
4
class ENRForkID(Container):
    fork_digest: ForkDigest
    next_fork_version: Version
    next_fork_epoch: Epoch

Node Discovery

Weber uses discv5 for node discovery, enhanced with Weber-specific capabilities:

  1. Reputation Tracking: Discovery process considers node reputation from previous interactions
  2. Geographic Diversity: Prioritizes connections to nodes in different geographic regions
  3. Quick Finality Support: Tracks which nodes participate in quick finality subnets

The discovery implementation must support ENR updates to track changing subnet subscriptions.

Connection Handling

Weber nodes should maintain connections with the following prioritization:

  1. Peers participating in sync committee subnets
  2. Peers participating in attestation subnets assigned to the node
  3. Peers participating in Weber quick finality subnets
  4. Peers with high reputation scores
  5. Peers with diverse network locations

The Gossip Domain: GossipSub

Weber uses libp2p's GossipSub protocol for efficiently broadcasting messages across the network, with performance-critical enhancements:

  1. Message Scoring: Messages are scored based on validator reputation and timeliness
  2. Enhanced Mesh Formation: Mesh connections are prioritized based on peer reliability
  3. Opportunistic Grafting: Dynamic mesh adaptation based on network conditions

Topics and Messages

Topics in Weber are organized by fork digest to support versioning and by functionality for specific message types. The format follows: /weber/{fork_digest}/{topic}/{encoding}

Global Topics

Name Message Type Message Contents
beacon_block SignedBeaconBlock A signed beacon block
beacon_aggregate_and_proof SignedAggregateAndProof A signed attestation aggregate with selection proof
voluntary_exit SignedVoluntaryExit A voluntary validator exit
proposer_slashing ProposerSlashing A proposer slashing
attester_slashing AttesterSlashing An attester slashing
sync_committee_contribution_and_proof SignedContributionAndProof A sync committee contribution and BLS proof of possession
validator_rotation SignedValidatorRotation A validator rotation request (Weber specific)
quick_finality_vote SignedQuickFinalityVote A quick finality vote from high-reputation validators (Weber specific)

Shard Topics

Name Message Type Message Contents
beacon_attestation_{subnet_id} Attestation An attestation with subnet selection
sync_committee_{subnet_id} SyncCommitteeMessage A sync committee message for light clients

Weber Enhanced Topics

Name Message Type Message Contents
weber_quick_finality_{subnet_id} QuickFinalityConsensusMessage Quick finality message for accelerated consensus
validator_reputation_update SignedReputationUpdate A signed validator reputation update
high_priority_attestation PrioritizedAttestation Critical attestation from high-reputation validators

Attestation Subnets

Weber maintains the subnet-based attestation design to distribute network load, with 64 attestation subnets. Validators are assigned to specific subnets according to the following rules:

1
2
3
4
5
6
7
8
def compute_subnet_for_attestation(attestation_slot: Slot, committee_index: CommitteeIndex) -> uint64:
    """
    Compute the correct subnet for an attestation for Phase 0.
    """
    slots_since_epoch_start = attestation_slot % SLOTS_PER_EPOCH
    committees_per_slot = get_committee_count_per_slot(state, compute_epoch_at_slot(attestation_slot))

    return (committees_per_slot * slots_since_epoch_start + committee_index) % ATTESTATION_SUBNET_COUNT

Sync Committee Subnets

Weber uses sync committee subnets to distribute sync committee messages which are used by light clients:

1
2
3
4
5
def compute_sync_committee_subnet(sync_committee_index: uint64) -> uint64:
    """
    Compute the correct subnet for a sync committee for the current slot.
    """
    return sync_committee_index % SYNC_COMMITTEE_SUBNET_COUNT

Weber Quick Finality Subnet

Weber introduces specialized subnets for quick finality messages:

1
2
3
4
5
6
7
8
9
def compute_finality_subnet(validator_index: ValidatorIndex, reputation_score: ReputationScore) -> Optional[uint64]:
    """
    Compute the quick finality subnet for a validator if eligible.
    Returns None if the validator is not eligible for quick finality.
    """
    if reputation_score < MIN_REPUTATION_FOR_QUICK_FINALITY:
        return None

    return validator_index % WEBER_FINALITY_SUBNET_COUNT

Message Validation

Before propagating messages, Weber nodes must validate them according to specific rules for each message type:

def validate_attestation(attestation: Attestation, current_slot: Slot) -> bool:
    """
    Validate an attestation before propagating it.
    """
    # The attestation is not from a future slot
    if attestation.data.slot > current_slot:
        return False

    # The attestation is within the last ATTESTATION_PROPAGATION_SLOT_RANGE slots
    if current_slot > attestation.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE:
        return False

    # The attestation is from the correct subnet
    computed_subnet = compute_subnet_for_attestation(
        attestation.data.slot,
        attestation.data.index
    )
    if computed_subnet != subnet_id:
        return False

    # Additional Weber-specific validations for reputation-weighted messages
    if is_high_reputation_attestation(attestation):
        return validate_high_reputation_attestation(attestation)

    return True

For Weber's quick finality messages, additional validation is required:

def validate_quick_finality_vote(vote: SignedQuickFinalityVote, state: BeaconState) -> bool:
    """
    Validate a quick finality vote before propagating it.
    """
    # Check that the validator is in the active validator set
    if vote.validator_index >= len(state.validators):
        return False

    # Verify the validator has sufficient reputation
    if get_validator_reputation(state, vote.validator_index) < MIN_REPUTATION_FOR_QUICK_FINALITY:
        return False

    # Verify signature
    domain = get_domain(state, DOMAIN_QUICK_FINALITY, compute_epoch_at_slot(state.slot))
    signing_root = compute_signing_root(vote.message, domain)
    if not bls.Verify(state.validators[vote.validator_index].pubkey, signing_root, vote.signature):
        return False

    return True

Attestation Aggregation

Weber optimizes the attestation aggregation process with a reputation-aware strategy:

def get_aggregator_index(state: BeaconState, slot: Slot, committee_index: CommitteeIndex) -> Optional[ValidatorIndex]:
    """
    Return the validator index of the next aggregator for a given slot and committee index.
    Weber enhances this with reputation weighting.
    """
    committee = get_beacon_committee(state, slot, committee_index)

    # Apply reputation weighting
    weighted_committee = []
    for validator_index in committee:
        rep_score = get_validator_reputation(state, validator_index)
        # Higher reputation gets more weight in selection
        weight = 1 + (rep_score / MAX_REPUTATION_SCORE)
        weighted_committee.extend([validator_index] * int(weight))

    # Randomized selection with reputation weighting    
    seed = get_seed(state, compute_epoch_at_slot(slot), DOMAIN_AGGREGATOR_SELECTION)
    signature = get_epoch_signature(state, compute_epoch_at_slot(slot), seed)
    random_byte = hash(signature)[0]

    index = random_byte % len(weighted_committee)
    return weighted_committee[index]

The Req/Resp Domain

Weber's request/response protocols handle direct communication between peers.

Protocol Identification

Weber req/resp protocols follow the format: /weber/{protocol}/{version}/{encoding}

For example: /weber/beacon_blocks_by_range/1/ssz_snappy

Encoding and Compression

Weber supports SSZ with Snappy compression as the primary encoding format:

Name Content Identifier Description
ssz_snappy application/octet-stream SSZ encoded and snappy compressed

Request/Response Messages

The following methods are defined in the req/resp domain:

Status

Protocol ID: /weber/status/1/ssz_snappy

Used to determine each peer's chain state during connection.

1
2
3
4
5
6
7
8
9
class Status(Container):
    fork_digest: ForkDigest
    finalized_root: Root
    finalized_epoch: Epoch
    head_root: Root
    head_slot: Slot
    # Weber additions
    quick_finality_epoch: Epoch
    network_health_score: uint16

Goodbye

Protocol ID: /weber/goodbye/1/ssz_snappy

Used when disconnecting from a peer.

Code Name Description
0 CLIENT_SHUTDOWN Regular shutdown
1 IRRELEVANT_NETWORK Wrong network
2 FAULT_ERROR Generic fault
3 UNABLE_TO_VERIFY_NETWORK Cannot verify network
4 TOO_MANY_PEERS Exceeded peer limit
5 REPUTATION_BELOW_THRESHOLD Peer reputation too low
6 FORK_INCOMPATIBLE Fork version mismatch

BeaconBlocks

Protocol ID: /weber/beacon_blocks_by_range/1/ssz_snappy

Request a range of beacon blocks.

1
2
3
4
class BeaconBlocksByRangeRequest(Container):
    start_slot: Slot
    count: uint64
    step: uint64  # Weber adds support for different step patterns

Protocol ID: /weber/beacon_blocks_by_root/1/ssz_snappy

Request specific beacon blocks by their root.

1
2
3
class BeaconBlocksByRootRequest(Container):
    # List of beacon block roots
    block_roots: List[Root, MAX_REQUEST_BLOCKS]

Weber Enhanced Requests

Protocol ID: /weber/reputation_by_index/1/ssz_snappy

Request reputation scores for specific validators.

class ReputationByIndexRequest(Container):
    validator_indices: List[ValidatorIndex, MAX_REQUEST_VALIDATORS]
1
2
3
class ReputationResponse(Container):
    reputation_scores: List[ReputationScore, MAX_REQUEST_VALIDATORS]
    epoch: Epoch

Protocol ID: /weber/quick_finality_checkpoints/1/ssz_snappy

Request recent quick finality checkpoints.

1
2
3
class QuickFinalityCheckpointsRequest(Container):
    start_epoch: Epoch
    count: uint64
class QuickFinalityCheckpointsResponse(Container):
    checkpoints: List[SignedQuickFinalityCheckpoint, MAX_CHECKPOINT_RESPONSE]

Weber-specific Network Optimizations

Prioritized Message Propagation

Weber implements a sophisticated prioritization system for message propagation:

def get_message_priority(message_type: str, message: Any, current_slot: Slot) -> int:
    """
    Determine priority level for a message (higher values = higher priority).
    """
    if message_type == "beacon_block":
        block = message
        # Recent blocks get highest priority
        if current_slot - block.slot <= WEBER_PRIORITY_SLOTS:
            return 100
        else:
            return 50

    elif message_type == "quick_finality_vote":
        vote = message
        # Quick finality votes get high priority
        return 90

    elif message_type == "beacon_attestation":
        attestation = message
        # Attestations are prioritized by validator reputation
        validator_indices = get_attesting_indices(attestation)
        avg_reputation = calculate_average_reputation(validator_indices)
        # Scale priority by reputation (50-80)
        return 50 + int((avg_reputation / MAX_REPUTATION_SCORE) * 30)

    # Default priority
    return 10

Dynamic Subnet Participation

Weber implements dynamic subnet participation based on network conditions:

def calculate_optimal_subnet_subscriptions(
    current_epoch: Epoch,
    validator_duties: List[ValidatorDuty],
    current_peers: List[PeerInfo]
) -> List[int]:
    """
    Dynamically determine optimal subnet subscriptions based on:
    1. Required subnets for validator duties
    2. Network coverage (which subnets are underserved)
    3. Node resources available
    """
    # Required subnets for validator duties
    required_subnets = get_required_subnets(validator_duties)

    # Analyze current network subnet coverage
    subnet_coverage = analyze_subnet_coverage(current_peers)

    # Identify underserved subnets that need additional nodes
    underserved_subnets = [
        subnet for subnet, coverage in subnet_coverage.items()
        if coverage < MIN_SUBNET_COVERAGE
    ]

    # Combine required and underserved subnets, up to resource capacity
    combined_subnets = list(set(required_subnets + underserved_subnets))
    if len(combined_subnets) > MAX_SUBNET_SUBSCRIPTIONS:
        # Prioritize required subnets, then underserved ones by coverage
        combined_subnets = required_subnets + sorted(
            [s for s in underserved_subnets if s not in required_subnets],
            key=lambda s: subnet_coverage[s]
        )
        combined_subnets = combined_subnets[:MAX_SUBNET_SUBSCRIPTIONS]

    return combined_subnets

Intelligent Peer Selection

Weber optimizes peer connections based on multiple factors:

def score_peer_for_connection(
    peer: PeerInfo,
    node_subnet_needs: List[int],
    current_peers: List[PeerInfo]
) -> float:
    """
    Score a potential peer for connection based on multiple factors.
    """
    score = 0.0

    # Reputation score component
    score += peer.reputation_score / MAX_REPUTATION_SCORE * 40

    # Subnet coverage component
    subnet_coverage_score = 0
    for subnet in node_subnet_needs:
        if subnet in peer.subnets:
            subnet_coverage_score += 1
    score += (subnet_coverage_score / max(1, len(node_subnet_needs))) * 30

    # Geographic diversity component
    region_counts = count_regions(current_peers)
    if peer.region in region_counts:
        # Penalize connecting to overrepresented regions
        region_penalty = min(20, region_counts[peer.region])
        score -= region_penalty
    else:
        # Bonus for new regions
        score += 10

    # Sync status component - prefer peers close to our chain head
    if peer.head_slot >= our_head_slot:
        score += 10
    else:
        slots_behind = our_head_slot - peer.head_slot
        # Penalize peers that are far behind
        score -= min(30, slots_behind // 64)

    return max(0, score)  # Score can't be negative

Validator Performance Tracking

Weber tracks validator network performance for reputation scoring:

def update_validator_network_performance(
    validator_index: ValidatorIndex,
    message_type: str,
    performance_data: dict
) -> None:
    """
    Update network performance metrics for a validator.
    This feeds into their reputation score.
    """
    # Extract performance metrics based on message type
    if message_type == "attestation":
        # Record attestation propagation time
        propagation_time = performance_data.get("propagation_time_ms")
        if propagation_time is not None:
            update_attestation_propagation_time(validator_index, propagation_time)

        # Record attestation subnet coverage
        subnet_id = performance_data.get("subnet_id")
        if subnet_id is not None:
            update_subnet_participation(validator_index, subnet_id)

    elif message_type == "block":
        # Record block propagation performance
        block_slot = performance_data.get("slot")
        propagation_time = performance_data.get("propagation_time_ms")
        if block_slot is not None and propagation_time is not None:
            update_block_propagation_time(validator_index, block_slot, propagation_time)

    # Periodically calculate network performance score component
    if should_update_network_reputation(validator_index):
        calculate_network_reputation_component(validator_index)

Network Security

Spam Protection

Weber implements several spam protection mechanisms:

  1. Rate Limiting: Per-peer message rate limits with validator reputation adjustments
  2. Message Validation: Strict validation before propagation
  3. Reputation Scoring: Peers serving invalid or spammy content receive reduced scores
  4. IP Diversity: Connection limits per IP range to prevent Sybil attacks

Eclipse Attack Prevention

Weber mitigates eclipse attacks through:

  1. Peer Diversity: Maintaining connections to peers across different subnets and regions
  2. Bootstrap Nodes: Regular reconnection to trusted bootstrap nodes
  3. Network Monitoring: Detecting unusual connection patterns or sudden peer disconnections
  4. Regular Peer Rotation: Periodically replacing peers to prevent gradual isolation

P2P Credential Management

class WeberP2PCredentials:
    """
    Management of node identity and credentials for P2P networking.
    """

    def __init__(self, data_dir: str, network_id: str):
        """Initialize credentials, loading or generating as needed."""
        self.data_dir = data_dir
        self.network_id = network_id
        self.identity_file = f"{data_dir}/node_id_{network_id}.json"
        self.peer_db_file = f"{data_dir}/peers_{network_id}.json"

        self.load_or_create_identity()
        self.load_peer_db()

    def load_or_create_identity(self) -> None:
        """Load existing credentials or create new ones if not available."""
        try:
            with open(self.identity_file, "r") as f:
                credentials = json.load(f)

            self.private_key = bytes.fromhex(credentials["private_key"])
            self.public_key = bytes.fromhex(credentials["public_key"])
            self.peer_id = credentials["peer_id"]

        except (FileNotFoundError, json.JSONDecodeError):
            # Generate new identity
            self.private_key = crypto.generate_private_key()
            self.public_key = crypto.derive_public_key(self.private_key)
            self.peer_id = crypto.peer_id_from_public_key(self.public_key)

            # Save to file
            self.save_identity()

    def save_identity(self) -> None:
        """Save credentials to disk."""
        credentials = {
            "private_key": self.private_key.hex(),
            "public_key": self.public_key.hex(),
            "peer_id": self.peer_id
        }

        with open(self.identity_file, "w") as f:
            json.dump(credentials, f)

Implementation Guidelines

Common Patterns

Implementing Weber's P2P interface should follow these patterns:

  1. Progressive Enhancement: Start with basic libp2p functionality, then add Weber enhancements
  2. Modular Design: Separate network logic from consensus logic
  3. Metrics Collection: Instrument all network operations for monitoring
  4. Circuit Breakers: Implement fallback mechanisms when network conditions degrade
  5. Graceful Degradation: Maintain basic functionality even when advanced features fail

Testing Recommendations

Robust testing of the P2P implementation should include:

  1. Network Simulation: Testing with simulated latency, packet loss, and bandwidth constraints
  2. Adversarial Testing: Injecting malicious or malformed messages
  3. Scalability Testing: Verifying behavior with large peer counts
  4. NAT Traversal: Testing connectivity through various NAT configurations
  5. Fork Transition: Validating behavior during protocol upgrades

Performance Metrics

Key metrics to track in a Weber P2P implementation:

  1. Message Propagation Time: How quickly blocks and attestations spread through the network
  2. Peer Connectivity: Distribution and stability of peer connections
  3. Bandwidth Utilization: Network usage per peer and message type
  4. Subnet Coverage: Effectiveness of subnet subscription strategy
  5. Quick Finality Performance: Propagation time for quick finality votes
  6. CPU/Memory Overhead: Resources consumed by the networking stack
  7. Discovery Success Rate: Effectiveness of peer discovery

These metrics should be exposed via standard monitoring interfaces to enable network-wide health monitoring.