Skip to content

Miscellaneous SQL bugs #75

@rnbrady

Description

@rnbrady

Summary

Reviewing the SQL after investigating the block ingestion issue turned up several independent SQL bugs/edge cases. These should be fixed one at a time with failing tests followed by fixes.

1. block.encoded_hex produces incorrect output

block.encoded_hex encodes the block transaction count using COUNT(cursor), which is not the transaction count. On PostgreSQL 14 this tends to emit 01; on newer PostgreSQL versions it can emit 00. The rest of the block encoding appears correct in recent live samples.

2. recordNodeValidation builds invalid SQL

src/db.ts builds an INSERT INTO node_transaction ... SELECT ... FROM (VALUES ...) INNER JOIN ... query that is syntactically invalid. This path is used when a node announces a transaction that is already known to be in the database, so affected node validation rows may be missing and the agent logs an async DB error.

3. transaction_data_carrier_outputs fails on empty locking bytecode

The function evaluates get_byte(locking_bytecode, 0) without first checking that the bytecode is non-empty.

Impact: querying transaction.data_carrier_outputs can fail for transactions with a nonzero-value output whose locking_bytecode = '\x'.

Example query shape:

query EmptyLockingBytecodeDataCarrierBug {
  transaction(
    where: {
      outputs: {
        locking_bytecode: { _eq: "\\x" }
        value_satoshis: { _neq: "0" }
      }
    }
    limit: 1
  ) {
    hash
    data_carrier_outputs {
      output_index
      value_satoshis
      locking_bytecode
    }
  }
}

Observed on pc3: index 0 out of valid range, 0..-1.

4. Value aggregates return NULL for coinbase-only blocks

Functions based on SUM(...) return NULL when every input value is NULL. For coinbase-only blocks, fields like block.input_value_satoshis, block.fee_satoshis, and block.generated_value_satoshis can therefore be NULL when callers expect numeric values.

Example observed on both pc3 and Pat for block 950314:

  • transaction_count: 1
  • output_value_satoshis: 312500000
  • input_value_satoshis: null
  • fee_satoshis: null
  • generated_value_satoshis: null

fee_satoshis should be 0; generated_value_satoshis should be 312500000.

5. node_transaction_history has no primary key

The table has an internal_id sequence but no primary key/unique constraint, unlike node_block_history. A local constraint check showed only the two foreign keys.

6. parse_bytecode_pattern_with_pushdata_lengths mishandles zero-length PUSHDATA

Valid zero-length pushes lose their length bytes in the returned pattern:

  • 4c00 -> 4c
  • 4d0000 -> 4d
  • 4e00000000 -> 4e

Expected patterns should preserve the length bytes.

7. parse_bytecode_pattern_redeem can throw on malformed pushes

Possible duplicate of #55

Malformed push opcodes like 4c can throw instead of returning a partial/null result because the function reads length bytes without bounds checks. The function also uses maybe_redeem = NULL, which is never true in SQL.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions