Skip to content

Weber Protocol -- Fork Logic

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

Table of contents

Introduction

This document specifies how the Weber protocol handles network upgrades (forks) and transitions between different protocol versions. The Weber fork mechanism is designed to allow coordinated network-wide upgrades while maintaining security during transition periods and ensuring backward compatibility where appropriate.

A fork in Weber represents a change to consensus rules that requires all participating nodes to upgrade. This document focuses on the mechanisms for executing such transitions, not the specific content of any particular upgrade.

Constants

Name Value Description
WEBER_FORK_VERSION Version('0x01000000') The current fork version for Weber
WEBER_FORK_EPOCH 2**14 (16,384) Epoch at which Weber fork takes effect
FORK_TRANSITION_WINDOW 2048 Number of epochs for fork transition period
MIN_FORK_SUPPORT_THRESHOLD 0.75 Minimum proportion of stake required to support a protocol upgrade
MAX_FORK_VERSION_DISAGREEMENT_EPOCHS 64 Maximum number of epochs to tolerate fork version disagreement

Fork Schedule

Weber follows a planned schedule for protocol upgrades to ensure network coordination. Each fork is identified by a unique version and scheduled at a specific epoch:

Fork Name Version Epoch Description
Phase 0 0x00000000 0 Initial beacon chain
Altair 0x01000000 74240 Sync committees, penalties
Weber 0x02000000 231680 Weber protocol enhancements

Fork Data Structures

Fork Descriptor

1
2
3
4
5
6
7
class Fork(Container):
    # Previous fork version
    previous_version: Version
    # Current fork version
    current_version: Version
    # Epoch of the fork
    epoch: Epoch

Fork Version

The fork version is a 4-byte identifier that uniquely identifies a protocol version:

class Version(Bytes4):
    pass

Fork Choice Compatibility

During a fork transition, the Weber fork choice rule is enhanced to ensure compatibility between pre-fork and post-fork nodes:

def is_fork_version_compatible(v1: Version, v2: Version) -> bool:
    """
    Determine if two fork versions are compatible for the purposes of fork choice.
    """
    # Fork versions are compatible if they're the same or if they're adjacent
    # versions in the defined fork schedule
    if v1 == v2:
        return True

    # Check if they are adjacent in the fork schedule
    for i in range(len(FORK_SCHEDULE) - 1):
        if (v1 == FORK_SCHEDULE[i] and v2 == FORK_SCHEDULE[i+1]) or \
           (v2 == FORK_SCHEDULE[i] and v1 == FORK_SCHEDULE[i+1]):
            return True

    return False

Fork Transition Process

Pre-fork Preparations

Before a fork occurs, nodes undergo a preparation phase:

def prepare_for_fork(state: BeaconState, fork_epoch: Epoch) -> None:
    """
    Prepare the node for an upcoming fork.
    """
    # Update fork version anticipation
    state.fork = Fork(
        previous_version=state.fork.current_version,
        current_version=get_fork_version(fork_epoch),
        epoch=fork_epoch
    )

    # Pre-compute fork digest
    fork_digest = compute_fork_digest(
        state.fork.current_version,
        state.genesis_validators_root
    )

    # Set fork readiness flag
    state.fork_readiness = True

    # Start monitoring network readiness
    start_fork_readiness_monitoring(fork_epoch)

Fork Epoch Transition

At the fork epoch, the state transition logic applies the fork:

def apply_fork_transition(state: BeaconState) -> None:
    """
    Apply state transition logic at fork boundary.
    """
    if state.slot % SLOTS_PER_EPOCH == 0:
        epoch = compute_epoch_at_slot(state.slot)

        # Check if this is a fork epoch
        if epoch == WEBER_FORK_EPOCH:
            # Apply state upgrades
            state = upgrade_state_to_weber(state)

            # Update fork data
            state.fork = Fork(
                previous_version=Version('0x01000000'),  # Previous version
                current_version=Version('0x02000000'),   # Weber version
                epoch=WEBER_FORK_EPOCH
            )

            # Update fork-specific parameters
            update_fork_parameters(state)

Post-fork Stabilization

After a fork occurs, nodes enter a stabilization phase:

def monitor_fork_stability(state: BeaconState) -> None:
    """
    Monitor network stability after a fork.
    """
    current_epoch = compute_epoch_at_slot(state.slot)
    time_since_fork = current_epoch - WEBER_FORK_EPOCH

    if 0 < time_since_fork <= FORK_TRANSITION_WINDOW:
        # Monitor chain finalization
        if current_epoch - state.finalized_checkpoint.epoch > 2:
            log_fork_finalization_issue()

        # Monitor participation
        epoch_participation = compute_epoch_participation_rate(state)
        if epoch_participation < MIN_FORK_PARTICIPATION_RATE:
            log_low_participation_warning(epoch_participation)

        # Check version adoption
        if get_fork_version_support(state) < MIN_FORK_SUPPORT_THRESHOLD:
            log_fork_support_warning()

Upgrade Mechanisms

State Upgrade

When transitioning to a new fork, the beacon state must be upgraded:

def upgrade_state_to_weber(pre: BeaconState) -> BeaconState:
    """
    Upgrade a pre-Weber beacon state to a Weber beacon state.
    """
    # Create new state with Weber structure
    post = WeberBeaconState()

    # Copy base fields
    post.genesis_time = pre.genesis_time
    post.genesis_validators_root = pre.genesis_validators_root
    post.slot = pre.slot

    # Copy validator registry
    post.validators = [copy_validator(v) for v in pre.validators]
    post.balances = pre.balances.copy()

    # Copy checkpoint and sync committee data
    post.justification_bits = pre.justification_bits.copy()
    post.previous_justified_checkpoint = pre.previous_justified_checkpoint
    post.current_justified_checkpoint = pre.current_justified_checkpoint
    post.finalized_checkpoint = pre.finalized_checkpoint
    post.current_sync_committee = pre.current_sync_committee
    post.next_sync_committee = pre.next_sync_committee

    # Initialize Weber-specific fields
    post.reputation_scores = [INITIAL_REPUTATION_SCORE for _ in range(len(post.validators))]
    post.quick_finality_participants = Bitvector[QUICK_FINALITY_COMMITTEE_SIZE]()
    post.latest_finalized_block_hash = compute_hash_root(post.finalized_checkpoint.root)

    return post

Configuration Upgrade

Network configuration parameters may change during a fork:

def update_fork_parameters(state: BeaconState) -> None:
    """
    Update network parameters based on the current fork version.
    """
    fork_version = state.fork.current_version

    if fork_version == Version('0x02000000'):  # Weber
        # Update Weber-specific parameters
        state.config.MAX_COMMITTEES_PER_SLOT = WEBER_MAX_COMMITTEES_PER_SLOT
        state.config.TARGET_COMMITTEE_SIZE = WEBER_TARGET_COMMITTEE_SIZE
        state.config.SLOTS_PER_EPOCH = WEBER_SLOTS_PER_EPOCH
        # Additional Weber parameter updates
        state.config.QUICK_FINALITY_THRESHOLD = WEBER_QUICK_FINALITY_THRESHOLD
        state.config.REPUTATION_WEIGHT_FACTOR = WEBER_REPUTATION_WEIGHT_FACTOR

Network Protocol Upgrade

The network protocol version also updates during a fork:

def update_network_protocol(fork_version: Version) -> None:
    """
    Update networking protocols based on fork version.
    """
    if fork_version == Version('0x02000000'):  # Weber
        # Update network protocol version
        set_network_protocol_version(WEBER_NETWORK_PROTOCOL_VERSION)

        # Enable Weber protocol message types
        enable_message_type(QUICK_FINALITY_VOTE_MESSAGE)
        enable_message_type(REPUTATION_UPDATE_MESSAGE)

        # Update GossipSub scoring parameters
        update_gossipsub_scoring_parameters(WEBER_GOSSIPSUB_PARAMETERS)

        # Activate Weber-specific subnets
        subscribe_to_subnet(QUICK_FINALITY_SUBNET)

Backward Compatibility

Pre-fork Message Handling

Nodes must handle messages from both pre-fork and post-fork nodes during the transition:

def process_message(message: Message, peer_fork_version: Version) -> None:
    """
    Process a network message considering fork compatibility.
    """
    current_fork_version = get_current_fork_version(state)

    # Determine if message sender is on a compatible fork
    if not is_fork_version_compatible(peer_fork_version, current_fork_version):
        log_incompatible_fork_version(peer_fork_version)
        return

    # Special handling for messages near fork boundary
    epoch = compute_epoch_at_slot(message.slot)
    if abs(epoch - WEBER_FORK_EPOCH) <= 2:
        # Apply heightened validity checks for messages near fork boundary
        if not validate_fork_boundary_message(message, peer_fork_version):
            return

    # Process message according to its fork version
    if peer_fork_version == current_fork_version:
        process_current_fork_message(message)
    else:
        process_other_fork_message(message, peer_fork_version)

Mixed-version Network Operation

During the transition period, the network will contain nodes running different versions:

def handle_mixed_version_network(peer_id: PeerId, peer_fork_version: Version) -> None:
    """
    Update peer tracking for mixed-version network operation.
    """
    current_epoch = compute_epoch_at_slot(state.slot)
    current_fork_version = get_current_fork_version(state)

    # Track peers by fork version
    update_peer_fork_version(peer_id, peer_fork_version)

    # Calculate network fork version distribution
    fork_version_counts = get_fork_version_distribution()

    # Log warning if fork distribution is concerning
    if current_epoch >= WEBER_FORK_EPOCH - FORK_WARNING_EPOCHS:
        weber_percentage = fork_version_counts.get(Version('0x02000000'), 0) / sum(fork_version_counts.values())
        if weber_percentage < MIN_FORK_SUPPORT_THRESHOLD:
            log_low_fork_adoption_warning(weber_percentage)

Implementation Guidelines

Testing Recommendations

Implementations should thoroughly test fork transitions:

  1. Genesis Initialization: Test correct initialization of Weber-specific state fields
  2. Boundary State Transition: Test the exact epoch of the fork transition
  3. Mixed Networks: Test networks with mixed pre-fork and post-fork nodes
  4. Fallback Mechanism: Test fallback to previous fork version if necessary
  5. Long-running Tests: Run simulations across the fork boundary for multiple epochs

Client Preparation Timeline

Clients should follow this recommended preparation timeline:

  1. T-3 months: Initial implementation of Weber changes
  2. T-2 months: Testnet deployment with fork transition
  3. T-1 month: Final specification freeze and security audits
  4. T-2 weeks: Release candidate for mainnet deployment
  5. T-1 week: Final mainnet-ready release

Performance Considerations

Implementers should consider these performance aspects during fork transitions:

  1. State Migration Efficiency: Optimize state upgrade functions
  2. Memory Usage: Monitor memory usage during state transitions
  3. Database Schema Changes: Prepare database migrations in advance
  4. Network Traffic Spikes: Expect and handle increased network traffic

Security Considerations

Transition Vulnerabilities

Fork transitions introduce specific security considerations:

  1. Validation Gap: Ensure no gap in validation rules between versions
  2. Replay Protection: Ensure messages cannot be replayed across forks
  3. Downgrade Attacks: Prevent forced downgrades to previous versions
  4. Signature Verification: Maintain signature verification across forks

Mitigating Split-network Risks

To mitigate the risk of network splits during a fork:

  1. Fork Readiness Metrics: Monitor and publish network readiness metrics
  2. Forced Checkpoint Sync: Provide checkpoint sync options if split occurs
  3. Emergency Fallback: Define emergency procedures for serious issues
  4. Extended Monitoring: Conduct enhanced network monitoring during transition