Number: SLIP-0048
Title: Deterministic key hierarchy for Graphene-based networks
Type: Informational
Status: Draft
Authors: Fabian Schuh <[email protected]>
Created: 2016-10-18
This SLIP defines the logical hierarchy for deterministic wallets using Graphene technology. It extends BIP-0044 known from Bitcoin which should be read and understood before going into the details of this specification.
The key derivation structure defined in BIP-0044/SLIP-0044 does not properly represent the possibilities available to Graphene based networks. For this reason, we defined this SLIP and provide a standard for hierarchies on those networks.
Graphene-based blockchains (such as BitShares, Steem, Peerplays, MUSE, etc.) do not use the UTXO model. Instead, there are accounts registered on the blockchains that have a (modifiable) role scheme attached to each of them. The actual roles depend on the use case of the network but most of them constitute an owner
and an active
role among others. Usually, the only difference between owner
and active
role is that the owner
can change the owner
role, while the active
cannot, and thus represents some kind of cold storage or super-admin roles.
Technically, each role can consist of multiple (weighted) keys or other accounts to facilitate hierarchical weighted roles on the blockchain.
Wallets are supposed to have at least one key installed that is associated with the account's owner role (i.e. the owner
key) to allow recovery.
The memo
key is different in that it is not a roles but a single key that is assigned to an account. This key is used for private (encrypted) messaging to derive a shared secret between sender and receiver.
m / purpose' / network' / role' / account-index' / key-index'
Each level has a special meaning, described in the chapters below. Apostrophe in the path indicates that BIP32 hardened derivation is used.
Purpose is a constant set to 48' (or 0x80000030) following the BIP43 recommendation. It indicates that the subtree of this node is used according to this specification.
Hardened derivation is used at this level.
One master node (seed) can be used for unlimited number of independent keys which can be used in different networks such as BitShares, Steem, PeerPlays and others. However, sharing the same space for various networks has some disadvantages.
This level creates a separate subtree for every network, avoiding reusing addresses across networks and improving privacy issues.
network
is a constant, set for each network. Developers may ask for registering unused number for their project.
The list of already allocated networks is in the chapter "Registered networks" below.
Hardened derivation is used at this level.
Each account can be associated with its own keys. To distinguish different roles, a roles id is used to obtain a specific sub tree. Since each Graphene-based network can have it's own specific set of roles, the actually used role indices are provided in the section "Registered networks", below.
Hardened derivation is used at this level.
The Role comes prior to the Account index so that a role-specific parent key can be derived which allows to derive child keys that do not interfer with other roles. A simple use-case would be a mobile wallet app that does not want to expose owner keys but only has active keys available by going through the tree starting with:
m / purpose' / network' / [active]
Since hierarchical key derivation can be used to obtain an infinite amount of keys, we allow users to store keys for an infinite amount of accounts by using account indices. This means that account-index 0, derives a subkey to obtain multiple keys associated with account A, while account-index 1 does the same for account B. Note that the public keys cannot be associated with each other unless a common parent node in the tree is published.
Software needs to discover all used accounts after importing the seed from an external source. Such an algorithm is described in "Account discovery" chapter.
Thus, software should prevent a update of an account with a specific key (see below) if a previous key does not have an account associated with it.
When the master seed is imported from an external source the software should start to discover the accounts in the following manner (for a specific role, e.g. active
):
- derive the first account's node (index = 0)
- derive the child keys for each role (respect the gap limit)
- use the blockchains API to obtain the account associated with one of this public keys
- if the public key is not associated with any account, stop discovery
- if there is an account associated with any of these key, increase the account index and go to step 1
Depending on the blockchain's API and available resources, account discovery could be done with multiple accounts (resepct the gap limit) at once. With a gap limit of 5 for account's and a gap limit of 5 for keys, we would need to scan for 25 keys. Combined with bloom filtering, the amount of data could be reduced at the expense of a single step lookup.
This algorithm is successful because software should disallow creation of new accounts if previous one has no associated account.
We want to be able to for example take an account's current role key and put it on a different device. If that device is compromised, we want the other (more secure) device to be able to generate the new generation of that posting key without further interaction. For this reason, each accounts-role leaf generates a new subtree of keys. This allows to keep the keys for other roles and merely update the role with the compromised keys. Other wallets with the same tree will still be able to access the accounts by deriving the new keys.
Public Key gap limit is currently set to 5. If the software hits 5 unused public keys in a row, it expects there are no used accounts beyond this point and stops searching the public key chain.
Wallet software should allow the advanced user to manually search beyond the gap limit of 5.
This paragraph describes how to onboard an existing account into this standard, e.g. for hardware wallets. Later it will be possible to create (and register) new accounts, given a funded account is already available through this specifications (account creation costs a fee on most networks).
The procedure to onboard an account involves two transactions and works as follows:
- The user requests an unused public key from the master (seed) node according to the specifications
- The obtained public key is added to the existing account's owner role (full-weight)
- This key is used for an
account_update
operation in order to replace the existing roles for sole access to the account by keys following this specification.
The advantages of this procedure are:
- This algorithm proves that it has the correct private key to obtain owner roles since this key is required to sign the
account_update
operation. - Optionally, alternative keys to specific operations (e.g. posting roles on Steem) can be added that do not follow the above specification, to allow for multi-signature schemes
- Wallets following this specification can be used solely as coldstorage for the owner key while the active key could be held outside the wallet
Disadvantages are:
- The user needs to be educated about the roles, or
- a simplified account roles setup scheme needs to be developed
Index | Network | Roles |
---|---|---|
0x00000000 | Steem | 0x0 : owner, 0x1 : active, 0x3 : memo, 0x4 : posting |
0x00000001 | BitShares | 0x0 : owner, 0x1 : active, 0x3 : memo |
0x00000002 | PeerPlays | 0x0 : owner, 0x1 : active, 0x3 : memo |
0x00000003 | Muse | 0x0 : owner, 0x1 : active, 0x3 : memo |
0x00000004 | EOS | 0x0 : owner, 0x1 : active |
Network | Role | Account-index | Key-Index | Path |
---|---|---|---|---|
Steem | active | first | first | m / 48' / 0' / 1' / 0' / 0' |
BitShares | owner | forth | forth | m / 48' / 1' / 0' / 3' / 3' |
EOS | owner | first | first | m / 48' / 4' / 0' / 0' / 0' |
2017/09/07: In the hierarchy, the key role and the account index are swapped to allow separation of roles in sub-trees.