Rather than having a single address for each spending authority, Penumbra allows the creation of many different publicly unlinkable diversified addresses. An incoming viewing key can scan transactions to every diversified address simultaneously, so there is no per-address scanning cost. In addition, Penumbra attaches a detection key to each address, allowing a user to outsource probabilistic transaction detection to a relatively untrusted third-party scanning service.
Addresses are parameterized by diversifiers, 11-byte tags used to derive up to distinct addresses for each spending authority. The diversifier is included in the address, so it should be uniformly random. To ensure this, diversifiers are indexed by a diversifier index ; the -th diversifier is the encryption of using AES-FF1 with the diversifier key .1
Each diversifier is used to generate a diversified basepoint as
performs hash-to-group for
decaf377 as follows: first, apply BLAKE2b-512
b"Penumbra_Divrsfy" to the input, then, interpret the
64-byte output as an integer in little-endian byte order and reduce it modulo
, and finally, use the resulting element as input to the
decaf377 CDH map-to-group method.
Each address has an associated detection key, allowing the creator of the address to delegate a probabilistic detection capability to a third-party scanning service.
The detection key consists of one component,
- , the detection key (component)2,
derived as follows. Define
prf_expand(label, key, input) as BLAKE2b-512 with
key, and input
from_le_bytes(bytes) as the function that interprets its input bytes as an
integer in little-endian order, and
to_le_bytes as the function that encodes
an integer to little-endian bytes. Then
dtk_d = from_le_bytes(prf_expand(b"PenumbraExpndFMD", to_le_bytes(ivk), d))
Each payment address has three components:
- the diversifier ;
- the transmission key , a
- the clue key , a
The diversifier is derived from a diversifier index as described above. The diversifier with index is the default diversifier, and corresponds to the default payment address.
The transmission key is derived as , where is the diversified basepoint.
The clue key is is derived as , where is the conventional
The raw binary encoding of a payment address is the 75-byte string
d || pk_d || ck_d. We pad this string to 80 bytes, then apply the F4Jumble algorithm to
this padded string. This mitigates attacks where an attacker replaces a valid
address with one derived from an attacker controlled key that encodes to an
address with a subset of characters that collide with the target valid address.
For example, an attacker may try to generate an address with the first
characters matching the target address. See ZIP316 for more on this
scenario as well as F4Jumble, which is a 4-round Feistel construction.
This jumbled string is then encoded with Bech32m with the following human-readable prefixes:
penumbrafor mainnet, and
penumbra_tnXYZ_for testnets, where XYZ is the current testnet number padded to three decimal places.
This convention is not enforced by the protocol; client software could in principle construct diversifiers in another way, although deviating from this mechanism risks compromising privacy.
As in the previous section, we use the modifier “component” to distinguish between the internal key component and the external, opaque key.