Verify blocks signatures with Silkaj/DuniterPy

Sous-section de :

À lancer avec apython, et aioconsole d’installé, pour avoir de l’asynchrone en console :

from duniterpy.api import bma
from duniterpy.api.client import Client
from duniterpy.documents import Block
from duniterpy.key.verifying_key import VerifyingKey

client = Client("BMAS g1.duniter.org 443")
received_block = await client(bma.blockchain.block, 15144)
block = Block.from_signed_raw(received_block["raw"] + received_block["signature"] + "\n")
key = VerifyingKey(block.issuer)
key.verify_document(block)
False                            

Signature du bloc 15144 invalide. Pareil avec la “bonne” signature :

good_signature = "aZusVDRJA8akPse/sv4uK8ekUuvTGj1OoKYVdMQQ/3+VMaDJ02I795GBBaLgjypZFEKYlPMssJMn/X+F/pxgAw=="
block = Block.from_signed_raw(received_block["raw"] + good_signature + "\n")

Également invalide pour les blocs suivant 1514{5,6} :

received_block = await client(bma.blockchain.block, 15145)
block = Block.from_signed_raw(received_block["raw"] + received_block["signature"] + "\n")
key = VerifyingKey(block.issuer)
key.verify_document(block)
False
ValueError: Failed to validate message

Je sais pas quel est le problème côté DuniterPy. Le code utilisé :

@Moul il n’y a pas de saut de ligne final après la signature :wink:

C’est comme ça qu’est implémenté le parsing de bloc dans DuniterPy. Par exemple dans Sakia :

grep -rn "Block\.from_signed_raw"
src/sakia/data/processors/blockchain.py:202:            block_doc = Block.from_signed_raw("{0}{1}\n".format(block['raw'], block['signature']))
src/sakia/services/transactions.py:136:                        block = Block.from_signed_raw(block_data["raw"] + block_data["signature"] + "\n")

Sinon, ça fait une erreur de ce genre :

>>> block = Block.from_signed_raw(received_block["raw"] + "aZusVDRJA8akPse/sv4uK8ekUuvTGj1OoKYVdMQQ/3+VMaDJ02I795GBBaLgjypZFEKYlPMssJ
Mn/X+F/pxgAw==")                                                                                                                     
Traceback (most recent call last):
  File "/home/moul/duniterpy/duniterpy/documents/document.py", line 68, in parse_field
    raise AttributeError
AttributeError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/moul/.local/lib/python3.7/site-packages/aioconsole/execute.py", line 95, in aexec
    result, new_local = yield from coro
  File "<aexec>", line 2, in __corofn
  File "/home/moul/duniterpy/duniterpy/documents/block.py", line 405, in from_signed_raw                                             
    signature = Block.parse_field("Signature", lines[n])                                                                             
  File "/home/moul/duniterpy/duniterpy/documents/document.py", line 71, in parse_field                                               
    raise MalformedDocumentError(field_name)                                                                                         
duniterpy.documents.document.MalformedDocumentError: Could not parse field Signature                                                 

Il y a un ticket à ce sujet.

Arf c’est peut-être l’occasion de découvrir dans la foulée un bug dans duniterpy :sweat_smile:

Possible :slight_smile:
Il est aussi possible que je ne l’utilise pas correctement.
Autrement, ça ne m’étonnerait pas de découvrir à nouveau des bugs. J’en ai trouvé pas mal depuis que je l’utilise.

Bon, c’est utilisé à un seul endroit dans Sakia :

    def handle_new_node(self, peer):
        key = VerifyingKey(peer.pubkey)
        if key.verify_document(peer):

Du coup, il me semble que je l’utilise correctement par rapport à ce code.

@Moul @vit je viens de vérifier le code et duniterpy ne sais vérifier que les documents utilisateurs + la fiche de peer, il ne sais pas vérifier le document bloc car ce dernier a une particularité : c’est le seul document dont on ne signe pas le format raw mais la partie InnerHash: BC.FE\nNonce: 012..348\n (le format raw sert justement a obtenir le inner hash)

Pour tester coté Duniterpy sans avoir de dev a faire, il vous suffit de rédiger un TU qui appelle directement la méthode verify de l’objet libnacl.sign.Verifier

https://git.duniter.org/clients/python/duniterpy/blob/dev/duniterpy/key/verifying_key.py#L30-37

1 Like

J’ai remplacé cette ligne par :

#prepended = signature + bytes(document.raw(), "ascii")
prepended = signature + bytes("InnerHash: {0}\nNonce: {1}\n".format(document.inner_hash, document.nonce), "ascii")

et je n’y arrive toujours pas avec la bonne signature ainsi qu’avec un autre bloc.

1 Like

@Moul forcément ça ne peut pas fonctionner tu a rajouté un espace devant Nonce, j’ai pourtant pris soin de ne pas insérer cet espace dans mes précédents post :wink:

2 Likes

Ok, top ! Merci pour la typo.
Du coup le bloc #15144 a une signature valide avec la bonne signature que t’as donné plus haut et a une signature invalide avec la signature présente dans le bloc.
Et, sur le bloc #15145, ça passe nickel !

3 Likes

Bon ben @moul a été plus rapide que moi ! :blush:

Tout est bien qui finit bien pour Duniterpy, ça me rassure.

[EDIT]
Un ticket vient d’être créé :

3 Likes

De même pour ce post : discussion rendue publique pour raison d’archivage.
La commande verify de Silkaj permet de vérifier la validité des blocs.

1 Like

@Moul @vit, maintenant que nous somme passé au protocole v12 sur la G1, l’un de vous peut-il fournir la liste définitive des blocs invalides sur la blockchain G1 ? (Et aussi refournir la liste pour la G1-test, je crois que Moul l’avais posté, mais je ne sais plus dans quel sujet.)

Bonne nouvelle, ça n’a pas changé ~depuis~ qu’on s’est attaqué au problème :

silkaj verify
Processing blocks verification  [####################################]  100%          
Within 0-309400 range, blocks with a wrong signature: 15144 31202 85448 87566 90830 109327 189835 199172 221274 253582
silkaj -gt verify
Processing blocks verification  [####################################]  100%          
Within 0-540714 range, blocks with a wrong signature: 24316 62067 62551 93288 173118 183706 196196 246027 247211 263207 307038 328741 335914 377316 395714 396024 407913 422366 496751

Ça rend la commande verify obsolète avant même qu’elle ne soit sortie. Bon, elle apporte une bonne expérience au projet en termes de test ainsi qu’une base de code pour télécharger toute la chaîne et y faire de potentielles autres études et analyses de la chaîne de blocs en masse.

2 Likes