TIP: 102
Title: Hierarchical Deterministic Wallet
Author: federico<[email protected]>
discussions-to: https://github.com/tronprotocol/tips/issues/102
status: Last Call
type: Standards Track
category: TRC
Created: 2019-10-22
This proposal defines a mechanism for extending hierarchical deterministic wallets, as described in BIP32 and BIP44, to support both Tron's transparent and shielded addresses.
BIP32 and BIP44 are the standard mechanism by which wallets for Bitcoin and its derivatives generate keys and addresses deterministically. However, they only support the transparent address generation. This specification will provide support for the hierarchically shielded addresses derivation in a similar way as BIP32, which can offer users better privacy protection.
At present, Tron network has scheduled to support the shielded addresses by ZK-SNARKs, so it is necessary to provide the standard compatibility for shielded hierarchical deterministic wallets.
In this specification, transparent address indicates the address which is public in the transaction, while the shielded address stands for the address which is not available from the transaction.
Existing HD wallets all use BIP44 to organize their derived keys. In order to improve existing user experiences, we broadly follow BIP 44's design here. However, we have altered the design where it makes sense to leverage features of shielded addresses.
m / purpose' / coin_type' / account' / transparent' / change / address_index
Apostrophe in the path indicates that BIP32 hardened derivation is used.
-
m
: a seed generated by the mnemonic codes to generate the master key. -
purpose
: a constant set to 102' (or 0x80000066) following the BIP43 recommendation. It indicates that the subtree of this node is used according to this specification. -
coin_type
: a constant identifying the cybercoin that this subtree's keys are used with. For Tron,coin_type
is 195 as assigned in SLIP44. -
account
: numbered from index 0 in sequentially increasing manner. Defined as in BIP44. -
transparent
: for transparent addresses,transparent
is set 1; for the shielded addresses,transparent
is set 0; -
change
: for transparent addresses, constant 0 is used for external chain and constant 1 for internal chain (also known as change addresses) as in BIP44; for shielded addresses,change
field is omitted, because shielded addresses are never publicly visible in transactions. -
address_index
: numbered from index 0 in sequentially increasing manner. Defined as in BIP44.
BIP39 should be used to derive binary seed from a mnemonic code.
We strongly recommend using 24 word mnemonic (256 bits of entropy).
We adapt BIP44 for generating transparent address, except an extra transparent
field is added. This is an upgrade of TIP001. We will focus on the shielded addresses derivation in the following section.
The shielded addresses derivation process described here are mainly based on the ZIP32 standard proposed by Zcash team.
Most of the notation and functions used in this TIP are defined here for convenience:
-
truncatek(S) means the sequence formed from the first k elements of S.
-
a || b means the concatenation of sequences a then b.
-
[k] P means scalar multiplication of the elliptic curve point P by the scalar k.
-
LEOS2IPl (S) is the integer in range {0..2l -1} represented in little-endian order by the byte sequence S of length l/8.
-
I2LEBSPl (k) is the sequence of l bits representing k in little-endian order.
-
LEBS2OSPl (B) is defined as follows when l is a multiple of 8: convert each group of 8 bits in B to a byte value with the least significant bit first, and concatenate the resulting bytes in the same order as the groups.
-
reprJ (P) is the representation of the Jubjub elliptic curve point P as a bit sequence.
-
BLAKE2b-256(p, x) refers to unkeyed BLAKE2b-256 in sequential mode, with an output digest length of 32 bytes, 16-byte personalization string p, and input x.
-
BLAKE2b-512(p, x) refers to unkeyed BLAKE2b-512 in sequential mode, with an output digest length of 64 bytes, 16-byte personalization string p, and input x.
-
PRFexpand (sk, t) := BLAKE2b-512("Tron_ExpandSeed", sk || t )
-
ToScalar(x) := LEOS2IP512 (x) (mod rJ ), where rJ is the order of the Jubjub large prime subgroup.
-
DiversifyHash(d) maps a diversifier d to a base point on the Jubjub elliptic curve, or to ⊥ if the diversifier is invalid.
The following FPE algorithm standardized is used:
- FF1-AES256.Encrypt(key, tweak, x) refers to the FF1 encryption algorithm using AES with a 256-bit key, and parameters radix = 2, minlen = 88, maxlen = 88. It will be used only with the empty string "" as the tweak. x is a sequence of 88 bits, as is the output.
We also define the following conversion function:
- I2LEOSPl (k) is the byte sequence S of length l/8 representing in little-endian order the integer k in range {0..2l -1}. It is the reverse operation of LEOS2IPl (S).
We adapt the path notation of BIP32 to describe shielded HD paths, using apostrophes to indicate hardened derivation (i' = i + 231 ) as in BIP44:
- CDKsk(CDKsk(CDKsk(m, a'), b), c) is written as m / a' / b / c
- CDKfvk(CDKfvk(CDKfvk(M, a), b), c) is written as M / a / b / c
BIP 32 defines a method to derive a number of child keys from a parent key. In order to prevent these from depending solely on the parent key itself, both the private and public keys are extended with a 32-byte chain code. We similarly extend Sapling keys with a chain code here. However, the concepts of "private" and "public" keys in BIP 32 do not map cleanly to shielded key components. We take the following approach:
-
We derive child expanded spending keys, rather than spending keys. This enables us to implement both hardened and non-hardened derivation modes.
-
We do not derive public keys directly, as this would prevent the use of diversified addresses. Instead, we derive full viewing keys, from which payment addresses can be generated. This maintains the trust semantics of BIP 32: someone with access to a BIP 32 extended public key is able to view all transactions involving that address, which a full viewing key also enables.
We represent a extended spending key as (ask, nsk, ovk, dk, c), where (ask, nsk, ovk) is the normal expanded spending key, dk is a diversifier key, and c is the chain code.
We represent a extended full viewing key as (ak, nk, ovk, dk, c), where (ak, nk, ovk) is the normal full viewing key, dk is the same diversifier key as above, and c is the chain code.
Define EncodeExtSKParts(ask, nsk, ovk, dk) := I2LEOSP256 (ask) || I2LEOSP256 (nsk) || ovk || dk.
Define EncodeExtFVKParts(ak, nk, ovk, dk) := LEBS2OSP256 (reprJ (ak)) || LEBS2OSP256 (reprJ (nk)) || ovk || dk.
Let pathi= m / xx' / 195' / i' / 0', i denotes the i-th account. Let skpathi, pkpathi, cpathi denotes the private key, public key and chain code at pathi derived by BIP32.
-
Calculate I = BLAKE2b-512 ("TronIP102", skpathi || pkpathi || cpathi ).
-
Split I into two 32-byte sequences, IL and IR .
-
Use IL as the master spending key skm , and IR as the master chain code cm.
-
Calculate askm , nskm , and ovkm via the standard derivation function:
- askm = ToScalar(PRFexpand (skm , [0x00]))
- nskm = ToScalar(PRFexpand (skm , [0x01]))
- ovkm = truncate32 (PRFexpand (skm , [0x02]))
-
Calculate dkm similarly:
- dkm = truncate32 (PRFexpand (skm , [0x10]))
-
Return (askm , nskm , ovkm , dkm , cm ) as the master extended spending key m.
As in BIP 32, the method for deriving a child extended key, given a parent extended key and an index i, depends on the type of key being derived, and whether this is a hardened or non-hardened derivation.
CDKsk((askpar , nskpar , ovkpar , dkpar , cpar ), i) → (aski , nski , ovki , dki , ci )
-
Check whether i ≥ 231 (whether the child is a hardened key).
- If so (hardened child): let I = PRFexpand (cpar , [0x11] || EncodeExtSKParts(askpar , nskpar , ovkpar , dkpar ) || I2LEOSP32 (i))
- If not (normal child): let I = PRFexpand (cpar , [0x12] || EncodeExtFVKParts(akpar , nkpar , ovkpar , dkpar ) || I2LEOSP32 (i)) where (nkpar , akpar , ovkpar ) is the full viewing key derived from (askpar , nskpar , ovkpar ).
-
Split I into two 32-byte sequences, IL and IR .
-
Let Iask = ToScalar(PRFexpand (IL , [0x13]))
-
Let Insk = ToScalar(PRFexpand (IL , [0x14]))
-
Return:
- aski = Iask + askpar
- nski = Insk + nskpar
- ovki = truncate32 (PRFexpand (IL , [0x15] || ovkpar ))
- dki = truncate32 (PRFexpand (IL , [0x16] || dkpar ))
- ci = IR
Let G and H be the base point in elliptic curve.
CDKfvk((akpar , nkpar , ovkpar , dkpar , cpar ), i) → (aki , nki , ovki , dki , ci )
-
Check whether i ≥ 231 (whether the child is a hardened key).
- If so (hardened child): return failure
- If not (normal child): let I = PRFexpand (cpar , [0x12] || EncodeExtFVKParts(akpar , nkpar , ovkpar , dkpar ) || I2LEOSP32 (i))
-
Split I into two 32-byte sequences, IL and IR .
-
Let Iask = ToScalar(PRFexpand (IL , [0x13]))
-
Let Insk = ToScalar(PRFexpand (IL , [0x14]))
-
Return:
- aki = [Iask] G + akpar
- nki = [Insk] H + nkpar
- ovki = truncate32 (PRFexpand (IL , [0x15] || ovkpar ))
- dki = truncate32 (PRFexpand (IL , [0x16] || dkpar ))
- ci = IR
The 88-bit diversifiers for a extended key are derived from its diversifier key dk. To prevent the diversifier leaking how many diversified addresses have already been generated for an account, we make the sequence of diversifiers pseudorandom and uncorrelated to that of any other account. In order to reach the maximum possible diversifier range without running into repetitions due to the birthday bound, we use FF1-AES256 as a Pseudo-Random Permutation as follows:
- Let j be the index of the desired diversifier, in the range 0 .. 288 -1.
- dj = FF1-AES256.Encrypt(dk, "", I2LEBSP88 (j)).
A valid diversifier dj is one for which DiversifyHash(dj) ≠ ⊥. For a given dk, approximately half of the possible values of j yield valid diversifiers.
The default diversifier for an extended key is defined to be dj , where j is the least nonnegative integer yielding a valid diversifier.
A full viewing key fingerprint with raw encoding FVK is given by:
BLAKE2b-256("TronFVFP", FVK)
It MAY be used to uniquely identify a particular full viewing key.
A "full viewing key tag" is the first 4 bytes of the corresponding full viewing key fingerprint. It is intended for optimizing performance of key lookups, and MUST NOT be assumed to uniquely identify a particular key.
The following encodings are analogous to the xprv
and xpub
encodings defined
in BIP 32 for transparent keys and addresses. Each key type has a raw representation
and a Bech32 encoding.
An extended spending key (ask, nsk, ovk, dk, c), at some path, with parent full viewing key tag parent_fvk_tag and child number i, is represented as a byte sequence:
I2LEOSP8 (depth) || parent_fvk_tag || I2LEOSP32 (i) || c || EncodeExtSKParts(ask, nsk, ovk, dk)
For the master extended spending key, depth is 0, parent_fvk_tag is 4 zero bytes, and i is 0.
When encoded as Bech32, the Human-Readable Part is secret-extended-key-main
for the production network, or secret-extended-key-test
for the test network.
A extended full viewing key (ak, nk, ovk, dk, c), at depth depth, with parent full viewing key tag parent_fvk_tag and child number i, is represented as a byte sequence:
I2LEOSP8 (depth) || parent_fvk_tag || I2LEOSP32 (i) || c || EncodeExtFVKParts(ak, nk, ovk, dk)
For the master extended full viewing key, depth is 0, parent_fvk_tag is 4 zero bytes, and i is 0.
When encoded as Bech32, the Human-Readable Part is txviews
for the production
network, or txviewtest
for the test network.
Based on BIP32, BIP44 and ZIP32, We provide a standard mechanism for both transparent and shielded hierarchical deterministic wallets for Tron.
None