Pas moins de quatre bugs différents ont causés des incohérences dans le storage de la ĞDev, je propose de les corriger via des batchs manuels, l’objet de ce sujet est de détailler ces batchs qui vont être soumis au vote.
D’abord deux notions importantes générales à substrate que vous devez avoir en tête :
1. Sudo n’est pas Root, et Root n’est pas Sudo
Sudo est optionnel, mais Root est obligatoire et nécessairement présent dans toute blockchain substrate.
Sudo est juste un moyen, parmi une infinité imaginable, d’agir en tant que Root. Sudo c’est juste une pallet qui permet à un compte défini (la sudo key) d’exécuter un call en tant que Root.
En l’absence de Sudo, il faut que soit défini au moins un autre moyen d’agir en tant que Root, ce “moyen” étant généralement un processus de gouvernance on-chain.
Mais dans tous les cas, Root est toujours présent et peut toujours tout faire, la seule chose qu’on peut choisir, c’est par quel processus la communauté peut agir en tant que Root.
Root peut modifier n’importe qu’elle entrée dans le storage, via les call system.setStorage
et system.killStorage
.
2. Il y a plusieurs moyens différents de corriger le storage, plus ou moins adaptés selon les cas
J’identifie au moins quatre moyens différents, et dans mon travail pour moonbeam on a déjà utilisé les trois premiers :
- Exécuter un ou plusieurs batchs manuels en tant que Root, c’est ce que je propose dans ce sujet.
- Créer un ou plusieurs call de hotfix dans le storage, qui ne peuvent généralement être appelés que par root. Cette méthode est plus lourde, puisqu’elle nécessite de coder et tester un ou des nouveau(x) extrinsic(s) temporaires, et de déployer un nouveau runtime avec ces extrinsics, mais elle permet de gérer des incohérences plus complexes, que les batchs manuels seuls ne peuvent pas gérer.
- Écrire une migration dans un nouveau runtime, et déployer ce nouveau runtime, ce qui exécutera la migration : c’est plus délicat, à n’utiliser que si les méthodes précédentes ne sont pas utilisables.
- Passer la blockchain en lecture seule pour les utilisateurs, et effectuer plusieurs fois l’une des trois méthodes précédentes le temps de corriger ce qui doit l’être, puis repasser la blockchain en mode “normal”. Chez moonbeam on a testé le passage en lecture seule pour s’assurer qu’on pourrait le faire si on en avait besoin un jour, mais on n’en a jamais eu besoin.
On préférera toujours 1. si c’est possible, sinon 2., sinon 3.
Dans le cas présent, la méthode des batchs manuels est parfaitement adaptée au bug #57, elle est en revanche limite pour les bugs #46 #60 et #62. Ça va fonctionner uniquement parce que nous avons encore très peu de données et très peu d’actions utilisateur, donc l’état change très lentement, mais en prod sous forte charge on aurait été obligés de créer un extrinsic de hotfix, puis, après l’upgrade d’utiliser cet extrinsic de hotfix dans un (ou plusieurs) batch manuel.
Détails du 1ᵉʳ batch soumis au vote
Je viens de proposer en blockchain un 1ᵉʳ batch, le hash du proposal est 0xd9667b15ff7ffe6e364c0aff31e117e4dfef81cc37b41727f7c18ef9e9962287
Ce 1er batch ne peut pas tout corriger, car tout les calls dans le batch doivent réussir, et pour tout corriger il aurait fallu proposer certains call qui pourront échouer selon l’évolution de l’état d’ici que le batch soit exécuté.
Ce 1er batch vide les huit comptes invalides, en effectuant un transfer_all
en leur nom vers la trésorerie. Il y a malheureusement deux cas ou cela ne va pas supprimer le compte :
- Si le compte à une identité associée,
transfer_all
va laisser 2 ĞD, et le compte ne sera donc pas supprimé. C’est le cas pour le compte5G27hLdxFgtaZtwFDhs6iMp5fBgwCaoEXaKFJzk16wV6MhnG
, c’est pourquoi le batch contient un call pour supprimer l’identité associée (index 23). Mais d’autres comptes peuvent créer une identité entre-temps, et on ne peut pas remove leur identité préventivement, car on ne sait pas quel IdtyIndex leur sera attribué. - Si le compte a déjà un solde de zéro, c’est le cas pour deux comptes sur les huit (du moins au moment où j’ai conçu le batch). Pour contourner ce point, je vais verser 2 ĞD sur ces deux comptes, mais ils peuvent être re-vidés entre-temps (et les autres comptes aussi).
Pour voir le détail du batch, dans la vue “Chain State”: smithsCollective.proposalOf()
puis indiquez en paramètre le hash du proposal, vous obtenez ceci :
Je vous recommande de copier-coller le détail du batch dans un éditeur de texte avancé style vscode :
On voit alors plus clairement que le call proposé est upgradeOrigin.dispatchAsRoot(utility.batch(calls))
.
Le 1ᵉʳ call du batch :
C’est un utility.dispatchAs(origin, call)
avec pour origin l’un des comptes invalide, et pour call un transfer_all
vers la trésorerie.
Tout les call du batch sont du même style sauf deux, celui qui supprime l’identité 23
et celui qui supprime une entrée dans UdAccountsStorage
. Regardons ce dernier :
{
args: {
keys: [
0xd97d5a9fdd130e4394796760df433ce0a3309dc5910075f2fe7bcc37353af7ea83d0d534df964266fca6837ccfc60172b8f7ab8cd50e22901a2703a238897bb2cd9cb1b13820ae4971833023b9f33534
]
}
method: killStorage
section: system
}
La clé correspond à twox128("UdAccountsStorage") ++twox128("UdAccounts") ++ blake2_128(0x5GFEEx7kqvP4QEPCAXkALeYCG7m8DiA5LQ4YzW62j7FytQyg) ++ 0x5GFEEx7kqvP4QEPCAXkALeYCG7m8DiA5LQ4YzW62j7FytQyg
.
Vous pouvez la vérifier dans Polkadot{.js} (il vous faut ajouter la clé publique dans vos contacts) :
En bas à gauche, “encoded storage key” est la raw key complete. En bas à droite vous avez la décomposition. Pour module et method la fonction de hashage n’est pas précisée, car c’est toujours twox128 dans tous les cas.