###### tags: `LéCøre` # Baking accounts spec ## Protocol ### Contracts Currently, there are 2 types of accounts, implicit and originated. Baker accounts are implicit accounts that delegate to themselves. For baker accounts, we propose a new contract type be added. Baker account is represented by a baker hash with the prefix ``"SG1"``. This has the advantage of distinguishing between implicit and baker contracts to allow us to associate baker-specific state only with the baker contracts. The baker account's hash handle can be deterministically calculated from a public key hash. This conversion is to be used during the migration and to preserve some compability with the previous protocol version. See the [#Proposed-change-for-preserving-compatibility](#Proposed-change-for-preserving-compatibility). Baker accounts are backed by a generic multisig Michelson script. More in [#Michelson](#Michelson) section. ### Storage New baker specific storages are added: ```ocaml module Consensus_key : Indexed_data_snapshotable_storage with type key = Baker_hash.t and type snapshot = Cycle_repr.t and type value = Signature.Public_key.t module Pvss_key : Indexed_data_storage with type key = Baker_hash.t and type value = Pvss_secp256k1.Public_key.t ``` Baker consensus key is a public key that's used for signing blocks and endorsements. Baker consensus key is stored in snapshot-able storage. A consensus key can be updated at any point. We snapshot the latest value of the key on cycle end for cycle `Constants.preserved_cycles + 2` in future. A consensus key value is read from a snapshot for the current cycle. A key change is therefore applied after `Constants.preserved_cycles + 1` cycles, on start of a next cycle. Bakers will be able to set their PVSS key, but the keys are not being used at the moment. ### Operations A new manager operations is added `Baker_registration` with the fields: ```ocaml { credit : Tez_repr.tez; consensus_key : Signature.Public_key.t; threshold : int; owner_keys : Signature.Public_key.t list; } ``` The `threshold` and `owner_keys` are used to initialize the script's storage (more in [#Michelson](#Michelson)). The baker registration will burn origination cost and the successful application will originate a new baker account. #### Baker operations A new type `_ Operation_repr.baker_operation` is added, which are async operations and are only allowed to be internal (that is, they can only be applied from a script). Baker operation kinds: - `Proposals` (changed from `manager_operation`) - `Ballot` (changed from `manager_operation`) - `Set_baker_active` - `Set_baker_consensus_key` - `Set_baker_pvss_key` ### Michelson While originated contracts script are typechecked to be of type: ``` lambda (pair 'arg 'global) -> (pair (list operation) 'global) ``` the type of baker script is extended with baker operations: ``` lambda (pair 'arg 'global) -> (pair (pair (list operation) (list baker_operation) 'global)) ``` This prevents originated contracts from being able to return baker operations, while allowing them to call baker scripts as a proxy. The baker accounts have operator accounts which are generic multisig contracts (https://gitlab.com/nomadic-labs/mi-cho-coq/-/blob/5b40d03f6211c1a82b86c91e974e8add2590e724/src/contracts/arthur/generic_multisig.tz). This has been modified to match the type given above, so that the generic action is of type `(lambda unit (pair (list operation) (list baker_operation)))`. Generic multisig's generic action allows further extensibility of actions that can be performed via baker scripts. The script has two entrypoints (excluding `%root`): - `%default` which can receive tokens - `%main` which is either to perform - the generic action or - change of threshold and owner keys This script contains storage with `%threshold` and a list of `%owner` keys (one or more public keys) that are able to invoke this script's actions on the ``%main`` entrypoint with at least `n` signatures, where `n` >= `threshold`. - new types being added - `baker_operation` represents `_ Operation_repr.baker_operation` - `baker_hash` represents `Baker_hash.t`, comparable - `pvss_key` represents `Pvss_secp256k1.Public_key.t`, comparable - new instructions - `SUBMIT_PROPOSALS` - `SUBMIT_BALLOT` - `SET_BAKER_ACTIVE` - `SET_BAKER_CONSENSUS_KEY` - `SET_BAKER_PVSS_KEY` - `SET_DELEGATE` the type is changed from: - `:: option key_hash : 'S -> operation : 'S` to - `:: option baker_hash : 'S -> operation : 'S` - `CREATE_CONTRACT` the delegate argument type is changed from: - `:: key_hash : option key_hash : ...` to - `:: key_hash : option baker_hash : ...` ### Protocol parameters A new field `bootstrap_bakers` is added that can be used to configure bootstrap bakers for a network with a given baker hash , balance, and consensus key that is also used as the owner key (with threshold = 1). ### RPC A new endpoint to get consensus key (the currently active snapshotted value): `../<block_id>/context/delegates/<baker_hash>/consensus_key` (GET) A baker hash for a given pkh can be found at `../<block_id>/helpers/baker_hash_of_pkh/<pkh>` (GET) An RPC for looking up an baker hash from a consensus key. ## Migration All (valid) registered bakers and their associated storages are migrated to their new SG1 baker accounts from public key hashes. The original public key hashes are used as baker's consensus keys and owner keys. We set the thresholds to 1, so that the scripts are effectively single-sig. We move the balance of the original implicit baker accounts to the new baker accounts and if a baker has at least one roll, we credit them with 1 tez, to allow them to pay for some fees for invoking baker script actions using their original implicit accounts. ## Client The client for performing baker actions supports both single-sig (special case of multisigs with a single key) and multi-sig. For the single-sig actions, the client can lookup the owner key to sign the operations with. For the multisig actions, we offer triples of actions "prepare" (to display the threshold, public keys, and byte sequence to sign), "sign" (to produce signatures) and "perform" (to execute command with the given signatures). # Proposed change for preserving compatibility The spec given above simply changes the type of `SET_DELEGATE` and `CREATE_CONTRACT` without taking compatibility into consideration. We want to preserve compatibility of the already deployed contracts for the original `SET_DELEGATE` and `CREATE_CONTRACT` instructions and also the original `Delegation` operations, for 3rd party services that forge this operation manually to keep working without any breakage. - to deal with deployed scripts: - introduce `Set_delegate` and `Create_contract` intermediate representations (in `Script_typed_ir`) that use Baker_hash - rename the original instructions to `Set_delegate_legacy` and `Create_contract_legacy` - we make the original versions legacy, so that only already deployed contracts can use them, for new contracts they will be deprecated - we need to store the consensus key to baker hash mapping for look-ups, we can no longer compute these procedurally, because baker's consensus key may change while their baker hash will stay the same - we have to enforce 1-to-1 mapping from consensus key to baker hash, so we'd need to enforce consensus key to be unique - to enforce consensus key to be unique, we introduce storage for two sets such as `Baker.Pending_consensus_keys` and `Baker.Active_consensus_keys`. - each baker will have an entry in `Baker.Pending_consensus_keys` with their current `Storage.Baker.Consensus_key` value and `Baker.Active_consensus_keys` with their active (snapshotted) `Storage.Baker.Consensus_key.Snapshot` value - when registering a new baker or changing consensus key, both storaged would be checked for collision - because consensus key activation takes up to 7 cycles, it's possible that a baker can change their consensus key at each cycle, to have up to 7 intermediate consensus keys. To avoid having to track up to 7+ keys for each baker, we make a new rule that if a consensus key is changed before it's last change is activated, then the last change will be discarded (i.e. it will never become active) - example: - cycle 0: baker starts with an active consensus key A (`Baker.Pending_consensus_keys` contains A and `Baker.Active_consensus_keys` contains A) - cycle 1: baker sets consensus key to B (B is added to `Baker.Pending_consensus_keys`) - cycle 5: baker sets consensus key to C (B is deleted and C is added from/to `Baker.Pending_consensus_keys`), because B is not active yet, it's also deleted from the snapshots from the previous cycles, so that it will never be activated - cycle 9: key C becomes active (A is deleted and C is added to from/to `Baker.Active_consensus_keys`) - this also has the advantage that both the pending and active key sets are bounded with at most 1 and exactly 1 record per baker, respectively - 00(8?) migration paths for removing the deprecated instructions versions 1. rewriting contracts parameter to `SET_DELEGATE` and `CREATE_CONTRACT` calls from pkh to baker_hash - this is tricky, we can find all calls to `SET_DELEGATE` and `CREATE_CONTRACT`, but we also need to find where the delegate parameter comes from and rewrite it - when the delegate parameter is also used elsewhere as PKH, this may not be possible to automate, so we might need to convert such scripts manually 2. convert PHKs to Baker_hash in the interpreter, or add an internal compatibility instruction `BAKER_HASH_OF_PKH` that invoked in migrated contracts before the call to `SET_DELEGATE`/`CREATE_CONTRACT` - if the pkh is used elsewhere, this wouldn't affect it as we only change the parameter before the calls to `SET_DELEGATE`/`CREATE_CONTRACT` - the downside is that with this, we'd need to keep the consensus key <-> baker hash mapping forever 3. drop legacy if not used on chain - https://gitlab.com/nomadic-labs/smart-contracts - chances are, the original `SET_DELEGATE` and `CREATE_CONTRACT` won't be used before they're deprecated # Proposed change of baker operations To achieve full compatibility with the Carthage baker operations, we want to preserve the current baker operations (transfer, submit proposal, submit ballot) with their encoding that continue to be signed by the baker's public key, which becomes their consensus key. To allow for this, we can utilise the 1-to-1 mapping between consensus key and the baker hash from the [#Proposed-change-for-preserving-compatibility](#Proposed-change-for-preserving-compatibility), extended so that transactions submitted from or to an active consensus key are mapped to their baker contract, so that any transaction fee, counter or balance checks and updates are applied on the baker contract. This prevents allocating implicit accounts on active consensus keys. Furthermore, implicit accounts of the consensus key are enforced to never be allocated on chain: - when registering a new baker account or changing baker's consensus key, the provided consensus key is required to be unallocated - trying to send transaction from or to a pending consensus key fails - set delegate to a pending consensus key also fails