Skip to content

Subtree Validation Reference Documentation

Overview

The Server type implements the core functionality for subtree validation in a blockchain system. It handles subtree and transaction metadata processing, interacts with various data stores, and manages Kafka consumers for distributed processing. The service is a critical component in validating transaction subtrees for inclusion in the blockchain.

Types

Server

The Server is the central component of the subtreevalidation package, managing the complete lifecycle of subtree validation including transaction validation, storage, and integration with other Teranode services.

type Server struct {
    // UnimplementedSubtreeValidationAPIServer embeds the auto-generated gRPC server base
    subtreevalidation_api.UnimplementedSubtreeValidationAPIServer

    // logger handles all logging operations for the service
    logger ulogger.Logger

    // settings contains the configuration parameters for the service
    // including connection details, timeouts, and operational modes
    settings *settings.Settings

    // subtreeStore manages persistent storage of subtrees
    // This blob store is used to save and retrieve complete subtree structures
    subtreeStore blob.Store

    // txStore manages transaction storage
    // This blob store is used to save and retrieve individual transactions
    txStore blob.Store

    // utxoStore manages the Unspent Transaction Output (UTXO) state
    // It's used during transaction validation to verify input spending
    utxoStore utxo.Store

    // validatorClient provides transaction validation services
    // It's used to validate transactions against consensus rules
    validatorClient validator.Interface

    // subtreeCount tracks the number of subtrees processed
    // Uses atomic operations for thread-safe access
    subtreeCount atomic.Int32

    // stats tracks operational statistics for monitoring and diagnostics
    stats *gocore.Stat

    // prioritySubtreeCheckActiveMap tracks active priority subtree checks
    // Maps subtree hash strings to boolean values indicating check status
    prioritySubtreeCheckActiveMap map[string]bool

    // prioritySubtreeCheckActiveMapLock protects concurrent access to the priority map
    prioritySubtreeCheckActiveMapLock sync.Mutex

    // blockchainClient interfaces with the blockchain service
    // Used to retrieve block information and validate chain state
    blockchainClient blockchain.ClientI

    // subtreeConsumerClient consumes subtree-related Kafka messages
    // Handles incoming subtree validation requests from other services
    subtreeConsumerClient kafka.KafkaConsumerGroupI

    // txmetaConsumerClient consumes transaction metadata Kafka messages
    // Processes transaction metadata updates from other services
    txmetaConsumerClient kafka.KafkaConsumerGroupI

    // invalidSubtreeKafkaProducer publishes invalid subtree events to Kafka
    invalidSubtreeKafkaProducer kafka.KafkaAsyncProducerI

    // invalidSubtreeLock is used to synchronize access to the invalid subtree producer
    invalidSubtreeLock sync.Mutex

    // invalidSubtreeDeDuplicateMap is used to de-duplicate invalid subtree messages
    invalidSubtreeDeDuplicateMap *expiringmap.ExpiringMap[string, struct{}]

    // orphanage is used to store transactions that are missing parents that can be validated later
    orphanage *expiringmap.ExpiringMap[chainhash.Hash, *bt.Tx]

    // orphanageLock is used to make sure we only process the orphanage once at a time
    orphanageLock sync.Mutex

    // pauseSubtreeProcessing is used to pause subtree processing while a block is being processed
    pauseSubtreeProcessing atomic.Bool

    // bestBlockHeader is used to store the current best block header
    bestBlockHeader atomic.Pointer[model.BlockHeader]

    // bestBlockHeaderMeta is used to store the current best block header metadata
    bestBlockHeaderMeta atomic.Pointer[model.BlockHeaderMeta]

    // currentBlockIDsMap is used to store the current block IDs for the current best block height
    currentBlockIDsMap atomic.Pointer[map[uint32]bool]
}

ValidateSubtree

The ValidateSubtree structure encapsulates all the necessary information required to validate a transaction subtree, providing a clean interface for the validation methods.

type ValidateSubtree struct {
    // SubtreeHash is the hash identifier of the subtree to validate
    SubtreeHash chainhash.Hash

    // BaseURL is the source location for retrieving missing transactions
    BaseURL string

    // TxHashes contains the hashes of all transactions in the subtree
    TxHashes []chainhash.Hash

    // AllowFailFast determines whether validation should stop on first error
    // When true, validation will terminate immediately upon encountering an invalid transaction
    // When false, validation will attempt to validate all transactions before returning
    AllowFailFast bool
}

missingTx

This structure pairs a transaction with its index in the original subtree transaction list, allowing the validation process to maintain the correct ordering and relationship of transactions.

type missingTx struct {
    // tx is the actual transaction data that was retrieved
    tx *bt.Tx

    // idx is the original position of this transaction in the subtree's transaction list
    idx int
}

Constructor

New

func New(
    ctx context.Context,
    logger ulogger.Logger,
    tSettings *settings.Settings,
    subtreeStore blob.Store,
    txStore blob.Store,
    utxoStore utxo.Store,
    validatorClient validator.Interface,
    blockchainClient blockchain.ClientI,
    subtreeConsumerClient kafka.KafkaConsumerGroupI,
    txmetaConsumerClient kafka.KafkaConsumerGroupI,
) (*Server, error)

Creates a new Server instance with the provided dependencies. This factory function constructs and initializes a fully configured subtree validation service, injecting all required dependencies. It follows the dependency injection pattern to ensure testability and proper separation of concerns.

Initialization Features:

  • Quorum management: Initializes singleton quorum manager for distributed locking
  • Transaction metadata caching: Optionally wraps UTXO store with caching layer
  • Kafka producer setup: Configures invalid subtree event publishing if enabled
  • Orphanage initialization: Sets up orphaned transaction storage with configurable timeout
  • Blockchain subscription: Establishes blockchain event listener for block updates
  • Best block tracking: Initializes current blockchain state tracking

Initialization Process

The method ensures that the service is configured with proper stores, clients, and settings before it's made available for use. It also initializes internal tracking structures and statistics for monitoring.

Core Methods

Health

func (u *Server) Health(ctx context.Context, checkLiveness bool) (int, string, error)

Checks the health status of the service and its dependencies. This method implements the standard Teranode health check interface used across all services for consistent monitoring, alerting, and orchestration. It provides both readiness and liveness checking capabilities to support different operational scenarios.

Health Check Components

The method performs checks appropriate to the service's role, including:

  • Store access verification: Subtree, transaction, and UTXO data stores
  • Service connections: Validator and blockchain service connectivity
  • Kafka consumer health: Message processing capability
  • Internal state consistency: Service operational status

HealthGRPC

func (u *Server) HealthGRPC(ctx context.Context, _ *subtreevalidation_api.EmptyMessage) (*subtreevalidation_api.HealthResponse, error)

Implements the gRPC health check endpoint, translating the core health check results to the gRPC protocol format.

Init

func (u *Server) Init(ctx context.Context) (err error)

Initializes the server metrics and performs any necessary setup. This method completes the initialization process by setting up components that require runtime initialization rather than construction-time setup. It's called after New() but before Start() to ensure all systems are properly initialized.

The initialization is designed to be idempotent and can be safely called multiple times, though typically it's only called once after construction and before starting the service.

Start

func (u *Server) Start(ctx context.Context, readyCh chan<- struct{}) error

Initializes and starts the server components including Kafka consumers and gRPC server. This method launches all the operational components of the subtree validation service.

Components Started:

  • FSM state synchronization: Waits for blockchain FSM to transition from IDLE state
  • Kafka consumers: For subtree and transaction metadata messages with retry configuration
  • gRPC server: For API access and inter-service communication
  • Background workers: Any timers or workers required for operation

Startup Configuration:

  • Subtree consumer: Configured with retry-and-move-on policy (3 retries, 2 second backoff)
  • Transaction metadata consumer: Configured with no retries (0 retries, 1 second backoff)
  • gRPC registration: Registers SubtreeValidationAPI service endpoints

Startup Sequence

The method implements a safe startup sequence to ensure all components are properly initialized before the service is marked as ready. It also handles proper error propagation if any component fails to start.

Once all components are successfully started, the method signals readiness through the provided channel and then blocks until the context is canceled or an error occurs. This design allows the caller to coordinate the startup of multiple services.

Stop

func (u *Server) Stop(_ context.Context) error

Gracefully shuts down the server components including Kafka consumers. This method ensures a clean and orderly shutdown of all service components, allowing in-progress operations to complete when possible and releasing all resources properly.

Shutdown Sequence

The method follows a consistent shutdown sequence:

  1. Stop accepting new requests - Prevents new work from starting
  2. Pause Kafka consumers - Prevents new messages from being processed
  3. Wait for completion - Allows in-progress operations to complete (with timeouts)
  4. Release resources - Closes connections and frees allocated resources

The method is designed to be called when the service needs to be terminated, either for normal shutdown or in response to system signals.

CheckSubtreeFromBlock

func (u *Server) CheckSubtreeFromBlock(ctx context.Context, request *subtreevalidation_api.CheckSubtreeFromBlockRequest) (*subtreevalidation_api.CheckSubtreeFromBlockResponse, error)

Validates a subtree and its transactions based on the provided request. This method is the primary gRPC API endpoint for subtree validation, responsible for coordinating the validation process for an entire subtree of interdependent transactions. It ensures that all transactions in the subtree adhere to consensus rules and can be added to the blockchain.

Key Features:

  • Distributed locking: Prevents duplicate validation of the same subtree
  • Retry logic: Lock acquisition with exponential backoff
  • Backward compatibility: Support for both legacy and current validation paths
  • Resource cleanup: Proper cleanup even in error conditions
  • Structured responses: Appropriate gRPC status codes
  • Orphan processing: Handles orphaned transactions after subtree validation

Validation Criteria

The validation process ensures that:

  • Consensus compliance: All transactions are valid according to consensus rules
  • Input validity: All transaction inputs refer to unspent outputs or other transactions in the subtree
  • No double-spending: No conflicts exist within the subtree or with existing chain state
  • Policy compliance: Transactions satisfy all policy rules (fees, standardness, etc.)

Resilience

The method will retry lock acquisition for up to 20 seconds with exponential backoff, making it resilient to temporary contention when multiple services attempt to validate the same subtree simultaneously.

CheckBlockSubtrees

func (u *Server) CheckBlockSubtrees(ctx context.Context, request *subtreevalidation_api.CheckBlockSubtreesRequest) (*subtreevalidation_api.CheckBlockSubtreesResponse, error)

Validates all subtrees referenced in a block to ensure they exist in storage and are properly validated. This method is used during block validation to verify that all subtrees in a block are available and valid before the block can be accepted.

Key Features:

  • Pause mechanism: Temporarily pauses subtree processing during validation to avoid conflicts
  • Chain awareness: Only pauses processing for blocks on the current chain or extending it
  • Parallel processing: Validates multiple subtrees concurrently for performance
  • Level-based validation: Processes transactions in dependency order across all subtrees
  • Stream processing: Efficiently processes subtree data directly from HTTP streams
  • Orphan handling: Manages orphaned transactions discovered during validation

Validation Process:

  1. Chain verification: Determines if the block is on the current chain or extending it
  2. Subtree existence check: Verifies which subtrees are missing from local storage
  3. Data retrieval: Fetches missing subtree data from network sources
  4. Transaction extraction: Extracts all transactions from all subtrees
  5. Level-based processing: Validates transactions in dependency order
  6. Subtree validation: Validates individual subtrees after transaction processing
  7. Orphan processing: Handles any orphaned transactions found during validation

Performance Optimization

The method uses several optimization techniques including stream processing for direct HTTP data handling, block-wide validation for better dependency resolution, parallel subtree processing, and efficient memory management during large block processing.

Transaction Metadata Management

GetUutxoStore

func (u *Server) GetUutxoStore() utxo.Store

Returns the UTXO store instance used by the server. This method provides access to the store that manages unspent transaction outputs.

SetUutxoStore

func (u *Server) SetUutxoStore(s utxo.Store)

Sets the UTXO store instance for the server. This method allows runtime replacement of the UTXO store, which can be useful for testing or for implementing different storage strategies.

SetTxMetaCache

func (u *Server) SetTxMetaCache(ctx context.Context, hash *chainhash.Hash, txMeta *meta.Data) error

Stores transaction metadata in the cache if caching is enabled. This method optimizes validation performance by caching frequently accessed transaction metadata.

Implementation Note

This method signature is documented but the actual implementation may delegate to the underlying UTXO store's caching mechanism.

SetTxMetaCacheFromBytes

func (u *Server) SetTxMetaCacheFromBytes(_ context.Context, key, txMetaBytes []byte) error

Stores raw transaction metadata bytes in the cache. This is a lower-level method that can be more efficient when the raw bytes are already available.

DelTxMetaCache

func (u *Server) DelTxMetaCache(ctx context.Context, hash *chainhash.Hash) error

Removes transaction metadata from the cache if caching is enabled. This ensures cache consistency when transactions are modified or invalidated.

DelTxMetaCacheMulti

func (u *Server) DelTxMetaCacheMulti(ctx context.Context, hash *chainhash.Hash) error

Removes multiple transaction metadata entries from the cache. This method is optimized for batch operations when multiple related entries need to be invalidated.

Internal Validation Methods

checkSubtreeFromBlock

func (u *Server) checkSubtreeFromBlock(ctx context.Context, request *subtreevalidation_api.CheckSubtreeFromBlockRequest) (ok bool, err error)

Internal implementation of subtree validation logic. This method contains the core business logic for validating a subtree, separated from the API-level concerns handled by the public CheckSubtreeFromBlock method. The separation allows for cleaner testing and better separation of concerns.

Subtree Storage Convention

The method expects the subtree to be stored in the subtree store with a special extension (.subtreeToCheck instead of .subtree) to differentiate between validated and unvalidated subtrees. This prevents the validation service from mistakenly treating an unvalidated subtree as already validated.

The validation process includes:

  1. Retrieving the subtree data from storage
  2. Parsing the subtree structure and transaction list
  3. Checking for existing transaction metadata to avoid redundant work
  4. Retrieving and validating missing transactions from appropriate sources
  5. Verifying transaction dependencies and ordering within the subtree
  6. Confirming all transactions meet consensus rules for blockchain inclusion

ValidateSubtreeInternal

func (u *Server) ValidateSubtreeInternal(ctx context.Context, v ValidateSubtree, blockHeight uint32,
    blockIds map[uint32]bool, validationOptions ...validator.Option) (err error)

Performs the actual validation of a subtree. This is the core method of the subtree validation service, responsible for the complete validation process of a transaction subtree. It handles the complex task of verifying that all transactions in a subtree are valid both individually and collectively, ensuring they can be safely added to the blockchain.

Validation Process Steps

The validation process includes several key steps:

  1. Retrieving the subtree structure and transaction list
  2. Identifying which transactions need validation (missing metadata)
  3. Retrieving missing transactions from appropriate sources
  4. Validating transaction dependencies and ordering
  5. Applying consensus rules to each transaction
  6. Managing transaction metadata storage and updates
  7. Handling any conflicts or validation failures

Performance Optimization Techniques:

  • Batch processing of transaction validations where possible
  • Caching of transaction metadata to avoid redundant validation
  • Parallel processing of independent transaction validations
  • Early termination for invalid subtrees (when AllowFailFast is true)
  • Efficient retrieval of missing transactions in batches

blessMissingTransaction

func (u *Server) blessMissingTransaction(ctx context.Context, subtreeHash chainhash.Hash, tx *bt.Tx, blockHeight uint32,
    blockIds map[uint32]bool, validationOptions *validator.Options) (txMeta *meta.Data, err error)

Validates a transaction and retrieves its metadata, performing the core consensus validation operations required for blockchain inclusion. This method applies full validation to a transaction, ensuring it adheres to all Bitcoin consensus rules and can be properly included in the blockchain.

Validation Components

The validation includes:

  • Transaction format: Structure and format validation
  • Input signatures: Cryptographic signature verification
  • UTXO availability: Input UTXO availability and spending authorization
  • Fee calculation: Fee calculation and policy enforcement
  • Script execution: Script execution and validation
  • Double-spend prevention: Conflict detection and prevention

Upon successful validation, the transaction's metadata is calculated and stored, making it available for future reference and for validation of dependent transactions.

checkCounterConflictingOnCurrentChain

func (u *Server) checkCounterConflictingOnCurrentChain(ctx context.Context, txHash chainhash.Hash, blockIds map[uint32]bool) error

Checks if the counter-conflicting transactions of a given transaction have already been mined on the current chain. If they have, it returns an error indicating that the transaction is invalid.

Transaction Retrieval Methods

getSubtreeTxHashes

func (u *Server) getSubtreeTxHashes(spanCtx context.Context, stat *gocore.Stat, subtreeHash *chainhash.Hash, baseURL string) ([]chainhash.Hash, error)

Retrieves transaction hashes for a subtree from a remote source. This method fetches the list of transactions that are part of a given subtree from a network peer or another service.

Retrieval Strategy:

  • Local first: Checks for existing .subtreeToCheck files in local storage
  • Network fallback: Fetches subtree hash list from remote URL if not available locally
  • Buffered processing: Uses buffered I/O for efficient hash list processing

processMissingTransactions

func (u *Server) processMissingTransactions(ctx context.Context, subtreeHash chainhash.Hash, subtree *util.Subtree,
    missingTxHashes []utxo.UnresolvedMetaData, allTxs []chainhash.Hash, baseURL string, txMetaSlice []*meta.Data, blockHeight uint32,
    blockIds map[uint32]bool, validationOptions ...validator.Option) (err error)

Handles the retrieval and validation of missing transactions in a subtree, coordinating both the retrieval process and the validation workflow. This method is a critical part of the subtree validation process.

Key Responsibilities

  1. Retrieving transactions that are referenced in the subtree but not available locally
  2. Organizing transactions into dependency levels for ordered processing
  3. Validating each transaction according to consensus rules
  4. Managing parallel processing of independent transaction validations
  5. Tracking validation results and updating transaction metadata

Resilience Features:

  • Multiple retrieval methods: Supports both file-based and network-based transaction retrieval
  • Fallback mechanisms: Ensures maximum resilience with automatic failover
  • Level-based processing: Transactions are grouped by dependency level and processed in order
  • Dependency ordering: Parent transactions are validated before their children

prepareTxsPerLevel

func (u *Server) prepareTxsPerLevel(ctx context.Context, transactions []missingTx) (uint32, [][]missingTx, error)

Organizes transactions by their dependency level for ordered processing. This method implements a topological sorting algorithm to organize transactions based on their dependency relationships. Transactions are grouped into levels, where each level contains transactions that can be processed independently of each other, but depend on transactions from previous levels.

Algorithm Details:

  • Dependency graph construction: Builds adjacency lists for efficient parent-child lookups
  • Level assignment: Assigns each transaction to the appropriate dependency level
  • Memory optimization: Pre-allocates slices based on calculated level sizes
  • Coinbase handling: Properly handles coinbase transactions in dependency analysis

getSubtreeMissingTxs

func (u *Server) getSubtreeMissingTxs(ctx context.Context, subtreeHash chainhash.Hash, subtree *util.Subtree,
    missingTxHashes []utxo.UnresolvedMetaData, allTxs []chainhash.Hash, baseURL string) ([]missingTx, error)

Retrieves transactions that are referenced in a subtree but not available locally. This method implements an intelligent retrieval strategy for missing transactions with optimizations for different scenarios.

Intelligent Retrieval Strategy

The method first checks if a complete subtree data file exists locally, which would contain all transactions. If not available, it makes a decision based on the percentage of missing transactions:

  • High missing percentage: Attempts to fetch the entire subtree data file from the peer to optimize network usage (configurable threshold via PercentageMissingGetFullData setting)
  • Low missing percentage: Retrieves only the specific missing transactions individually
  • Automatic fallback: Falls back to individual transaction retrieval if subtree data fetch fails

Resilience Features:

  • Fallback mechanisms: Ensures maximum resilience with automatic failover
  • Multiple retrieval methods: Switches between file-based and network-based retrieval as needed
  • Network optimization: Minimizes bandwidth usage through intelligent batching decisions

getMissingTransactionsFromFile

func (u *Server) getMissingTransactionsFromFile(ctx context.Context, subtreeHash chainhash.Hash, missingTxHashes []utxo.UnresolvedMetaData,
    allTxs []chainhash.Hash) (missingTxs []missingTx, err error)

Retrieves missing transactions from a locally stored subtree data file. This method attempts to read transaction data from a locally stored subtree file, which can be more efficient than retrieving individual transactions from the network.

File Processing:

  • Subtree reconstruction: Rebuilds subtree structure from transaction hashes if needed
  • Data file reading: Reads from .subtreeData files in blob storage
  • Transaction mapping: Maps requested transaction hashes to their positions in the subtree
  • Efficient lookup: Uses subtree lookup maps for fast transaction retrieval

getMissingTransactionsFromPeer

func (u *Server) getMissingTransactionsFromPeer(ctx context.Context, subtreeHash chainhash.Hash, missingTxHashes []utxo.UnresolvedMetaData,
    baseURL string) (missingTxs []missingTx, err error)

Retrieves missing transactions from a peer node. This method handles the network communication required to fetch transactions that are not available locally, organizing the retrieval into batches for efficiency.

getMissingTransactionsBatch

func (u *Server) getMissingTransactionsBatch(ctx context.Context, subtreeHash chainhash.Hash, txHashes []utxo.UnresolvedMetaData, baseURL string) ([]*bt.Tx, error)

Retrieves a batch of transactions from the network. This method optimizes network utilization by fetching multiple transactions in a single request, reducing the overhead of multiple separate requests.

isPrioritySubtreeCheckActive

func (u *Server) isPrioritySubtreeCheckActive(subtreeHash string) bool

Checks if a priority subtree check is active for the given subtree hash. Priority checks get special handling and resource allocation to ensure critical subtrees are validated promptly.

processOrphans

func (u *Server) processOrphans(ctx context.Context, blockHash chainhash.Hash, blockHeight uint32, blockIds map[uint32]bool)

Processes orphaned transactions that were discovered during subtree validation. This method attempts to validate transactions that were previously missing parents, organizing them by dependency level and processing them in parallel where possible.

Orphan Processing:

  • Dependency analysis: Organizes orphaned transactions by dependency level
  • Parallel validation: Processes independent transactions concurrently
  • Automatic cleanup: Removes successfully validated transactions from the orphanage
  • Metrics tracking: Tracks the number of orphans processed for monitoring

publishInvalidSubtree

func (u *Server) publishInvalidSubtree(ctx context.Context, subtreeHash, peerURL, reason string)

Publishes invalid subtree events to Kafka for system-wide notification. This method helps coordinate the handling of invalid subtrees across the network by notifying other services.

Features:

  • Deduplication: Prevents duplicate notifications for the same invalid subtree
  • State awareness: Only publishes during normal operation (not during sync)
  • Structured messaging: Uses protobuf messages for reliable communication

Kafka Handlers

consumerMessageHandler

func (u *Server) consumerMessageHandler(ctx context.Context) func(msg *kafka.KafkaMessage) error

Returns a function that processes Kafka messages for subtree validation. It handles both recoverable and unrecoverable errors appropriately. The handler includes sophisticated error categorization to determine whether errors should result in message reprocessing or rejection.

Handler Features

Key features include:

  • Error categorization: Different handling for recoverable vs. non-recoverable errors
  • State-aware processing: Considers the current blockchain state
  • Context handling: Proper context cancellation handling
  • Idempotent processing: Prevents duplicate validation

subtreesHandler

func (u *Server) subtreesHandler(msg *kafka.KafkaMessage) error

Handles incoming subtree messages from Kafka. This method unmarshals the message, extracts the subtree hash and base URL, acquires the appropriate lock, and triggers the validation process. It includes comprehensive error handling and logging for operational visibility.

txmetaHandler

func (u *Server) txmetaHandler(msg *kafka.KafkaMessage) error

Handles incoming transaction metadata messages from Kafka. This method processes updates to transaction metadata that might be required for proper subtree validation, ensuring the metadata store remains consistent with the latest transaction state.

Additional Internal Methods

updateBestBlock

func (u *Server) updateBestBlock(ctx context.Context) error

Updates the service's cached best block information by querying the blockchain client. This method maintains current blockchain state for validation operations.

State Updates:

  • Best block header: Updates cached best block header
  • Block metadata: Updates cached block metadata including height
  • Block ID mapping: Updates current block IDs map for chain validation
  • Store synchronization: Updates subtree store's current block height

blockchainSubscriptionListener

func (u *Server) blockchainSubscriptionListener(ctx context.Context)

Background goroutine that listens for blockchain events and updates the service's cached blockchain state. This method ensures the service maintains current blockchain information for validation operations.

Subscription Features:

  • Automatic reconnection: Handles subscription failures with backoff and retry
  • Block notifications: Processes block addition notifications
  • State synchronization: Updates best block information on block events
  • Graceful shutdown: Properly handles context cancellation