§ 04 · Keys & addresses
Address
A locking script, written for humans
On this page 8 sections
An addressAddress: The text you share to get paid, like prl1p… It holds a 32-byte key or script fingerprint plus a checksum, and every Pearl mainnet address is exactly 63 characters long. is a locking script written so that a person can copy it, read it aloud or put it in a QR code.
Under the text there are only two things: a witness versionWitness version: A number at the start of an output's lock that says which spending rules apply. Pearl accepts only version 1 (Taproot, prl1p…) and version 2 (P2MR, prl1z…). (which kind of lock) and 32 bytes of witness programWitness program: The 32 bytes inside a Pearl locking script, after the version opcode. For Taproot it is the tweaked public key; for P2MR it is the Merkle root of the scripts. An address is a witness program written in bech32m. (whose lock). Bech32m wraps them in a network prefix and a checksum. Paste an address into a wallet and the wallet unwraps it back into the locking script it puts in a transaction output.
Try it
This address received 2 PRL in block 130,825. Decode it, then change one character.
What are the parts of an address?
| Field | Size | Description |
|---|---|---|
| prefix (HRP) | 3 chars | prl: the network. Test networks use tprl, regtest rprl. |
| separator | 1 char | 1: the last 1 in the string ends the prefix. |
| version | 1 char | p = 1, Taproot. z = 2, P2MR. Nothing else is accepted. |
| program | 52 chars | 32 bytes, five bits per character: the tweaked key Q (Taproot) or the script tree root (P2MR). |
| checksum | 6 chars | Computed from everything before it. Catches typos. See bech32m. |
The human-readable partHuman-readable part (HRP): The prefix before the 1 in an address, naming its network: prl on mainnet, tprl on the test networks and rprl on regtest. is the only part you can read. Everything after the separator uses a 32-character alphabet, qpzry9x8gf2tvdw0s3jn54khce6mua7l, where each character stands for a number from 0 to 31. It leaves out 1, b, i and o, which are easy to confuse.
- Size
- 63 characters (mainnet), 64 (test networks)
- Type
- bech32m text
- Format
- prefix ‖ 1 ‖ version ‖ program ‖ checksum
Which prefixes does Pearl use?
The prefix says which network an address is for. Wallets refuse an address from the wrong network.
| Network | Prefix | Taproot address of private key 1 | Length |
|---|---|---|---|
| mainnet | prl |
prl1pmfr3p9j00pfxjh0zmgp99y8zftmd3s5pmedqhyptwy6lm87hf5sse9xfq3 |
63 |
| testnet, testnet2 | tprl |
tprl1pmfr3p9j00pfxjh0zmgp99y8zftmd3s5pmedqhyptwy6lm87hf5ssj2zhly |
64 |
| regtest | rprl |
rprl1pmfr3p9j00pfxjh0zmgp99y8zftmd3s5pmedqhyptwy6lm87hf5ssgn706v |
64 |
| Bitcoin, for comparison | bc |
bc1pmfr3p9j00pfxjh0zmgp99y8zftmd3s5pmedqhyptwy6lm87hf5sspknck9 |
62 |
Look at the middle: the 52 program characters are identical on every row. Same key, same program. Only the prefix and the checksum change, because the checksum covers the prefix too.
Two test networks share tprl, and regtest shares rprl with simnet, so an address tells you “mainnet, test or regtest”, not which testnet. (Pearl’s code also reserves tprl for a signet, but no Pearl signet runs.)
Which versions are there?
The version character picks the kind of lock. Pearl accepts exactly two.
| Character | Version | Type | Program | Status |
|---|---|---|---|---|
q |
0 | segwit v0 (Bitcoin’s bc1q) | Rejected: “unsupported witness version: 0” | |
p |
1 | Taproot (P2TR)P2TR (Pay-to-Taproot): The standard Pearl output: witness version 1, with addresses starting prl1p. It can be spent with one Schnorr signature, or through a hidden tree of scripts. | tweaked public key Q | What wallets make |
z |
2 | P2MRP2MR (Pay-to-Merkle-Root): An output locked only by a tree of scripts, with no public key in it: witness version 2, addresses starting prl1z. Valid on Pearl from the start; still a draft proposal for Bitcoin. | Merkle root of scripts | Valid; wallets never make one |
r, y, … |
3–16 | Rejected |
The program must also be exactly 32 bytes. Bech32m itself could carry 2 to 40, but Pearl’s decoder insists on 32 for both versions.
How does an address become a locking script?
Directly. Decode the version and the program, then write OP_n (0x50 + version), a push of 32 bytes (0x20) and the program:
| Address starts | Locking script (34 bytes) |
|---|---|
prl1p… |
51 20 ‖ 32-byte Q |
prl1z… |
52 20 ‖ 32-byte root |
Here is the real payment that sent 2 PRL to the address in the “Try it” box. Its second output’s locking script is 5120 followed by the 32 bytes behind vt5a5d5…:
| Field | Bytes | Value |
|---|---|---|
| Version version | 4 @0 | 1 int32 little-endian. Pearl wallets and pearld coinbases use version 1; relay policy accepts 1-2. |
| Marker marker | 1 @4 | 0x00 Segwit marker (BIP-144): says witness data follows the outputs. |
| Flag flag | 1 @5 | 0x01 Witness flag; 0x01 is the only value Pearl accepts. |
| Input count vin.length | 1 @6 | 1 CompactSize number of inputs. |
| Previous txid vin[0].txid | 32 @7 | 33218153e215d1a1e80941850a696af178fc83bd6cf266be92e7ba9170553959 The transaction being spent, stored byte-reversed (internal order); shown here in normal display order. |
| Output index vin[0].vout | 4 @39 | 683 Which output of that transaction is spent (uint32 LE). |
| scriptSig length vin[0].scriptSig.length | 1 @43 | 0 Always 0 for non-coinbase inputs in Pearl: every spend uses the witness instead. |
| Sequence vin[0].sequence | 4 @44 | 0xffffffff Final (0xffffffff): no RBF signal, no relative locktime, and locktime is ignored for this input. Pearl wallets use this. |
| Output count vout.length | 1 @48 | 2 CompactSize number of outputs. |
| Amount vout[0].value | 8 @49 | 295,646,640 grains (2.95646640 PRL) int64 little-endian, in grains (1 PRL = 100,000,000 grains). |
| Script length vout[0].scriptPubKey.length | 1 @57 | 34 CompactSize length of the locking script. |
| Locking script vout[0].scriptPubKey | 34 @58 | prl1p674s9jt2xs4amk6aq7ul867kcp8qwll5zsvpzzu6revewsu5z6eqv9d5z4 OP_1 OP_PUSHBYTES_32 <output key Q>: a Taproot output. |
| Amount vout[1].value | 8 @92 | 200,000,000 grains (2.00000000 PRL) int64 little-endian, in grains (1 PRL = 100,000,000 grains). |
| Script length vout[1].scriptPubKey.length | 1 @100 | 34 CompactSize length of the locking script. |
| Locking script vout[1].scriptPubKey | 34 @101 | prl1pvt5a5d5z42egswfv7uscht7rv2rj3z4m8n3qp3w35592ydq88v4seuhn70 OP_1 OP_PUSHBYTES_32 <output key Q>: a Taproot output. |
| Witness items witness[0].length | 1 @135 | 1 Number of witness stack items for input 0. |
| Schnorr signature length witness[0][0].length | 1 @136 | 64 CompactSize length of this witness item. |
| Schnorr signature witness[0][0] | 64 @137 | 4686c7014749649b25555874c7772ecb7a44e85454893ed5888db11ebcdc82a22d74ce1a1671d02810f5ba1dca3277409e95a653ba9644099f7da26be7a86ba2 64-byte BIP-340 signature (R.x ‖ s) with SIGHASH_DEFAULT: a Taproot key-path spend. |
| Locktime locktime | 4 @201 | 0 0: no time lock. |
That’s all a “payment to an address” is on chain: an output whose locking script is the address, unwrapped. A Taproot address holds the key itself, not a hash of it, so there is nothing to look up.
What does the decoder reject?
These are the results of feeding strings to Pearl’s own DecodeAddress on mainnet settings:
| Input | Result |
|---|---|
bc1pmfr3p9…sspknck9 (Bitcoin) |
rejected: only Taproot and P2MR addresses are supported |
1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH (base58) |
rejected, same message |
prl1qw508d6qejxtdg4y5r3zarvary0c5xw7k34d768 (version 0) |
rejected: unsupported witness version: 0 |
prl1rmfr3p9…sswn0rny (version 3) |
rejected: unsupported witness version: 3 |
prl1p…hf5ssvek99n (old bech32 checksum) |
rejected: expected bech32m encoding |
prl1pqqqq…xfau6t (20-byte program) |
rejected: witness program must be 32 bytes |
PRL1PMFR3P9J00…HF5SSE9XFQ3 (all capitals) |
accepted |
prl1pmfr3p9…sse9xFq3 (mixed case) |
rejected |
All-capital addresses are allowed because QR codes store capitals more compactly. Mixing cases is not.
Why exactly 63 characters?
Count the pieces: 3 (prl) + 1 (separator) + 1 (version) + 52 (program) + 6 (checksum) = 63.
The 52 comes from the 32 bytes: 256 bits, cut into 5-bit characters, needs 52 characters (260 bits), with 4 zero bits of padding at the end. Since every Pearl program is 32 bytes, no Pearl address is ever shorter or longer. Test addresses add one letter for the t or r: 64.
Make a guess
Someone sends you a 62-character address that starts prl1p. Is it valid?
Think of your answer first, then check it.
Answer No.
Every valid mainnet address is exactly 63 characters. A 62-character prl1p string is missing a character somewhere, and the checksum will fail. (Bitcoin’s bc1p addresses are 62 characters because their prefix is one letter shorter.)
What do Pearl wallets hand out?
- Taproot, always. Oyster and the Pearl Desktop WalletPearl Desktop Wallet: The point-and-click wallet app made by Pearl Research Labs. It creates a wallet backed by 12 words and runs Oyster behind the scenes. give out
prl1p…addresses from the BIP-86 path, a fresh one each time you ask, so payments aren’t trivially linked. - Never P2MR. P2MR outputs are valid and do appear on chain (rarely: 5 in 200 recent blocks, against 62,638 Taproot outputs), but a search of Pearl’s v1.5.2 code finds nothing outside tests that creates a P2MR address.
- Quantum-safe on request only.
getnewaddress … pq=truegives aprl1p…address whose tweak commits to an XMSS script. It looks like any other Taproot address.
Code: address to locking script
With this site’s library (the same code the tools use):
import { decodeAddress } from '~/lib/bech32m';
const a = decodeAddress('prl1pvt5a5d5z42egswfv7uscht7rv2rj3z4m8n3qp3w35592ydq88v4seuhn70');
a.network; // 'mainnet'
a.version; // 1
a.type; // 'p2tr'
a.script; // Uint8Array(34): 51 20 62e9da3682aab288392cf7218bafc36287288abb3ce200c5d1a50aa234073b2b Pearl’s own Go code does the same in btcutil.DecodeAddress and txscript.PayToAddrScript.