Docs
v1

Concept

Addresses and keys

How a Tenrai wallet turns 24 words into ML-DSA-65 keys, how a key becomes a spend lock and an address, and what fingerprints and verification words are.

On this page

From words to keys

StepResult
24 English BIP-39 words256 bits of entropy
Optional passphrase, stretched with Argon2id32 bytes mixed into the root
HMAC-SHA512 over entropy and stretched passphraseThe wallet root
One hardened step each for purpose, coin type, account, branch, indexA 32-byte seed
ML-DSA-65 key generation from that seedOne key pair
From the recovery phrase to an address. The 24 words hold 256 bits of entropy. With an optional passphrase, stretched by Argon2id, they give the wallet root through HMAC-SHA512. A hardened path gives a key seed per address, the seed an ML-DSA-65 key pair, and its lock the Bech32m address. Side branches: the messenger key comes from the root on its own path, the recovery fingerprint from the first key, and the six verification words from the address. Every step is one-way. Optional passphrase Argon2id 24 words 256-bit entropy Wallet root HMAC-SHA512 Key seed per address path Key pair ML-DSA-65 Address encodes its lock Messenger key own path, Messenger ID Fingerprint of the first key Verification words six public words Every step is one-way: no value reveals the words or the keys before it.
From the 24 words to an address, and what else a wallet derives on the way.

The scheme is called TENRAI-HD-v1. BIP-39 serves only as a way to write down 256 bits. The derivation is neither BIP-32 nor the BIP-39 seed derivation, so wallets built for other chains do not reproduce these keys. TCC-0001 has the exact rules.

  • 24 words only. The key-generation seed is 32 bytes; a 12-word phrase would supply half the entropy and is refused.
  • Every step is hardened. There is no public derivation: nobody can compute your addresses from one public key.
  • Paths are account, then branch (0 receive, 1 change), then index.
  • The passphrase is normalized (NFKD) before use, may be up to 256 bytes, and is never stored. It protects a stolen phrase. It does not add to the 256 bits the words already hold.

Argon2id runs once per unlock with 64 MiB of memory and 3 passes, not once per address.

The messenger key comes from the same root on its own hardened path, m/0x544D'/0x5452'/0', which shares no step with the address keys.

From key to address

A spend lock is 41 bytes: one profile byte and a 40-byte commitment.

ProfileCommitment
1, single keySHAKE256("TRAI_LOCK_V1" ‖ 0x01 ‖ public key), 40 bytes
2, thresholdThe same over a 2-of-3 or 3-of-5 set of keys

An address is a format byte and the lock, encoded as lower-case Bech32m with the network's prefix:

NetworkPrefixPayment scheme
Mainnettraitrai:
Testnet, Testnet100ttraitrai-test:
Developmentdtraitrai-dev:

Parsers refuse mixed case, a bad checksum, a wrong prefix and unknown lock data.

The public key stays hidden behind the hash until the address first spends. After that it sits in the chain's key registry. That is safe: ML-DSA is post-quantum, so reusing an address does not weaken it, and it makes later spends smaller.

Burn address

The single-key lock whose commitment is 40 zero bytes is the burn lock. Spending from it would take a public key that hashes to zero, which nobody can find. The chain burns every transaction output paid to it, so the amount leaves the supply. Each network has one burn address:

Text
Mainnet               trai1qyqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqhhus7s
Testnet, Testnet100   ttrai1qyqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfztafh
Development           dtrai1qyqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpxq6ml

Recovery fingerprint

A wrong passphrase cannot be detected; it opens a different, valid, empty wallet. The fingerprint makes the difference visible:

Text
digest      = SHA-384("TENRAI-HD-v1/WALLET-FINGERPRINT" ‖ first receive public key)
fingerprint = "TRAI-" ‖ 4 hex digits ‖ "-" ‖ 4 hex digits      for example TRAI-9C41-0B7E

Wallets show it when a wallet is created and whenever one is restored. It is part of TCC-0001.

Verification words

Six public words stand for an address, so two people can compare it by eye or by voice:

Text
digest = SHA-384("TRAI_ADDRESS_WORDS_V1", [prefix, address version, lock])
words  = six 11-bit indexes into a fixed list of 2,048 words
  • The list shares no word with the BIP-39 list, so the words are never mistaken for a recovery phrase.
  • The prefix is part of the hash: one key has different words on mainnet and on a testnet.
  • Finding a key whose address shows the same six words as a chosen address takes about 2^66 key generations.

They are a comparison aid. Anyone who knows the address can compute them, so they prove nothing about ownership. For that there is the ownership proof. The word list and the derivation are TCC-0003.

Wallet file

The wallet file stores only the 32 bytes of entropy, encrypted. It is 103 bytes long:

BytesField
9Magic TRAISEED1
2Version
16Argon2id salt
24XChaCha20-Poly1305 nonce
4Ciphertext length
48Encrypted entropy and tag

The key comes from the password through Argon2id. A wrong password and a changed byte fail the same check. The recovery passphrase is not in the file. TCC-0002 has the offsets, the encryption and a test vector.

Type to search guides, concepts and reference pages.