Migrate identity (identity.changeOwnerKey())

Tous ce workflow jusqu’au transfert des coins sur un de nos portefeuilles fonctionne maintenant.

J’en suis donc à la migration de l’identité.

@elois peux-tu m’éclairer sur ce qu’est newKeySig: CommonRuntimeEntitiesNewOwnerKeySignature de l’extrinsic identity.changeOwnerKey(newKey, newKeySig) ?

Je dois signer un document avec la nouvelle clé et l’insérer ici ?

Tu doit signer un message avec la nouvelle clé et insérer uniquement la signature, le message étant connu par la blockchain, ça ne servirait à rien de le fournir. voici le message à signer:

"icok" ++ GENESIS_HASH ++ IDTY_INDEX ++ OLD_OWNER_KEY

Tout doit être encodé et concaténé en binaire.

  • "icok" c’est juste un préfixe constant pour s’assurer qu’on signe bien pour cette opération et pas une autre, icok correspondant aux initiales de identity change owner key. Encodé en ASCII ou utf8 c’est pareil, en hexa ça donne 0x69636F6B.
  • GENESIS_HASH pour empêcher le rejeu sur un autre réseau.
  • IDTY_INDEX pour identifier l’identité pour laquelle on veut changer de clé publique. Doit être encodé en little endian non-signé sur 4 octets.
  • OLD_OWNER_KEY l’ancienne clé publique, pour empêcher le rejeu sur le même réseau. Ça implique qu’il ne faut jamais revenir à une ancienne clé.
1 « J'aime »

Ah ok en effet je pouvais pas le deviner lol

Ca veut dire que je ne dois pas essayer de récupérer GENESIS_HASH depuis le storage, mais que je dois inscrire en dur tous les GENESIS_HASH par monnaie ?

Non, pourquoi voudrais-tu prouver seulement le genesis hash et pas le reste ? Soit tu utilise un light client, qui lui aura le genesis hash en dur, ainsi que toutes les chain spec, et toute la procédure complexe pour prouver n’importe quelle donnée, soit tu n’utilise pas de light client et tu es alors de toute façon obligé de faire confiance au nœud RPC que tu requête.

Traduction: récupère le genesis hash depuis le storage.

1 « J'aime »

Bon j’ai fait 2 essaies, et je ne comprends pas pourquoi le batchAll fonctionne pour vider le compte, mais pas pour l’extrinsic d’avant qui est changeOwnerKey().

Il s’agit de la migration de l’identité zombie ayant pour adresse 5GAT6CJW8yVKwUuQc7sM5Kk9GZVTpbZYk9PfjNXtvnNgAJZ1 vers la nouvelle adresse 5D2HHRj6zCEL9iY3CBpKy1mBMtpXUJCFDNvevBqVQAWdzqSS.

Voici la sortie brut de ce batchAll qui contient 3 extrinsics:

[api.tx.universalDividend.claimUds(), api.tx.identity.changeOwnerKey("5D2HHRj6zCEL9iY3CBpKy1mBMtpXUJCFDNvevBqVQAWdzqSS", "icok0x07c112ff6ab9d7d0d531ebe59f98b35318b2813b1655577380819d38d6182d9905GAT6CJW8yVKwUuQc7sM5Kk9GZVTpbZYk9PfjNXtvnNgAJZ1"), api.tx.balances.transferAll(false)]

Seul le transfertAll (et le claimUd aussi je présume, dans le vide) a fonctionné sur mes 2 essaies.

Je croyais que batchAll devait échouer pour tous les call si l’un des call échoue ?

Alors déjà ça c’est pas bon, j’ai dit qu’il fallait fournir uniquement la signature, pas le message, message qui est en plus invalide puisque le préfixe icok n’est pas encodé.

Ensuite, je ne vois aucun batchAll sur la blockchain gdev pour aucune de ces 2 adresses, peut tu m’indiquer le numéro de bloc ?

Le batchAll revert tous les changements si l’un les subcall du batch échoue, donc le transfert n’a pas été exécuté, seuls les frais ont été prélevés.

Je viens de refaire exactement le même call, éxecuté au bloc 139061.

Je comprends rien dans mon code je le vois bien passer par le batch pourtant, y’a un truc que je pige pas ça doit être mon code…

(merci @HugoTrentesaux de réalimenter le compte zombie au fur et à mesure pour que je puisse me le revider ^^)

Sisi le transfert se fait bien, mais en effet je en vois pas de batch en blockchain…

1 « J'aime »

Ce n’est pas un batch, c’est un balances.transfertAll.

Parce que ce n’est pas un batch…

1 « J'aime »

ok donc depuis hier j’ai compris pourquoi mon batch ne s’exécutait pas, maintenant il s’exécute, mais échoue à chaque fois (c’est mieux…)

Du coup pour mieux tester, j’ai viré le batch et je n’appel que l’extrinsic changeOwnerKey()

Je ne fournis que la signature au call comme tu l’a indiqué. La seul variable pour laquelle je ne suis pas sûr est le OLD_OWNER_KEY:

J’ai essayé de passer l’adresse ss58, la pubkey en hexa, la pubkey en base58, rien de tout cela fonctionne.
J’ai aussi essayé chacune de ces possibilités avec « icok » en hexa, pareil.

Voici les sorties de mon dernier essai:

messageToSign: icok0x07c112ff6ab9d7d0d531ebe59f98b35318b2813b1655577380819d38d6182d9921DCovzCEnQm9GUWe6mr8u42JR1JAuoj3HbQUGdCkfTzSr
newKeySig: 0xf049d219dfac20a522849f9acb61a51cea4dc8e0d9fb9f978604bdf4fca7e25ad01c1fa0f55ed065706e2de045a40c3b7319c4a3edb6221dc575e0b942088e81

J’ai pour erreur: Exception: identity.InvalidNewOwnerKeySig

Ici c’est la clé publique en base58 que j’ai inséré.
J’ai vérifié que la signature du message est bien correct via polkadot.js:

Je suis donc sûr que le soucis vient de mon message à signer.

Voici mon bout de code isolé qui formate ce message et prépare l’extrinsic, si ça peut aider à debugguer:

    final genesisHash = await getGenesisHash();
    final idtyIndex = await getIdentityIndexOf(fromAddress);
    final oldPubkey = await addressToPubkey(fromAddress);
    final messageToSign = 'icok$genesisHash$idtyIndex$oldPubkey';
    final newKeySig =
        await signMessage(messageToSign, destAddress, destPassword);

    txInfo = TxInfoData(
      'identity',
      'changeOwnerKey',
      sender,
    );

    txOptions = [destAddress, newKeySig];

Désolé pour toutes ces questions, j’essaie vraiment de comprendre sans te déranger…

C’est normal, il faut concaténer la pubkey en binaire comme le dit elois.

Il faut apprendre à fonctionner avec des Vector{UInt8}, là tu fais une interpolation de chaîne, donc comme dit elois

Voici quelques exemples :

julia> Int.(['i', 'c', 'o', 'k'])
4-element Vector{Int64}:
 105
  99
 111
 107

julia> UInt8.(['i', 'c', 'o', 'k'])
4-element Vector{UInt8}:
 0x69
 0x63
 0x6f
 0x6b
2 « J'aime »

ok autant pour moi j’ai du mal lire.

J’ai toujours le même problème, cette fois si en utilisant des Uint8List directement:

prefix: [105, 99, 111, 107]
genesisHash: [48, 55, 99, 49, 49, 50, 102, 102, 54, 97, 98, 57, 100, 55, 100, 48, 100, 53, 51, 49, 101, 98, 101, 53, 57, 102, 57, 56, 98, 51, 53, 51, 49, 56, 98, 50, 56, 49, 51, 98, 49, 54, 53, 53, 53, 55, 55, 51, 56, 48, 56, 49, 57, 100, 51, 56, 100, 54, 49, 56, 50, 100, 57, 57]
idtyIndex: [21, 0, 0, 0]
oldPubkey: [181, 82, 178, 99, 198, 4, 156, 190, 78, 35, 102, 137, 255, 7, 162, 31, 16, 79, 255, 132, 130, 237, 230, 222, 176, 88, 245, 217, 237, 78, 196, 239]

messageToSign: [105, 99, 111, 107, 48, 55, 99, 49, 49, 50, 102, 102, 54, 97, 98, 57, 100, 55, 100, 48, 100, 53, 51, 49, 101, 98, 101, 53, 57, 102, 57, 56, 98, 51, 53, 51, 49, 56, 98, 50, 56, 49, 51, 98, 49, 54, 53, 53, 53, 55, 55, 51, 56, 48, 56, 49, 57, 100, 51, 56, 100, 54, 49, 56, 50, 100, 57, 57, 21, 0, 0, 0, 181, 82, 178, 99, 198, 4, 156, 190, 78, 35, 102, 137, 255, 7, 162, 31, 16, 79, 255, 132, 130, 237, 230, 222, 176, 88, 245, 217, 237, 78, 196, 239]
newKeySig: 0xf419a2e7f0561589d1cf28cc7e0fcc6b486d75169fb891d5fce17295ed90d0765d20dd734d6480fc528b157eb68a9f5e5921a1f9fe5abb34e5de48327a31da8c
Exception: identity.InvalidNewOwnerKeySig

Arf, j’aurais dû préciser SCALE encodé.

Si tu avais l’habitude du binaire tu m’aurais signalé que je n’ai pas précisé comment encoder le nombre entier IdtyIndex, car il n’y a pas une façon standard d’encoder un entier en binaire, il faut nécessairement préciser.

Dans le cas présent, c’est du little endian fixed size sur 4 octets, ça donne donc [21, 0, 0, 0].

1 « J'aime »

arf désolé tu as répondu pendant que j’éditais mpon message avec exactement ce correctif, mais l’erreur persiste…


J’espère que le soucis ne viens pas de ma fonction de signature qui est un peu tricky à cause du binding JS:

Future<String> signMessage(
      Uint8List message, String address, String password) async {
    final params = SignAsExtensionParam();
    params.msgType = "pub(bytes.sign)";
    params.request = {
      "address": address,
      "data": message,
    };

    final res = await sdk.api.keyring.signAsExtension(password, params);
    return res?.signature ?? '';
  }

J’ai pu vérifier que cela fonctionne très bien pour des messages de type String grâce à l’ui polakdot.js, mais depuis que j’ai changé pour des messages de type Uint8List, je ne sais pas si l’api derrière le traite comme une string ou pas, c’est le soucis des type dynamic induis par le binding JS…

Du coup lorsque je vérifie la signature du message Uint8List depuis l’ui polkadot.js, il me dit que ce n’est pas bon mais je ne sais pas si c’est dû au fait que l’ui n’attends que des messages de type String.

1 « J'aime »

Dans ta dernière version du message, le préfixe et IdtyIndex sont enfin corrects, par contre le genesis hash ne correspond pas à celui de la gdev, si tu teste sur une blockchain locale peut tu indiquer le genesis hash hexa fourni par polkadot js dans settings → metadata ?

Aussi, je ne sais pas quel est censé être l’ancienne clé, peut tu fournir une représentation ss58 ou hexa de l’ancienne clé ? Il me faut ces éléments pour essayer de générer le message de mon côté et voir quelles sont les diff :slight_smile:

Voilà plus de détail:

fromAddress: 5GAT6CJW8yVKwUuQc7sM5Kk9GZVTpbZYk9PfjNXtvnNgAJZ1
fromPubkey: DCovzCEnQm9GUWe6mr8u42JR1JAuoj3HbQUGdCkfTzSr
destAddress: 5D2HHRj6zCEL9iY3CBpKy1mBMtpXUJCFDNvevBqVQAWdzqSS
genesisHashString: 0x07c112ff6ab9d7d0d531ebe59f98b35318b2813b1655577380819d38d6182d99

prefix: [105, 99, 111, 107]
genesisHash: [7, 193, 18, 255, 106, 185, 215, 208, 213, 49, 235, 229, 159, 152, 179, 83, 24, 178, 129, 59, 22, 85, 87, 115, 128, 129, 157, 56, 214, 24, 45, 153]
idtyIndex: [21, 0, 0, 0]
oldPubkey: [181, 82, 178, 99, 198, 4, 156, 190, 78, 35, 102, 137, 255, 7, 162, 31, 16, 79, 255, 132, 130, 237, 230, 222, 176, 88, 245, 217, 237, 78, 196, 239]

messageToSign: [105, 99, 111, 107, 48, 55, 99, 49, 49, 50, 102, 102, 54, 97, 98, 57, 100, 55, 100, 48, 100, 53, 51, 49, 101, 98, 101, 53, 57, 102, 57, 56, 98, 51, 53, 51, 49, 56, 98, 50, 56, 49, 51, 98, 49, 54, 53, 53, 53, 55, 55, 51, 56, 48, 56, 49, 57, 100, 51, 56, 100, 54, 49, 56, 50, 100, 57, 57, 21, 0, 0, 0, 181, 82, 178, 99, 198, 4, 156, 190, 78, 35, 102, 137, 255, 7, 162, 31, 16, 79, 255, 132, 130, 237, 230, 222, 176, 88, 245, 217, 237, 78, 196, 239]
newKeySig: 0x42a6ba600f7ddac10dbd3a6ff1dc6f7c5c1afa6e0855cbf56b0e2e4c6908681f926414ae6268768daee93a4f7a818ff5611daba6e6c7f2dbf34ee9de5395598d

Je suis sur la GDev, pas en local.
Je viens de remarquer que je ne transformais pas correctement l’hexa du genesisHash en binary, j’ai donc indiqué ce qui me semble être la bonne valeur de genesisHash (mais le problème persiste…)

fromPubkey est l’ancienne pubkey en base58 correspondant à l’ancienne adresse ss58 fromAddress
oldPubkey est fromPubkey en binary.

Je récupère le genesisHash avec le call api.genesisHash.toHex() comme indiqué dans la doc polkadot.js, et je constate que c’est le même hash que via les metadata de polkadot.js UI.

Oui mais ensuite il faut convertir l’hexadécimal en binaire, ce qui donne ceci pour le genesis hash de la ĞDev:
EDIT: C’est le format binaire qu’on veut ici, donc toHex() ne sert à rien, utilise directement api.genesisHash.toU8a().

Ce qui doit te donner ça:

[128, 7, 193, 18, 255, 106, 185, 215, 208, 213, 49, 235, 229, 159, 152, 179, 83, 24, 178, 129, 59, 22, 85, 87, 115, 128, 129, 157, 56, 214, 24, 45, 153]

J’ai généré moi-même le message à partir de tes données, et voici le message que j’obtiens:

[105, 99, 111, 107, 128, 7, 193, 18, 255, 106, 185, 215, 208, 213, 49, 235, 229, 159, 152, 179, 83, 24, 178, 129, 59, 22, 85, 87, 115, 128, 129, 157, 56, 214, 24, 45, 153, 21, 0, 0, 0, 0, 0, 0, 0, 181, 82, 178, 99, 198, 4, 156, 190, 78, 35, 102, 137, 255, 7, 162, 31, 16, 79, 255, 132, 130, 237, 230, 222, 176, 88, 245, 217, 237, 78, 196, 239]

Seul le genesis hash diffère, le reste est bien identique à ton message.

1 « J'aime »

C’est ce que je voulais faire au début, mais pour une raison que j’ignore, lorsque j’essaie de juste récupérer cette valeur U8a depuis le binding javascript, Dart lève une erreur FormatException (FormatException: Unexpected end of input (at character 1), alors que je veux juste retourner la valeur de api.genesisHash.toU8a() via une sortie dynamic. Le problème ne se pose pas en hexa, donc je passe en hexa car plus simple pour traverser les contraintes du binding.

Je ne comprends pas d’où sort ce 128 au début de cette chaine, c’est la seule différence avec ma sortie, mais j’ai beau retourner dans tous les sens, je n’ai jamais ce byte en début de chaine…

J’enlève évidement le 0x de début de string hexa sans quoi la conversion est impossible (comme partout ailleurs pour mes transformation hexa en dart).

Je continue de creuser mais je ne comprends pas.


Par désespoir je me suis rabattu sur python pour comparer, et j’ai bien la même chose qu’en dart, je ne sais pas d’où vient ton byte 128:

hex_string = "07c112ff6ab9d7d0d531ebe59f98b35318b2813b1655577380819d38d6182d99"
tata = bytearray.fromhex(hex_string)
print(list(tata))

[7, 193, 18, 255, 106, 185, 215, 208, 213, 49, 235, 229, 159, 152, 179, 83, 24, 178, 129, 59, 22, 85, 87, 115, 128, 129, 157, 56, 214, 24, 45, 153]

Moi non plus, c’est le code rust que j’ai écrit à l’arrache en 5 min pour générer le message qui contenait une erreur :sweat_smile:

Voici le message corrigé:

[105, 99, 111, 107, 7, 193, 18, 255, 106, 185, 215, 208, 213, 49, 235, 229, 159, 152, 179, 83, 24, 178, 129, 59, 22, 85, 87, 115, 128, 129, 157, 56, 214, 24, 45, 153, 21, 0, 0, 0, 0, 0, 0, 0, 181, 82, 178, 99, 198, 4, 156, 190, 78, 35, 102, 137, 255, 7, 162, 31, 16, 79, 255, 132, 130, 237, 230, 222, 176, 88, 245, 217, 237, 78, 196, 239]

À tu bien exactement ce message ? Peut tu coller en réponse la dernière version de ton message ?

Oui:

[105, 99, 111, 107, 7, 193, 18, 255, 106, 185, 215, 208, 213, 49, 235, 229, 159, 152, 179, 83, 24, 178, 129, 59, 22, 85, 87, 115, 128, 129, 157, 56, 214, 24, 45, 153, 21, 0, 0, 0, 181, 82, 178, 99, 198, 4, 156, 190, 78, 35, 102, 137, 255, 7, 162, 31, 16, 79, 255, 132, 130, 237, 230, 222, 176, 88, 245, 217, 237, 78, 196, 239]

La différence:
Chez moi idtyIndex = [21, 0, 0, 0]
Chez toi = [21, 0, 0, 0, 0, 0, 0, 0]

Ca me semble être la seule différence.

Et c’est ta version qui est la bonne, j’ai encore corrigé mon code, et copié/collé la dernière version de ton message dans un assert_eq, j’ai bien exactement le même message maintenant :slight_smile:

Donc ça devrait fonctionner, si tu as toujours la même erreur, c’est que tu ne génères pas la signature correctement.

1 « J'aime »