|
| 1 | +# Migrating a Partner Chain to use Beefy |
| 2 | + |
| 3 | +## Introduction |
| 4 | + |
| 5 | +This document describes step by step how to extend an already running Partner Chain |
| 6 | +with Beefy. The migration path described maintains backward compatibility, avoids a |
| 7 | +chain reset and allows historical blocks to be imported by new nodes post-migration. |
| 8 | + |
| 9 | +## Warnings |
| 10 | + |
| 11 | +1. Remember that runtime upgrades are inherently risky and can brick a chain if done |
| 12 | +incorrectly. **Always test any runtime upgrade before applying it to a live chain.** This |
| 13 | +can be done using [try-runtime](https://github.com/paritytech/try-runtime-cli) for |
| 14 | +quickly verifying only the runtime and storage updates, or using a locally run chain, |
| 15 | +ideally one cloned from the target live chain. It is advised to do both. |
| 16 | +2. For security, Partner Chain committee selection requires that a committee is chosen |
| 17 | +and produces at least one block for each Partner Chain epoch. This means that a chain |
| 18 | +will **stall indefinitely** if at some point the valid candidate pool for some epoch is |
| 19 | +empty. |
| 20 | +3. Any on-chain updates to the authority selection inputs on Cardano (SPO registrations, |
| 21 | +D-Param and permissioned candidate list) **only become effective after two full Cardano |
| 22 | +epochs have passed**. If the updates are done in preparation for subsequent changes to the |
| 23 | +Partner Chain that would invalidate the previous on-chain values, at least this much time |
| 24 | +should be observed before applying these changes. |
| 25 | + |
| 26 | +## Context and considerations |
| 27 | + |
| 28 | +Beefy is a bridge-oriented protocol that provides state inclusion proofs for cross-chain |
| 29 | +communication. It is not a finality gadget by itself and builds upon existing finality |
| 30 | +mechanisms such as Grandpa. |
| 31 | + |
| 32 | +Node components of Beefy are capable of detecting the pallet in the runtime and will stay |
| 33 | +inactive until it is present and the Beefy genesis block is set. This means that nodes can |
| 34 | +be prepared to support Beefy before the migration and still support historical blocks as |
| 35 | +needed. |
| 36 | + |
| 37 | +Beefy introduces its own set of authority keys that are used by committee members to sign |
| 38 | +finality proofs each round. These keys are managed by `pallet-session` together with other |
| 39 | +session keys used by the chain. The main concern when adding Beefy to a running chain is |
| 40 | +migrating the existing key storages and ensuring that block producer's Beefy keys are set |
| 41 | +before Beefy starts operating. |
| 42 | + |
| 43 | +The Partner Chain toolkit has been prepared to support migrating to Beefy and similar |
| 44 | +functionalities that require introduction of new authority keys, with the only requirement |
| 45 | +being that the feature's implementation makes it possible to postpone its activation for |
| 46 | +some time after being added to the runtime. |
| 47 | + |
| 48 | +## Migration steps outline |
| 49 | + |
| 50 | +In short, the migration plan is as follows: |
| 51 | + |
| 52 | +0. Update candidate data to include Beefy keys by updating the permissioned candidate list |
| 53 | + and instructing SPOs to re-register themselves |
| 54 | +1. Add Beefy offchain services to the node code and upgrade nodes in the network, including |
| 55 | + Beefy keys in their keystores. |
| 56 | +2. Add Beefy to the runtime, together with all supporting pallets |
| 57 | +3. Extend the session keys type to include Beefy keys |
| 58 | +4. Schedule session keys type migration |
| 59 | +5. Perform runtime upgrade |
| 60 | +6. Wait for a new session, which will update authority keys |
| 61 | +7. Set new genesis block in Beefy to activate the pallet |
| 62 | + |
| 63 | + |
| 64 | +## Migration steps details |
| 65 | + |
| 66 | +### Updating candidate data |
| 67 | + |
| 68 | +When in |
| 69 | +the next steps the runtime is upgraded and Beefy keys are added to the session keys type, |
| 70 | +any candidate missing them in their candidate data on Cardano will be rejected by the commitee |
| 71 | +selection algorithm. |
| 72 | + |
| 73 | +To prepare for that, the Partner Chains toolkit allows candidate data stored on Cardano to include |
| 74 | +additional keys to those that are currently required. Those keys will become visible to the Partner |
| 75 | +Chain after two Cardano epochs have passed. |
| 76 | + |
| 77 | +In this preparatory step, both the governance authority of the Partner Chain and individual Cardano |
| 78 | +SPOs should update data on-chain to include Beefy keys. |
| 79 | + |
| 80 | +First, every SPO and permissioned node operator needs to generate their Beefy key and insert it into |
| 81 | +their keystore. The key can be generated using the command: |
| 82 | +```shell |
| 83 | +$ pc-node key generate --scheme ecdsa |
| 84 | +``` |
| 85 | +and the generated secret phrase can be inserted to the keystore using: |
| 86 | +```shell |
| 87 | +$ pc-node key insert --scheme ecdsa --key-type beef --suri "<secret phrase>" --keystore-path "<keystore path>" |
| 88 | +``` |
| 89 | + |
| 90 | +The governance authority should prepare the permissioned candidate list in CSV format in the following format: |
| 91 | +```csv |
| 92 | +<cross-chain skey hex>,aura:<aura skey hex>,gran:<grandpa skey hex>,beef:<beefy skey hex> |
| 93 | +<cross-chain skey hex>,aura:<aura skey hex>,gran:<grandpa skey hex>,beef:<beefy skey hex> |
| 94 | +... |
| 95 | +``` |
| 96 | +and put it on-chain using the following command: |
| 97 | +```shell |
| 98 | +$ pc-node smart-contracts upsert-permissioned-candidates |
| 99 | + --permissioned-candidates-file <permissioned candidate file> |
| 100 | + --payment-key-file <governance skey> |
| 101 | + --genesis-utxo <genesis utxo> |
| 102 | +``` |
| 103 | + |
| 104 | +The SPOs should re-register their node using the wizard (see `docs/user-guides/registered.md` for instructions) |
| 105 | +or by manually creating relevant signatures using: |
| 106 | +```shell |
| 107 | +$ pc-node registration-signatures \ |
| 108 | + --genesis-utxo <genesis utxo> \ |
| 109 | + --mainchain-signing-key <Cardano skey hex> \ |
| 110 | + --sidechain-signing-key <cross-chain skey hex> \ |
| 111 | + --registration-utxo <registration utxo> # UTXO spendable from the candidate's wallet |
| 112 | +``` |
| 113 | +and then submitting them on-chain using: |
| 114 | +```shell |
| 115 | +$ pc-node smart-contracts register \ |
| 116 | + --genesis-utxo <genesis utxo> \ |
| 117 | + --spo-public-key <Cardano pkey> \ |
| 118 | + --spo-signature <Cardano signature> \ |
| 119 | + --partner-chain-public-keys <cross-chain pkey>,aura:<aura pkey>,gran:<grandpa pkey>,beef:<beefy pkey> \ |
| 120 | + --sidechain-signature <cross-chain signature> \ |
| 121 | + --registration-utxo <registration utxo> \ # same as in previous command |
| 122 | + --payment-key-file <Cardano skey file> |
| 123 | +``` |
| 124 | + |
| 125 | +This process should be coordinated between the governance authority and the SPOs to allow enough time for everyone |
| 126 | +to go through the steps before the runtime upgrade. |
| 127 | + |
| 128 | +Note that the nodes in the network can be freely upgraded while the re-registration period is ongoing. |
| 129 | + |
| 130 | +### Upgrading network nodes |
| 131 | + |
| 132 | +This step can be performed during or even before the re-registrations. |
| 133 | + |
| 134 | +The node code of the Partner Chain should be updated to include Beefy services and then rolled out to the network. |
| 135 | + |
| 136 | +Adding Beefy components to the node is quite involved, so the reader is encouraged to reference the demo node |
| 137 | +implementation for an example of how to do it, most importantly `demo/node/src/service.rs` for |
| 138 | +offchain services and `demo/node/src/rpc.rs` for RPC services. |
| 139 | +Keep in mind that due to Substrate using strong typing where it's possible, some changes to the runtime code will |
| 140 | +be required at this point. |
| 141 | + |
| 142 | +After the node code has been updated, new node binary should be distributed to the node operators and an upgrade |
| 143 | +period should be coordinated with enough time for the majority to upgrade their nodes. |
| 144 | + |
| 145 | +### Adding Beefy to the runtime |
| 146 | + |
| 147 | +This step can be performed during re-registrations and together with the node code modifications in the previous step. |
| 148 | + |
| 149 | +The Beefy pallet should be added to the runtime and configured, together with other pallets that support its operation: |
| 150 | +- `pallet_beefy` |
| 151 | +- `pallet_mmr` |
| 152 | +- `pallet_beefy_mmr` |
| 153 | + |
| 154 | +Refer to the demo runtime at `demo/runtime/src` for an example of how to integrate these |
| 155 | +pallets to your runtime. |
| 156 | + |
| 157 | +### Adding Beefy keys to session keys type |
| 158 | + |
| 159 | +Adding Beefy to session keys requires just adding another field to the `impl_opaque_keys` macro. |
| 160 | +For a chain that uses Aura and Grandpa for consensus, it would look like this: |
| 161 | +```rust |
| 162 | +impl_opaque_keys! { |
| 163 | + #[derive(MaxEncodedLen, PartialOrd, Ord)] |
| 164 | + pub struct SessionKeys { |
| 165 | + pub aura: Aura, |
| 166 | + pub grandpa: Grandpa, |
| 167 | + pub beefy: Beefy, |
| 168 | + } |
| 169 | +} |
| 170 | +``` |
| 171 | + |
| 172 | +Keep in mind that modifying the session keys type means that from the next runtime upgrade, the added |
| 173 | +key type becomes mandatory and any candidate whose registration data doesn't include them will be |
| 174 | +considered invalid. |
| 175 | + |
| 176 | +### Scheduling session keys migration |
| 177 | + |
| 178 | +Any modification of the session keys type requires a storage migration that will update the values stored |
| 179 | +in `pallet-session-validator-management` and `pallet-session`. |
| 180 | + |
| 181 | +First, the original session keys type should be preserved under a different name. Eg: |
| 182 | + |
| 183 | +```rust |
| 184 | +impl_opaque_keys! { |
| 185 | + #[derive(MaxEncodedLen, PartialOrd, Ord)] |
| 186 | + pub struct LegacySessionKeys { |
| 187 | + pub aura: Aura, |
| 188 | + pub grandpa: Grandpa, |
| 189 | + } |
| 190 | +} |
| 191 | +``` |
| 192 | + |
| 193 | +Next, a conversion from the old type to the new type should be defined by implementing the `UpgradeAuthorityKeys` |
| 194 | +trait. When a new field is added to the type, a valid default value should be used, eg.: |
| 195 | + |
| 196 | +```rust |
| 197 | +impl UpgradeAuthorityKeys<SessionKeys> for LegacySessionKeys { |
| 198 | + fn upgrade(self) -> SessionKeys { |
| 199 | + SessionKeys { |
| 200 | + aura: self.aura, |
| 201 | + grandpa: self.grandpa, |
| 202 | + beefy: ecdsa::Public::default().into(), |
| 203 | + } |
| 204 | + } |
| 205 | +} |
| 206 | +``` |
| 207 | + |
| 208 | +Finally, a key migration should be added to the runtime's migration list, using `AuthorityKeysMigration` |
| 209 | +provided by `pallet-session-validator-management`, eg: |
| 210 | + |
| 211 | +```rust |
| 212 | +pub type Migrations = ( |
| 213 | + AuthorityKeysMigration<Runtime, opaque::LegacySessionKeys, 0, 1>, |
| 214 | +); |
| 215 | +``` |
| 216 | + |
| 217 | +The migration is versioned to prevent re-running it in case it is not removed from the runtime before |
| 218 | +subsequent upgrades. The current version of the session keys used by the runtime is tracked by |
| 219 | +`pallet-session-validator-management` and updated as part of the migration. In the example, the migration |
| 220 | +is defined as going from session keys version 0 to version 1, which will be the case for chains that |
| 221 | +never migrated their keys using this mechanism before. |
| 222 | + |
| 223 | +### Upgrading the runtime |
| 224 | + |
| 225 | +**Important:** This step should only be performed once at least one committee member candidate's registration |
| 226 | +data containing Beefy keys becomes available for observation. This happens after *two full Cardano epochs* |
| 227 | +have passed after it was updated. |
| 228 | + |
| 229 | +After the runtime code has been updated, including the session keys, the on-chain runtime should be updated |
| 230 | +by calling `system/setCode` extrinsic through the governance mechanism used by the Partner Chain, eg. `sudo` |
| 231 | +or `democracy` pallet. |
| 232 | + |
| 233 | +### Wait for session change |
| 234 | + |
| 235 | +Note that since a default value is used by the storage migration for the newly added keys, they will not be |
| 236 | +usable right away after the runtime upgrade. This fact makes it crucial that Beefy (or any similar feature |
| 237 | +being added) must not become active immediately after runtime upgrade. Luckyly, in this case Beefy pallet |
| 238 | +starts in a dormat state until it is activated. |
| 239 | + |
| 240 | +Real Beefy keys of the committee members will be sourced from Cardano and registered in `pallet-session` |
| 241 | +after one or two session rotations after the runtime upgrade, depending on the exact timing. The keys |
| 242 | +should still be manually verified to have been updated by reading the `currentCommittee` storage of the |
| 243 | +session management pallet. |
| 244 | + |
| 245 | +### Activating Beefy |
| 246 | + |
| 247 | +Once the sessions rotate and the current committee has its Beefy keys correctly updated, the Beefy pallet |
| 248 | +can be activated by invoking the `beefy/setNewGenesis` extrinsic using the governance method employed |
| 249 | +by the Partner Chain in question. This extrinsic accepts an offset of blocks after which the Beefy pallet |
| 250 | +will become active. Once it happens, the block producers will start participating in Beefy consensus rounds. |
| 251 | +At this point, it can be verified that Beefy is working correctly by checking logs for lines reporting |
| 252 | +successful rounds, like the following one: |
| 253 | +``` |
| 254 | +2025-11-27 12:42:02 🥩 Concluded mandatory round #1 |
| 255 | +``` |
0 commit comments