BTQ Docs
Dilithium

Dilithium Addresses

Dilithium address formats in BTQ. P2MR (BIP360, witness v2) Bech32m is the only Dilithium receive type. How to generate and validate one.

Dilithium Addresses

BTQ introduces Dilithium-specific address formats while maintaining backward compatibility with ECDSA addresses for legacy support.

As of v0.4.2-testnet, Dilithium lives in exactly one address type: P2MR (BIP360, witness version 2). Dilithium opcodes are consensus-valid only inside P2MR tapscript leaves. The Base58 and witness-v0 Dilithium formats described further down are historical — kept here because such outputs still exist on testnet, not because you should create new ones.

Address Types

P2MR — the current Dilithium address (Bech32m, witness v2)

A P2MR output commits to the 32-byte Merkle root of a script tree. Leaves may hold a single Dilithium key check, Dilithium multisig (OP_CHECKMULTISIGDILITHIUM), a threshold accumulator built from OP_CHECKSIGDILITHIUM, or other script policies.

P2MR uses the standard network HRP with witness version 2, encoded Bech32m — so the distinguishing marker is the 1z separator, versus 1q for ECDSA SegWit v0:

NetworkHRPDilithium P2MRECDSA SegWit v0
Mainnetqbtcqbtc1z…qbtc1q…
Testnettbtqtbtq1z…tbtq1q…
Signetqtbqtb1z…qtb1q…
Regtestqcrtqcrt1z…qcrt1q…

The witness program is the 32-byte script-tree Merkle root.

Historical: Legacy Addresses (Base58)

Superseded by P2MR. getnewdilithiumaddress no longer produces these. Documented for reading existing testnet outputs.

Base58 addresses are identified by a version byte, not by a fixed letter. The leading character is a consequence of that byte:

TypeMainnet version byteRenders asTestnet / Signet / RegtestRenders as
ECDSA Pubkey75X…111m… / n…
ECDSA Script135w…1962…
Dilithium Pubkey76X…112n…
Dilithium Script136w… / x…1972…

On mainnet you cannot tell an ECDSA pubkey address from a Dilithium one by eye — version bytes 75 and 76 both render as X… and their two-character prefixes overlap at Xa. Use validateaddress or getaddressinfo to determine the actual type; never infer it from the prefix.

Historical: Dilithium Witness v0 (Bech32)

This path was an early experiment and is disabled. Dilithium and ECDSA witness-v0 programs were too easy to confuse, and the resulting outputs were not spendable with Dilithium keys. There are no live examples of it on testnet.

The dedicated HRPs below remain in the chain parameters and are still recognized by the address decoder, but the wallet will not generate them:

NetworkECDSA HRPDilithium witness-v0 HRP
Mainnetqbtcdbtc
Testnettbtqtdbt
Signetqtbsdbt
Regtestqcrtrdbt

Generating Addresses

Using RPC

getnewdilithiumaddress is the only supported way to create a Dilithium receive address. It takes an optional label; the address_type argument, if given, must be "p2mr":

# Generate a new Dilithium P2MR address
btq-cli getnewdilithiumaddress

# Optionally attach a label
btq-cli getnewdilithiumaddress "Mining Rewards"

This RPC returns a JSON object, not a bare string. Shell snippets that capture it directly (ADDR=$(btq-cli ... getnewdilithiumaddress)) will capture the whole object. Extract the field:

btq-cli getnewdilithiumaddress | jq -r '.address'

Response fields:

FieldMeaning
addressThe P2MR receive address
p2mr_idWallet-local P2MR metadata id
scriptPubKeyThe output script
merkle_rootRoot of the committed script tree

Support Matrix

CommandResult
getnewdilithiumaddress✅ P2MR (witness v2) address
getnewdilithiumaddress "label" "p2mr"✅ same, with a label
getnewdilithiumaddress "" "legacy"❌ Dilithium receives must use P2MR
getnewdilithiumaddress "" "bech32"❌ rejected — disabled format
getnewaddress "" "dilithium-legacy"⚠️ historical Base58 format; not for new wallets
getnewaddress "" "dilithium-bech32"❌ disabled — witness-v0 experiment, turned off

Dilithium opcodes are consensus-valid only inside P2MR tapscript leaves. Legacy Dilithium P2PKH and witness-v0 destinations are no longer created by the wallet.

Address Validation

validateaddress and getaddressinfo both return an isdilithium flag. This is the reliable way to tell a Dilithium address from an ECDSA one — the Base58 prefix is not sufficient on mainnet.

$ btq-cli -testnet validateaddress "n5KchJ9AJaiHRkLpKF7Bx8dbjsgt1CZdC4"
{
  "isvalid": true,
  "address": "n5KchJ9AJaiHRkLpKF7Bx8dbjsgt1CZdC4",
  "scriptPubKey": "...",
  "isscript": false,
  "iswitness": false,
  "isdilithium": true
}

A Dilithium legacy address resolves to a P2PKH-shaped script terminated by OP_CHECKSIGDILITHIUM rather than OP_CHECKSIG:

OP_DUP OP_HASH160 <20-byte keyhash> OP_EQUALVERIFY OP_CHECKSIGDILITHIUM

Script Patterns

P2MR (current)

The output commits only to a Merkle root — no key is revealed until a spend:

scriptPubKey:

OP_2 <32-byte script tree merkle root>

Spending reveals the chosen leaf script, a control block proving the leaf is committed to the root, and the authorization the leaf requires. Common leaf shapes:

LeafScript
Single key<dilithium pubkey> OP_CHECKSIGDILITHIUM
Multisig 2-of-3OP_CHECKMULTISIGDILITHIUM over three committed keys
Threshold accumulatorper-key OP_CHECKSIGDILITHIUM summed with OP_ADD, compared to a threshold

Dilithium witnesses are far larger than ECDSA ones — a signature is 2,420 bytes and a public key 1,312 bytes, so expect P2MR spends to dwarf equivalent ECDSA transactions.

Historical: Pay-to-Dilithium-Public-Key (P2DPK)

Direct payment to a Dilithium public key:

<1312-byte Dilithium pubkey> OP_CHECKSIGDILITHIUM

Historical: Pay-to-Dilithium-Witness-PubKey-Hash (P2DWPKH)

The disabled witness-v0 form, whose scriptPubKey is indistinguishable from ECDSA P2WPKH. Documented for completeness only:

scriptPubKey:

OP_0 <20-byte keyhash>

Witness stack:

[0]: <2421-byte signature>  (2420 Dilithium + 1 sighash type)
[1]: <1312-byte pubkey>

scriptCode (for signing/verification):

OP_DUP OP_HASH160 <20-byte keyhash> OP_EQUALVERIFY OP_CHECKSIGDILITHIUM

Key Hash Computation

The 20-byte key hash is computed the same way as Bitcoin:

KeyHash = RIPEMD160(SHA256(dilithium_pubkey))

Where dilithium_pubkey is the full 1,312-byte Dilithium public key.

Address Encoding Details

Bech32m Encoding (P2MR, current)

Address = Bech32m(hrp, witness_version = 2, merkle_root)

Where hrp is the standard network HRP (qbtc / tbtq / qtb / qcrt) and the witness program is the 32-byte script-tree Merkle root. Witness version 2 encodes as the character z, which is why these read qbtc1z… / tbtq1z….

Historical: Base58 Encoding

Legacy Dilithium addresses use Base58Check encoding:

Address = Base58Check(version_byte + keyhash + checksum)

Version bytes:

  • Mainnet Dilithium pubkey: 0x4c (76) — starts with X
  • Mainnet Dilithium script: 0x88 (136) — starts with w or x
  • Testnet / Signet / Regtest Dilithium pubkey: 0x70 (112) — starts with n
  • Testnet / Signet / Regtest Dilithium script: 0xc5 (197) — starts with 2

Historical: Dilithium Witness-v0 Bech32 Encoding

The disabled witness-v0 path used dedicated HRPs:

Address = Bech32(hrp, witness_version = 0, keyhash)

Where hrp = dbtc (mainnet), tdbt (testnet), sdbt (signet), rdbt (regtest), and witness_program is a 20-byte keyhash. These decode but are never generated — see the support matrix.

Auto-Detection

BTQ's script interpreter automatically detects Dilithium vs ECDSA based on data size:

// In script interpreter
bool is_dilithium = (pubkey.size() > 100);  // 1312 vs 33 bytes

if (is_dilithium) {
    // Use OP_CHECKSIGDILITHIUM
} else {
    // Use OP_CHECKSIG (ECDSA)
}

This allows seamless mixed-mode operation without explicit type markers.

Address Book

Wallets maintain separate tracking for Dilithium and ECDSA addresses:

# List all addresses
btq-cli listreceivedbyaddress

# Filter by address type (future feature)
btq-cli listdilithiumaddresses

Security Considerations

Address Reuse

Like Bitcoin, address reuse is discouraged:

  • ECDSA: Reveals public key after first spend, reducing security margin
  • Dilithium: Same concern, though Dilithium's security margin is larger

Always generate a new address for each transaction.

Backup

Dilithium private keys are 2,560 bytes (vs 32 bytes for ECDSA):

Dilithium key backup: ~76x larger than ECDSA

Ensure your wallet backup includes all Dilithium keys. The wallet.dat file contains both ECDSA and Dilithium keys.

HD derivation (BIP-32 style) for Dilithium keys is not yet implemented. Each key must be backed up individually.

Network Prefixes Summary

The Dilithium P2MR column is the current Dilithium receive format. The Base58 columns are the historical formats, retained for reading existing outputs.

NetworkDilithium P2MR (current)ECDSA SegWit v0ECDSA Legacy (v75/111)Dilithium Legacy (v76/112)P2P Port
Mainnetqbtc1z…qbtc1q…X…X…9333
Testnettbtq1z…tbtq1q…m… / n…n…19333
Signetqtb1z…qtb1q…m… / n…n…38333
Regtestqcrt1z…qcrt1q…m… / n…n…19444

On mainnet the ECDSA and Dilithium legacy columns are both X… — version bytes 75 and 76 are adjacent and overlap in Base58. Use the isdilithium flag from validateaddress, never the prefix.

The disabled Dilithium witness-v0 HRPs (dbtc, tdbt, sdbt, rdbt) are listed separately under Historical: Dilithium Witness v0 — they are not a current address format and do not appear in the table above.

On this page