owned this note
owned this note
Published
Linked with GitHub
---
tags: draft, tezos
---
# Baking Account
author: Chiachi, YunYan, GA
date: 2021/03/18
[TOC]
## Developing branch
https://gitlab.com/marigold/tezos/-/tree/marigold@bakingaccount
## Summary
This draft states only the very first step of the Baking Account. Further Baking Account features can be built upon this. The goal is to allow users to register public keys as master key and spending key.
- master key: can perform all kind of operations
- spending key: can only perform the transaction
The idea is as follows:
- creating keychain via a new operation, `update_keychain`, which requires revealed key/keyhash.
- Once keychain is applied and created, the keyhash can't be computed from a public key and vice versa.
- previously used keys are not allowed to be set to a new master key on the same keychain.
- the new updated master keys will be delayed (+8 cycles) for propagating.
- current master/spending key is not allowed to be set to new master/spending key
## Implementation
### Keychain
We define `keychain` as
```ocaml
type keychain = {
master_key : key;
spending_key : key;
next_key : (key * cycle) option;
forsaken_key : key list;
}
```
Where, the `master key` is just like a *manager key* and has all manager rights; and, the `spending key` can only do the transfer.
Whenever a *master key updating* is requested, the new key will be stored in the `next key`, along with a corresponding `cycle number` where this updating will happen. The replaced key will be stored in `forsaken_key` as a history. Any forsaken key cannot be used as a master key again.
#### Parameter for delayed cycles
The number of cycles for the delayed update is defined as a parameter: `master_key_delay_cycles`. Currently, it's fixed to `8` cycles.
### Breaking the key-keyhash invariant
There is an invariant that `public key` and `public key hash` are tied to each other so we can use any of them to indicate an account. The keychain, however, will break this presumption.
So it's important to make sure no module in protocol will use a `public key` for identifying an account. As an example, the owner of rolls was changed to a `public key hash`.
#### Impact
After separating key-keyhash and running testing for each ops, it boils down to:
- ops are impacted by this change:
- endorsement
- double endorsement evidence
- double baking evidence
- delegation
- proposals
- ballot
- ops are non-impacted:
- reveal
- transaction
- origination
- ops are irrelevant to this change (key/keyhash/signature).
- seed nonce revelation
- activate account
### update_keychain (new operations)
The `update_keychain` op is defined as
```ocaml
type 'kind operation += {
| Update_keychain : {
master_key : Signature.Public_key.t option;
spending_key: Signature.Public_key.t option;
}
-> Kind.update_keychain manager_operation
```
If the given keyhash has been converted to a keychain, the operation will update master_key or spending_key. If not, the applied rules for updating a keychain are as follows:
- Provide a new **master key** only,
→ it will be used for updating the both *master key* as well as *spending key*
- Provide a new **spending key** only,
→ it will be used for updating only *spending key*
- Provide both of **master key** and **spending key**,
→ update keychain with given new keys separately
- Provide **no key**,
→ the *revealed public key* will be used for both keys.
#### Pseudocode
Basic notations:
- `K` : represent a public key in Tezos
- `KH` : represent a key hash, i.e. tz{1,2,...} account
- `Km` : represent master key
- `Ks` : represent spending key
- `Kn` : represent the *next* consensus key waiting to be updated
- `a|b` : means `a` or `b`
- `{key, value}` : a key-value record
- `hash` : a function for computing `KH` from a given `K`
##### Create
- `init` : given `K` `Ks`, `Km` and `KH = hash(K)`, if `KH` doesn't exist in storage, `init` will create a key-value record, `{KH, (Km, Ks)}`, into storage
```
init(K, Km, Ks) ->
let KH = hash(K)
if (KH exists in storage)
reject
else
// save record
{KHc, (Km, Ks)}
```
##### Update
```
update (Km'|Ks', Km, KH) ->
if ( Km is the master key of KH
& Km' <> km
& km' not any old master keys of the same KH )
case
updating spending key ->
{KH, (Km, Ks')}
updating master key ->
{KH, (Km, Ks, Kn = Km')}
// the Kc will be replaced by Kc' after 8 cycles
else
reject
```
##### Delete
No implementation.
```
delete (KH, Km) ->
delete {KH, (Km, Ks)} from stoarge
```
##### Read
No implementation.
```
print_KH(KHi) -> { KHi, (Kmi, Ksi) }
```
#### Execute operation
```
perform_operation(KH, Km|Ks, op) ->
case
KH maps to km|ks ->
if km|ks has correct op permission && signature checked
then exec operation
else reject
KH maps no keys ->
if signature checked
then exec operation
else reject
otherwise -> reject
```
## Test
This change was tested in three perspectives:
- the behavior of new operation: `update_keychain`
- the behavior of all other affected operations
- storage accessing
## TODO
- [x] keychain
- [x] delayed update
- [x] new operation: `update_keychain`
- [ ] add PVSS key