Unverified Commit e488787c authored by Fabian Schuh's avatar Fabian Schuh Committed by Pavol Rusnak

[Steem] Integrate 'cointype' and add specifications slip-48

parent ee8e256c
......@@ -21,6 +21,7 @@ Each SLIP should provide a concise technical specification of the feature and a
| [SLIP-0016](slip-0016.md) | Format for password storage and its encryption | Standard | Draft |
| [SLIP-0017](slip-0017.md) | Elliptic Curve Diffie-Hellman using deterministic hierarchy | Standard | Draft |
| [SLIP-0044](slip-0044.md) | Registered coin types for BIP-0044 | Standard | Draft |
| [SLIP-0048](slip-0048.md) | Deterministic key hierarchy for Graphene-based networks | Informational | Draft |
---
......
......@@ -122,7 +122,7 @@ index | hexa | coin
132 | 0x80000084 | [Factom Entry Credits](https://github.com/FactomProject)
133 | 0x80000085 | [Zcash](https://z.cash)
134 | 0x80000086 | [Lisk](https://lisk.io/)
135 | 0x80000087 |
135 | 0x80000087 | [Steem](http://steem.io)
136 | 0x80000088 | [ZCoin](https://zcoin.tech)
137 | 0x80000089 | [Rootstock](http://www.rsk.co/)
138 | 0x8000008A | [Giftblock](https://github.com/gyft/giftblock)
......
# SLIP-0048 : Deterministic Key Hierarchy for Graphene-based Networks
```
Number: SLIP-0048
Title: Deterministic key hierarchy for Graphene-based networks
Type: Informational
Status: Draft
Authors: Fabian Schuh <Fabian@chainsquad.com>
Created: 2016-10-18
```
## Abstract
This SLIP defines the logical hierarchy for deterministic wallets using Graphene technology.
It extends [BIP-0044](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) known from Bitcoin which should be read and understood before going into the details of this specification.
## Motivation
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.
## Account Roles on Graphene
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.
## Deterministic Key Hierarchy
m / purpose' / network' / account-index' / role' / 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
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.
### Network
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.
### Account-Index
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.
### Role
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.
## Account discovery
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.
### Key-Index
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
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.
## Account Setup Procedure
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:
1. The user requests an unused public key from the master (seed) node according to the specifications
2. The obtained public key is **added** to the existing account's **owner** role (full-weight)
3. 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
## Registered networks
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
## Examples
Network | Account-index | Role | Key-Index | Path
-----------|----------------|---------------|-----------|----------------------------
Steem | first | active | first | m / 48' / 0' / 0' / 1' / 0'
BitShares | forth | owner | forth | m / 48' / 1' / 3' / 0' / 3'
## References
- [BIP-0044: Multi-Account Hierarchy for Deterministic Wallets](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki)
#!/usr/bin/env python2
import binascii
import hashlib
import hmac
import struct
import ecdsa
from mnemonic import mnemonic
privdev = 0x80000000
def int_to_string(x, pad):
result = ['\x00'] * pad
while x > 0:
pad -= 1
ordinal = x & 0xFF
result[pad] = (chr(ordinal))
x >>= 8
return ''.join(result)
def string_to_int(s):
result = 0
for c in s:
if not isinstance(c, int):
c = ord(c)
result = (result << 8) + c
return result
def seed2hdnode(seed, modifier, curve):
while True:
h = hmac.new(modifier, seed, hashlib.sha512).digest()
key, chaincode = h[:32], h[32:]
a = string_to_int(key)
if (a < curve.order and a != 0):
break
seed = h
return (key, chaincode)
def fingerprint(publickey):
h = hashlib.new('ripemd160', hashlib.sha256(publickey).digest()).digest()
return h[:4]
def publickey(private_key, curve):
Q = string_to_int(private_key) * curve.generator
xstr = int_to_string(Q.x(), 32)
parity = Q.y() & 1
return chr(2 + parity) + xstr
def derive(parent_key, parent_chaincode, i, curve):
assert len(parent_key) == 32
assert len(parent_chaincode) == 32
k = parent_chaincode
if ((i & privdev) != 0):
key = '\x00' + parent_key
else:
key = publickey(parent_key, curve)
d = key + struct.pack('>L', i)
while True:
h = hmac.new(k, d, hashlib.sha512).digest()
key, chaincode = h[:32], h[32:]
a = string_to_int(key)
key = (a + string_to_int(parent_key)) % curve.order
if (a < curve.order and key != 0):
key = int_to_string(key, 32)
break
d = '\x01' + h[32:] + struct.pack('>L', i)
return (key, chaincode)
def get_curve_info(curvename):
return (ecdsa.curves.SECP256k1, 'Bitcoin seed')
def show_testvector(name, curvename, seedhex, derivationpath):
curve, seedmodifier = get_curve_info(curvename)
master_seed = binascii.unhexlify(seedhex)
k, c = seed2hdnode(master_seed, seedmodifier, curve)
p = publickey(k, curve)
fpr = '\x00\x00\x00\x00'
path = 'm'
print("### " + name + " for " + curvename)
print("Seed (hex): " + seedhex)
print('* Chain ' + path)
print(' * fpr: ' + binascii.hexlify(fpr))
print(' * chain: ' + binascii.hexlify(c))
print(' * prv: ' + binascii.hexlify(k))
print(' * pub: ' + binascii.hexlify(p))
depth = 0
for i in derivationpath:
fpr = fingerprint(p)
depth = depth + 1
path = path + "/" + str(i & (privdev - 1))
if ((i & privdev) != 0):
path = path + "<sub>H</sub>"
k, c = derive(k, c, i, curve)
p = publickey(k, curve)
print('* Chain ' + path)
print(' * fpr: ' + binascii.hexlify(fpr))
print(' * chain: ' + binascii.hexlify(c))
print(' * prv: ' + binascii.hexlify(k))
print(' * pub: ' + binascii.hexlify(p))
print
m = mnemonic.Mnemonic("english")
show_testvector("Test vector 1",
'secp256k1',
binascii.hexlify(m.to_seed("hello")),
[
privdev + 48,
privdev + 0,
privdev + 0,
privdev + 1,
privdev + 0,
])
show_testvector("Test vector 2",
'secp256k1',
binascii.hexlify(m.to_seed("hello")),
[
privdev + 48,
privdev + 1,
privdev + 3,
privdev + 0,
privdev + 3,
])
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment