# ProtocolSettings in Neo: Structure, Configuration, and Hardfork Consensus Behavior

> Explore ProtocolSettings in Neo and its immutable network parameters. Learn how hardforks deterministically gate consensus behavior changes at specific block heights.

- Repository: [The Neo Project/neo](https://github.com/neo-project/neo)
- Tags: deep-dive
- Published: 2026-03-08

---

**ProtocolSettings is the immutable central configuration object that defines all network-wide parameters for the NEO blockchain, using a hardfork activation map to deterministically gate consensus behavior changes at specific block heights.**

In the `neo-project/neo` repository, `ProtocolSettings` serves as the definitive source of truth for blockchain configuration, loaded from JSON or defaulting to MainNet values. Understanding ProtocolSettings in Neo is essential for configuring private networks, auditing protocol upgrades, and extending consensus logic, as it encapsulates the hardfork enumeration that dictates when critical validation rules transition from static configuration to dynamic on-chain governance.

## Core Structure of ProtocolSettings

The `ProtocolSettings` class, defined in [`src/Neo/ProtocolSettings.cs`](https://github.com/neo-project/neo/blob/main/src/Neo/ProtocolSettings.cs), acts as a record bundling network identifiers, consensus limits, and committee information. Once instantiated, the object is immutable and thread-safe for the lifetime of the node.

### Network Identity and Consensus Parameters

The primary fields define the cryptographic and timing constraints of the network:

- **`Network`** (`uint`): The magic number identifying the network (e.g., MainNet = `0`).
- **`AddressVersion`** (`byte`): The prefix byte for Neo addresses (`0x35` for MainNet).
- **`StandbyCommittee`**: An `IReadOnlyList<ECPoint>` containing public keys of all committee members (validators plus candidates).
- **`ValidatorsCount`** (`int`): The number of consensus validators selected from the `StandbyCommittee`.
- **`SeedList`** (`string[]`): Default seed nodes for peer-to-peer bootstrapping.
- **`MillisecondsPerBlock`** (`uint`): Base block time (default 15000ms), superseded by on-chain Policy after **HF_Echidna**.
- **`MaxValidUntilBlockIncrement`** (`uint`): Maximum offset a transaction may specify for `ValidUntilBlock`.
- **`MaxTransactionsPerBlock`** (`uint`): Transaction ceiling per block (512).
- **`MemoryPoolMaxTransactions`** (`int`): Memory pool capacity (50,000 transactions).
- **`MaxTraceableBlocks`** (`uint`): Blocks inspectable by contracts, also delegated to Policy after HF_Echidna.
- **`InitialGasDistribution`** (`ulong`): Genesis GAS minting amount (52,000,000 GAS).

The class also exposes computed properties like **`StandbyValidators`** (the first `ValidatorsCount` keys from the committee) and **`TimePerBlock`** (a `TimeSpan` conversion of the millisecond setting).

### The Hardfork Activation Map

The **`Hardforks`** property is an `ImmutableDictionary<Hardfork, uint>` mapping each hardfork enum to its activation block height. This map is the mechanism through which the network coordinates protocol upgrades without requiring simultaneous software deployment.

## Loading ProtocolSettings and Validation Rules

Configuration is loaded via `ProtocolSettings.Load(string path)`, which deserializes JSON using `Microsoft.Extensions.Configuration`.

### Configuration Loading Process

The loading pipeline in [`ProtocolSettings.cs`](https://github.com/neo-project/neo/blob/main/ProtocolSettings.cs) (lines 25–82) executes the following sequence:

1. **Deserialization**: Parse the JSON configuration file.
2. **Fallback**: Missing fields default to values from the static `ProtocolSettings.Default` instance.
3. **EnsureOmmitedHardforks**: Automatically assigns height `0` to any omitted hardfork entries, ensuring `IsHardforkEnabled` returns `true` from genesis for unspecified upgrades.
4. **CheckingHardfork**: Validates hardfork continuity and monotonicity, throwing `ArgumentException` for invalid configurations.

```csharp
// Load from custom configuration
var settings = ProtocolSettings.Load("protocol.json");
// Or use MainNet defaults
var defaults = ProtocolSettings.Default;

```

### Hardfork Map Validation (CheckingHardfork)

The `CheckingHardfork` method enforces two critical invariants required for deterministic consensus:

- **Continuity**: Configured hardforks must form a contiguous subsequence of the enum order. Gaps (e.g., configuring HF_Aspidochelone and HF_Echidna while omitting HF_Basilisk) trigger an exception.
- **Monotonic Heights**: Later hardforks cannot activate at lower block heights than earlier ones. Each subsequent enum value must have an activation height greater than or equal to its predecessor.

These rules prevent ambiguity in protocol state that could cause consensus forks.

## Hardfork Enumeration and Activation Logic

Hardforks are defined in [`src/Neo/Hardfork.cs`](https://github.com/neo-project/neo/blob/main/src/Neo/Hardfork.cs) as a canonical ordered enumeration:

```csharp
public enum Hardfork : byte
{
    HF_Aspidochelone,
    HF_Basilisk,
    HF_Cockatrice,
    HF_Domovoi,
    HF_Echidna,
    HF_Faun,
    HF_Gorgon
}

```

The enum order represents the mandatory sequence of protocol upgrades; the blockchain assumes upgrades occur in this specific progression.

### IsHardforkEnabled Implementation

The `IsHardforkEnabled` method provides the runtime guard used throughout the consensus engine and native contracts:

```csharp
public bool IsHardforkEnabled(Hardfork hardfork, uint index)
{
    if (Hardforks.TryGetValue(hardfork, out uint height))
        return index >= height;
    return false;
}

```

When checking a hardfork against a block index, the method returns `true` only after the chain has reached the configured activation height. Because `EnsureOmmitedHardforks` populates missing entries with height `0`, omitted hardforks are effectively active from genesis.

## Impact of Hardforks on Consensus Behavior

Hardfork activation is consulted in `Transaction.Verify`, `Block.Verify`, and native contract logic to determine which protocol version to enforce. This pattern allows the network to seamlessly transition between validation rules without restarting nodes.

### HF_Echidna and the Shift to Policy Contract Governance

**HF_Echidna** represents a significant architectural shift in how consensus parameters are managed. After this hardfork activates, several previously static settings become dynamic on-chain values governed by the native **Policy** contract:

- **Block Time**: `MillisecondsPerBlock` is no longer read from `ProtocolSettings`. Instead, consensus code uses `NeoSystemExtensions.GetTimePerBlock()`, which queries the Policy contract.
- **Max Traceable Blocks**: The limit on blocks inspectable by contracts is fetched from Policy rather than the static configuration.
- **Max ValidUntilBlock Increment**: Transaction validity windows are similarly delegated to on-chain policy.

This transition enables network parameter adjustments through governance votes rather than software upgrades.

### Validation Guards in Consensus Code

Consensus logic uses the hardfork guard to branch between protocol versions. For example, when validating block timestamps or transaction formats, the engine checks the activation status:

```csharp
public static void ValidateBlock(ProtocolSettings settings, Block block)
{
    // After HF_Echidna, dynamic policy overrides static settings
    TimeSpan expectedBlockTime = settings.IsHardforkEnabled(Hardfork.HF_Echidna, block.Index)
        ? GetPolicyBlockTime()      // Extension method reads native Policy contract
        : settings.TimePerBlock;    // Static fallback
        
    // Validation continues with appropriate rules...
}

```

Subsequent hardforks like **HF_Faun** and **HF_Gorgon** introduce additional changes to VM opcode pricing, transaction validation rules, and native contract behavior, all gated by the same `IsHardforkEnabled` pattern.

## Practical Implementation Examples

### Loading Custom Network Configurations

Private networks and testnets can customize protocol parameters via JSON configuration:

```csharp
var customSettings = ProtocolSettings.Load("custom.config.json");

Console.WriteLine($"Network magic: {customSettings.Network}");
Console.WriteLine($"Committee size: {customSettings.StandbyCommittee.Count}");

// Check fork status at specific height
bool isEchidnaActive = customSettings.IsHardforkEnabled(Hardfork.HF_Echidna, 1_200_000);

```

### Implementing Hardfork Guards

When implementing new consensus features, always guard them with the hardfork check to maintain backward compatibility during the transition period:

```csharp
if (settings.IsHardforkEnabled(Hardfork.HF_Gorgon, block.Index))
{
    ApplyNewValidationRule();
}
else
{
    ApplyLegacyValidationRule();
}

```

The unit tests in [`tests/Neo.UnitTests/UT_ProtocolSettings.cs`](https://github.com/neo-project/neo/blob/main/tests/Neo.UnitTests/UT_ProtocolSettings.cs) demonstrate these patterns, validating that hardfork maps load correctly and respect continuity constraints.

## Summary

- **Structure**: `ProtocolSettings` bundles network identifiers (`Network`, `AddressVersion`), consensus parameters (`ValidatorsCount`, `MillisecondsPerBlock`), and the `Hardforks` activation map into an immutable configuration object loaded from [`src/Neo/ProtocolSettings.cs`](https://github.com/neo-project/neo/blob/main/src/Neo/ProtocolSettings.cs).
- **Validation**: The loading process enforces hardfork continuity (no gaps in the enum sequence) and monotonic activation heights via `CheckingHardfork`, while `EnsureOmmitedHardforks` defaults missing entries to height `0`.
- **Consensus Impact**: Hardforks like **HF_Echidna** transition critical parameters from static configuration to the native Policy contract, enabling on-chain governance. The `IsHardforkEnabled` method gates all protocol changes, ensuring deterministic behavior across the network at the configured block heights.

## Frequently Asked Questions

### What happens if a hardfork entry is missing from the configuration?

The `EnsureOmmitedHardforks` method automatically assigns height `0` to any omitted hardfork entries during loading. This means the hardfork is considered active from the genesis block, ensuring that `IsHardforkEnabled` returns `true` for all queried heights and preventing null reference exceptions in consensus code.

### How does HF_Echidna change block time validation?

After **HF_Echidna** activates, the consensus engine stops reading `MillisecondsPerBlock` from `ProtocolSettings`. Instead, it calls extension methods like `NeoSystemExtensions.GetTimePerBlock()` to retrieve the value dynamically from the native Policy contract, allowing network participants to adjust block time through on-chain governance rather than node software updates.

### What validation rules ensure hardfork consistency?

The `CheckingHardfork` method validates two invariants: **continuity** (configured hardforks must be a contiguous subsequence of the enum order without gaps) and **monotonic heights** (each subsequent hardfork must activate at a block height greater than or equal to the previous one). Violations raise `ArgumentException` during configuration loading.

### Where are hardfork checks implemented in the consensus layer?

Hardfork checks appear in `Transaction.Verify`, `Block.Verify`, and native contract logic throughout the codebase. The consensus engine uses `ProtocolSettings.IsHardforkEnabled(Hardfork, uint)` to determine which validation rules, pricing models, or transaction formats to apply at a given block index, ensuring coordinated protocol upgrades across the network.