Skip to content

Protocol Change Proposal: Fix Bech32 Address Support #3053

Description

@adamkrellenstein

Fix Bech32 Address Support

Motivation

It has been 8 years since Bech32 addresses were introduced and became the standard for most wallets.

Bech32 address support was added to Counterparty in 2018 with the following code to get the address from the script_pubkey:

bech32 = bitcoinlib.bech32.CBech32Data.from_bytes(0, script_pubkey[2:22])

This line works well for P2WPKH addresses but silently outputs the wrong values for P2WSH and P2TR addresses which are converted to P2WPKH addresses (20 bytes of script instead of 32). In 2024 during parsing optimization, this method was rewritten in Rust and the bug was intentionally reproduced to maintain backwards-compatibility. The proposal is to fix this bug, which requires a protocol change.

Current Parser Support

Address Type Support Status
P2PK Supported
P2PKH Supported
P2MS Supported
P2SH Supported
P2WPKH Supported
P2WSH Supported with bug: scripts truncated to 22 bytes as for a P2WPKH address
P2TR Supported with bug: scripts truncated to 22 bytes as for a P2WPKH address

Code Changes

To add support for a type, we must do it in 3 places:

  • When we parse the outputs to determine the destination (function parse_vout() in Rust)
  • When we parse the inputs to determine the source (function get_transaction_sources() in Python)
  • In the transaction composer (function address_to_script_pub_key() in Python)

For the first two points, it's just a matter of fixing the script_to_address() function.

In the case of Composer it is just a matter of using the correct class from the bitcoin-utils library.

The real problem is managing the numerous balances, orders, dispensers, etc. registered in the database with incorrect addresses.

Methodology

The cleanest method is to do a complete database reparse to fix all incorrect addresses. This means that all checkpoints need to be updated and we cannot use them to verify that the bug fix has no side effects elsewhere in the code.

To solve this problem and be able to verify that nothing has changed except the addresses, a new hash will be temporarily introduced. Currently, the ledger_hash is calculated by concatenating the block_index, address, asset, and quantity for each row added to the debits and credits tables.

The idea is to add a new hash using only the first 36 characters (4 for the prefix, 32 for the first 20 encoded bytes) of the address instead of the complete address:

  1. Step 1: Add a new migration_hash and completely reparse the database to calculate it for all blocks
  2. Step 2: Fix script_to_address() function
  3. Step 3: Reparse to calculate the new ledger_hash while ensuring the migration_hash remains identical
  4. Step 4: Use the migration_hash as the new ledger_hash

API Changes

No change

Database Changes

No change

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions