Skip to content

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
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information