PREVIEW — This package is in preview. APIs may change between releases.
Transaction ordering, block production, policy enforcement, and batch creation for Nethereum AppChain networks.
The sequencer is the centralised operator in an AppChain — your business, your rules. It accepts transactions, validates them against configurable policies, orders them into blocks, and produces blocks at configurable intervals or on demand. All produced state is publicly verifiable and synchronisable by any follower.
The sequencer integrates with pluggable consensus strategies (single-sequencer or Clique PoA), supports batch production for L1 anchoring, and provides a coordinator for managing sequencer lifecycle during initial sync and production phases.
- Transaction Validation Pipeline: Nonce checking, balance validation, intrinsic gas calculation, and sender recovery
- Configurable Block Production: Interval-based or on-demand block production modes
- Policy Enforcement: Pluggable access control with sender allowlists and calldata size limits
- Batch Production: Aggregates blocks into batches for L1 anchoring with optional compression
- Sequencer Coordinator: Manages sync-to-production lifecycle transitions
- Circuit Breaker: Stops block production after consecutive failures
dotnet add package Nethereum.AppChain.Sequencer- Nethereum.CoreChain -
BlockProducer,TransactionProcessor,ITxPool,IBlockProductionStrategy - Nethereum.AppChain -
IAppChainandAppChainConfig - Nethereum.AppChain.Sync -
IBatchStorefor batch metadata tracking - Nethereum.Signer - Transaction signature verification and sender recovery
- Nethereum.Model - Transaction, block, and receipt data structures
The sequencer supports two production modes:
- Interval-based (default): Produces blocks at a fixed interval (e.g., every 1000ms), collecting pending transactions from the pool
- On-demand: Produces a block immediately when a transaction is submitted, providing instant confirmation
var intervalConfig = SequencerConfig.Default; // 1000ms interval
var onDemandConfig = SequencerConfig.OnDemand; // Immediate productionEvery submitted transaction passes through validation before entering the pool:
- Policy enforcement - Check sender authorization and calldata limits
- Sender recovery - Recover sender address from ECDSA signature
- Nonce checking - Verify nonce matches expected next nonce
- Balance validation - Ensure sender has sufficient funds for value + gas
- Intrinsic gas calculation - Verify gas limit covers minimum execution cost
The PolicyEnforcer validates transactions against configurable rules:
var policy = PolicyConfig.RestrictedAccess(
allowedWriters: new[] { address1, address2 },
maxCalldataBytes: 128_000);Violation types: UnauthorizedSender, CalldataTooLarge, NonceTooLow, InsufficientBalance
Batches aggregate sequential blocks for efficient L1 anchoring:
var batchConfig = BatchProductionConfig.WithCadence(blocksPerBatch: 100);
// or time-based:
var batchConfig = BatchProductionConfig.WithTimeThreshold(seconds: 60);using Nethereum.AppChain.Sequencer;
var sequencerConfig = new SequencerConfig
{
SequencerAddress = signerAddress,
SequencerPrivateKey = privateKey,
BlockTimeMs = 1000,
MaxTransactionsPerBlock = 1000,
AllowEmptyBlocks = false
};
var sequencer = new Sequencer(appChain, sequencerConfig, txPool, blockProducer);
await sequencer.StartAsync();
// Submit a transaction
var txHash = await sequencer.SubmitTransactionAsync(signedTransaction);using Nethereum.AppChain.Sequencer;
var config = new SequencerConfig
{
SequencerAddress = signerAddress,
SequencerPrivateKey = privateKey,
BlockTimeMs = 500,
Policy = PolicyConfig.RestrictedAccess(
allowedWriters: new[] { userAddress1, userAddress2 },
maxCalldataBytes: 64_000)
};
var policyEnforcer = new PolicyEnforcer(config.Policy);
var sequencer = new Sequencer(appChain, config, txPool, blockProducer,
policyEnforcer: policyEnforcer);
await sequencer.StartAsync();var config = SequencerConfig.OnDemand;
config.SequencerAddress = signerAddress;
config.SequencerPrivateKey = privateKey;
var sequencer = new Sequencer(appChain, config, txPool, blockProducer);
await sequencer.StartAsync();
// Block produced immediately on transaction submission
await sequencer.SubmitTransactionAsync(signedTx);var config = new SequencerConfig
{
SequencerAddress = signerAddress,
SequencerPrivateKey = privateKey,
BlockTimeMs = 1000,
BatchProduction = BatchProductionConfig.WithCadence(blocksPerBatch: 100)
};
var batchProducer = new SequencerBatchProducer(
blockStore, txStore, receiptStore, batchStore, config.BatchProduction);
var sequencer = new Sequencer(appChain, config, txPool, blockProducer,
batchProducer: batchProducer);
await sequencer.StartAsync();var coordinator = new SequencerCoordinator(
sequencer, liveBlockSync, peerManager, coordinatorConfig);
// Starts in sync mode, transitions to production when caught up
await coordinator.StartAsync();
coordinator.ModeChanged += (sender, args) =>
{
Console.WriteLine($"Mode: {args.Mode}"); // Syncing → Producing
};Core sequencer orchestrating block production.
public class Sequencer : ISequencer, IAsyncDisposable
{
public Sequencer(IAppChain appChain, SequencerConfig config,
ITxPool txPool, IBlockProducer blockProducer,
IPolicyEnforcer? policyEnforcer = null,
IBatchStore? batchStore = null,
IBatchProducer? batchProducer = null,
IBlockProductionStrategy? blockProductionStrategy = null);
public Task StartAsync();
public Task StopAsync();
public Task<string> SubmitTransactionAsync(ISignedTransaction transaction);
public Task ProduceBlockAsync();
public event EventHandler<BlockProducedEventArgs>? BlockProduced;
}Operational parameters.
Key properties:
BlockTimeMs(default: 1000) - Block production intervalMaxTransactionsPerBlock(default: 1000) - Per-block transaction limitMaxPoolSize(default: 10000) - Transaction pool capacityAllowEmptyBlocks(default: false) - Whether to produce empty blocksBlockProductionMode- Interval or OnDemand
Transaction validation against access control policies.
public class PolicyEnforcer : IPolicyEnforcer
{
public PolicyEnforcer(PolicyConfig policy);
public Task<PolicyValidationResult> ValidateTransactionAsync(ISignedTransaction tx);
public void UpdatePolicy(PolicyConfig newPolicy);
}Full node wrapping IAppChain with optional sequencer.
public class AppChainNode : ChainNodeBase
{
public AppChainNode(IAppChain appChain, ISequencer? sequencer = null, IFilterStore? filterStore = null);
public bool CanAcceptTransactions { get; }
public Task<string> SendTransactionAsync(ISignedTransaction tx);
}- Nethereum.AppChain.Server - HTTP server hosting the sequencer
- Nethereum.AppChain - Core chain abstraction
- Nethereum.CoreChain - Block production and transaction processing