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
}

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 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:

  • Kafka consumers: For subtree and transaction metadata messages
  • gRPC server: For API access and inter-service communication
  • Background workers: Any timers or workers required for operation

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

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.

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.

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.

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
  1. Organizing transactions into dependency levels for ordered processing
  2. Validating each transaction according to consensus rules
  3. Managing parallel processing of independent transaction validations
  4. 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, map[uint32][]missingTx)

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.

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
  • Low missing percentage: Retrieves only the specific missing transactions individually

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 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.

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.

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.