Assessment reports>Radix>Low findings>Unbounded transaction reference validation
Category: Optimization

Unbounded transaction reference validation

Low Severity
Low Impact
Low Likelihood

Description

The kernel's check_references() function processes a transaction's reference addresses without enforcing any limit on their quantity. While transactions are bounded by a 1MB size limit, this still allows for approximately 34,000 references to be processed in a single transaction:

fn check_references(
    &mut self,
    callback: &mut M,
    references: &IndexSet<Reference>,
) -> Result<(IndexSet<GlobalAddress>, IndexSet<InternalAddress>), BootloadingError> {
    let mut global_addresses = indexset!();
    let mut direct_accesses = indexset!();

    // Unbounded iteration over references
    for reference in references.iter() {
        let node_id = &reference.0;

        if ALWAYS_VISIBLE_GLOBAL_NODES.contains(node_id) {
            continue;
        }
        // ... reference validation logic ...
    }

    Ok((global_addresses, direct_accesses))
}

Every reference requires the following:

  1. Checking against always visible nodes

  2. Validating the reference type

  3. Reading substate information

  4. Verifying the reference value

  5. Adding to appropriate collections

Impact

Processing large numbers of references could increase block processing time as each reference requires substate reads and validation, add computational overhead during transaction validation, and potentially affect block-generation timing if transactions with many references are included.

The practical impact is low because the 1MB size limit provides an upper bound on the number of references that can be included in a single transaction.

Recommendations

We recommend enforcing a limit on the number of references that can be included in a transaction.

// @ radix-common/src/constants/transaction_execution.rs
pub const MAX_TRANSACTION_REFERENCES: usize = 1024;
fn check_references(
    &mut self,
    callback: &mut M,
    references: &IndexSet<Reference>,
) -> Result<(IndexSet<GlobalAddress>, IndexSet<InternalAddress>), BootloadingError> {
    if references.len() > MAX_TRANSACTION_REFERENCES {
        return Err(BootloadingError::TooManyReferences(references.len()));
    }
    // ... existing validation logic ...
}

Remediation

This issue has been acknowledged by Radix Publishing Limited, and a fix was implemented in commit ea2ff3c2.

Zellic © 2025Back to top ↑