block.Block : computed_inner_hash(), sign(), proof_of_work are faulty
While trying to write a genesis block, I encountered problems with block.Block's methods.
computed_inner_hash() and sign() don't match the DUBP. As a consequence, proof_of_work does not either for creating a block (it is fine for checking a block).
I wrote independent functions that work. Here is a snippet showing that current Duniterpy's implementation of InnerHash and signature are wrong, and my propositions :
#!/usr/bin/env python3
"""
Duniter block.Block class is buggy.
In this file are :
* proposed functions
* tests using a valid GTest block, that show:
* duniterpy's implementations are wrong
* new implementations are right
"""
###### imports ######
import hashlib
import base64
from duniterpy.documents.block import Block
from duniterpy.documents.block_uid import BlockUID, block_uid
from duniterpy.key.signing_key import SigningKey
###### proposed functions ######
def new_inner_hash(block) -> str:
"""
Duniterpy's Block.computed_inner_hash() is bugged.
Use this instead.
"""
doc = block.raw()
# get all the block content, except InnerHash, Nonce, Signature (2 last lines of raw block)
inner_doc = "\n".join(doc.split("\n")[:-3]) + "\n"
return hashlib.sha256(inner_doc.encode("ascii")).hexdigest().upper()
def new_sign_block(block, key) -> str:
"""
Duniterpy's Block.sign() does not match the RFC specs.
Use this instead.
"""
# sign only Innerhash and Nonce lines (2 last lines of raw block)
signed = "InnerHash: {inner_hash}\nNonce: {nonce}\n".format(inner_hash = block.inner_hash, nonce = block.nonce)
signing = base64.b64encode(key.signature(bytes(signed, "ascii")))
signature = signing.decode("ascii")
return signature
###### values ######
key = SigningKey.from_credentials (salt = "a", password = "a", scrypt_params = None)
# GTEST block #699652
raw_block = "Version: 12\nType: Block\nCurrency: g1-test\nNumber: 699652\nPoWMin: 59\nTime: 1612006804\nMedianTime: 1612002508\nUnitBase: 3\nIssuer: 6upqFiJ66WV6N3bPc8x8y7rXT3syqKRmwnVyunCtEj7o\nIssuersFrame: 36\nIssuersFrameVar: 0\nDifferentIssuersCount: 7\nPreviousHash: 00029FC6A511CD41361B016DEA02982350B196474786FC460FCA25708F93BBD1\nPreviousIssuer: 8iy1coxB82TsK62ZXbWy92ZcntCgVxAcC3WKuZrTb62o\nMembersCount: 25\nIdentities:\nJoiners:\nActives:\nLeavers:\nRevoked:\nExcluded:\nCertifications:\nTransactions:\nInnerHash: 2E5E26304A59AD8925D193C4ABD87A89847B6B129F534E7B5E8F06A09B6BB605\nNonce: 10000000085087\n"
raw_block_signature = "oj3JKhkTs20st1o3bfg3CofDuuIXcdI/HrOcy336qGPeOU/P+CXjSH/olYhMh3VtLJyeR3Yp+MeneEPuWHksDw=="
test_block = Block.from_signed_raw(raw_block + raw_block_signature + "\n")
###### tests ######
# inner hash
assert not test_block.computed_inner_hash() == test_block.inner_hash #InnerHash
assert new_inner_hash(test_block) == test_block.inner_hash
# signature
test_block.sign([key])
signature = test_block.signed_raw().split("\n")[-1]
assert not signature == raw_block_signature
signature = new_sign_block(test_block, key)
assert signature == raw_block_signature
Edited by matograine