Baking Account

author: Chiachi, YunYan, GA date: 2021/03/18

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

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

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

  • keychain
  • delayed update
  • new operation: update_keychain
  • add PVSS key
Select a repo