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

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, 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 (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.
// 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 as a canonical ordered enumeration:

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:

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:

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:

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:

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

The unit tests in 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.
  • 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →