Skip to content

Implement verbose blockchain.transaction.get with confirmation height #244

Description

@EddieHouston

Summary

blockchain.transaction.get(tx_hash, true) currently returns:

{"code":-32603,"message":"verbose transactions are currently unsupported"}

Implement verbose transaction lookup using a Bitcoin Core-compatible response, with the confirming block height exposed as an electrs extension.

Motivation

Some clients are given a transaction ID and an address associated with that transaction and need to determine whether it is confirmed and, if so, its confirming block height.

The Electrum 1.4 workflow is to derive the address's script hash, call blockchain.scripthash.get_history, find the transaction, and optionally verify it with blockchain.transaction.get_merkle.

This breaks down for very active addresses because get_history can fail with Too many history entries, even with a high --electrum-txs-limit. Raising the limit further is not robust: a lookup for one transaction becomes proportional to the address's entire history.

blockchain.transaction.get_merkle(tx_hash, height) verifies a candidate height but cannot discover it. electrs already has a direct indexed lookup for this information:

self.query.chain().tx_confirming_block(&txid)

Proposed response

Keep verbose=false unchanged and continue returning raw transaction hex.

For verbose=true, return the Bitcoin Core getrawtransaction(txid, true) object and add a namespaced electrs extension:

{
  "txid": "<txid>",
  "hash": "<wtxid>",
  "version": 2,
  "size": 194,
  "vsize": 113,
  "weight": 449,
  "locktime": 0,
  "vin": [],
  "vout": [],
  "hex": "<raw transaction>",
  "blockhash": "<confirming block hash>",
  "confirmations": 10,
  "blocktime": 1234567890,
  "time": 1234567890,
  "electrs": {
    "confirmed": true,
    "block_height": 962126
  }
}

For a known mempool transaction:

{
  "txid": "<txid>",
  "hex": "<raw transaction>",
  "electrs": {
    "confirmed": false,
    "block_height": null
  }
}

An unknown transaction should continue to return a missing-transaction error. Looking up the raw transaction first distinguishes an unknown transaction from a known unconfirmed transaction when tx_confirming_block() returns None.

Compatibility

The Electrum protocol defines the verbose=true result as the daemon-specific verbose transaction dictionary. Preserving Bitcoin Core's existing fields and meanings maintains that contract.

Putting additional fields under electrs makes the implementation-specific extension explicit, avoids collisions with current or future Bitcoin Core fields, and leaves room for future electrs-specific metadata. Existing clients can ignore the additional object.

A top-level block_height field would be simpler, but namespacing appears safer for compatibility.

Existing implementation in romanz/electrs

Current romanz/electrs already implements the core verbose lookup behavior:

transaction_get() in romanz/electrs

For verbose=true, it looks up the transaction in the electrs index, obtains the confirming block hash when confirmed, supplies that block hash to the daemon, and returns Bitcoin Core's verbose transaction response:

if verbose {
    let blockhash = self
        .tracker
        .lookup_transaction(txid)?
        .map(|(blockhash, _tx)| blockhash);
    return self.daemon.get_transaction(&txid, blockhash, verbose);
}

This avoids requiring Bitcoin Core's global txindex for confirmed transactions. The proposed implementation here could follow the same approach, then add the height already known by electrs. The romanz/electrs implementation does not currently add block_height; that is the additional behavior proposed in this issue.

Implementation outline

  1. Use tx_confirming_block() to determine confirmation status, height, and block hash.
  2. For a confirmed transaction, request getrawtransaction(txid, true, blockhash) from Bitcoin Core. Supplying electrs's indexed block hash avoids requiring Bitcoin Core's global txindex.
  3. For a mempool transaction, request the normal verbose transaction response.
  4. Add the electrs object before returning the response.

Constructing the verbose object locally would avoid another daemon RPC, but matching Bitcoin Core's response accurately would be more complex, particularly for Liquid/Elements builds.

Relationship to Electrum Protocol 1.7

Electrum Protocol 1.7 proposes blockchain.outpoint.get_status, which returns funder_height and addresses this use case when the output index and scriptPubKey are known. Protocol 1.7 support is tracked in #231.

Verbose transaction support remains useful independently because:

  • deployed clients and servers still commonly negotiate protocol 1.4;
  • the query is naturally TXID-oriented;
  • clients may not yet know the matching output index;
  • it avoids fetching the complete history of a high-activity address;
  • romanz/electrs already demonstrates a practical implementation using the existing index and Bitcoin Core's verbose response.

This can provide a backward-compatible near-term solution while #231 tracks the broader protocol upgrade.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions