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:
- Step 1: Add a new
migration_hash and completely reparse the database to calculate it for all blocks
- Step 2: Fix
script_to_address() function
- Step 3: Reparse to calculate the new
ledger_hash while ensuring the migration_hash remains identical
- Step 4: Use the
migration_hash as the new ledger_hash
API Changes
No change
Database Changes
No change
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:
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
Code Changes
To add support for a type, we must do it in 3 places:
parse_vout()in Rust)get_transaction_sources()in Python)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-utilslibrary.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_hashis calculated by concatenating theblock_index, address, asset, and quantity for each row added to thedebitsandcreditstables.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:
migration_hashand completely reparse the database to calculate it for all blocksscript_to_address()functionledger_hashwhile ensuring themigration_hashremains identicalmigration_hashas the newledger_hashAPI Changes
No change
Database Changes
No change