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 (0x35for MainNet).StandbyCommittee: AnIReadOnlyList<ECPoint>containing public keys of all committee members (validators plus candidates).ValidatorsCount(int): The number of consensus validators selected from theStandbyCommittee.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 forValidUntilBlock.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:
- Deserialization: Parse the JSON configuration file.
- Fallback: Missing fields default to values from the static
ProtocolSettings.Defaultinstance. - EnsureOmmitedHardforks: Automatically assigns height
0to any omitted hardfork entries, ensuringIsHardforkEnabledreturnstruefrom genesis for unspecified upgrades. - CheckingHardfork: Validates hardfork continuity and monotonicity, throwing
ArgumentExceptionfor 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:
MillisecondsPerBlockis no longer read fromProtocolSettings. Instead, consensus code usesNeoSystemExtensions.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:
ProtocolSettingsbundles network identifiers (Network,AddressVersion), consensus parameters (ValidatorsCount,MillisecondsPerBlock), and theHardforksactivation map into an immutable configuration object loaded fromsrc/Neo/ProtocolSettings.cs. - Validation: The loading process enforces hardfork continuity (no gaps in the enum sequence) and monotonic activation heights via
CheckingHardfork, whileEnsureOmmitedHardforksdefaults missing entries to height0. - Consensus Impact: Hardforks like HF_Echidna transition critical parameters from static configuration to the native Policy contract, enabling on-chain governance. The
IsHardforkEnabledmethod 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →