fmerg / pymerkle

Merkle-tree in Python

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unable to validate proofs from dicts or JSON strings (version 3.0.2b)

febrezo opened this issue · comments

I'm having problems when creating proofs from dicts or JSON files.

>>> from pymerkle import MerkleTree, validateProof, validationReceipt
>>> 
>>> tree = MerkleTree()                                           # Empty SHA256/UTF-8 Merkle-tree with
>>>                                                               # defense against second-preimage attack
... 
>>> for _ in range(665):                                          # Update the tree with 666 records
...     tree.encryptRecord(bytes('%d-th record' % _, 'utf-8'))
... 
>>> _audit = tree.auditProof('12-th record')                      # Request audit-proof for the given record        
>>> 
>>> validateProof(target_hash=tree.rootHash(), proof=_audit)           # Quick validation of the above proof (True)
True

Afterwards, I get the serialized representation of the proof:

>>> x = _audit.serialize()
>>> 
>>> from pymerkle.proof import Proof

And I create a new proof:

>>> new_proof = Proof(from_dict=x)

Validating new proofs throws this exception:

>>> validateProof(target_hash=tree.rootHash(), proof=new_proof)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/<MY_USER>/.local/lib/python3.7/site-packages/pymerkle/validations.py", line 46, in validateProof
    proof.body['proof_path'], proof.body['proof_index'])
  File "/home/<MY_USER>/.local/lib/python3.7/site-packages/pymerkle/hashing.py", line 230, in multi_hash
    signed_hashes[i][1], signed_hashes[i + 1][1])
  File "/home/<MY_USER>/.local/lib/python3.7/site-packages/pymerkle/hashing.py", line 174, in hash
    first.decode(encoding=self.ENCODING),
AttributeError: 'str' object has no attribute 'decode'
commented

This is fortunately a high-level issue.

Proof validation (a special application of the .multi_hash() function, which is built upon .hash() with 2 arguments) presupposes that the hashes comprising the proof-path are stored as bytes objects. The problem arises because, when reconstructing a proof with the from_dict kwarg, the hashes are not converted into bytes but remain str (like in the provided dictionary). You can see the type difference by invoking the corresponding dictionaries within the Python interpreter:

>>> _audit.__dict__                                                                                                                                  
 
{'header': {'uuid': '18405cec-a56e-11e9-9d53-701ce71deb6a',
  'timestamp': 1563022788,
  'creation_moment': 'Sat Jul 13 15:59:48 2019',
  'generation': True,
  'provider': '12a32350-a56e-11e9-9d53-701ce71deb6a',
  'hash_type': 'sha256',
  'encoding': 'utf_8',
  'security': True,
  'status': True},
 'body': {'proof_index': 2,
  'proof_path': ((1,
    b'77c3f71453eb7ae4e7ca41d8d5bf241e9c64dd60886692c9a1ef886d2d7ff58d'),
   (-1, b'4b3184b045fe0817855faa5a364a159389cf52a1161aa627495911c75a53fa17'),
   (1, b'a1e98774ac9834bc287ebe729e2ff5a1d89d6679b9426304071dc01546e978af'),
   (1, b'0190cf8cbf99445db248af0cf325020bb96899b1a31dbd9062c5bc5df6649a50'),
   (-1, b'391439096b73f6075078f0172117e1d4757afa6192526a38d5473bbdc5dabfc1'),
   (1, b'66951981875c2a5f94b2e6faf02a0419a3242b825998805fdd094a761ddd180a'),
   (1, b'bb6387d5ae9ebe242e29200cee60b04a9439055c5ab95d36259946c0ed56b7ef'),
   (1, b'07782a72e2be80c48c9533a9e0ed8d0ec9b47fec4e2d0388967f80ce23de72ef'),
   (1, b'3c3afa8902dc28e816e0dbb67b0052de23c6cee37cc333f388985d1ad77de288'),
   (1, b'50762ce9874169e792a50ad072b960c712b78822d24756faf9f389fafa009cb6'),
   (-1, b'c5725a8e2daac91792a85dd7f202c8e38a1fb8634fe749d42b50f8a622645f27'))}}

>>> new_proof.__dict__                                                                                                                                  
 
{'header': {'uuid': '18405cec-a56e-11e9-9d53-701ce71deb6a',
  'timestamp': 1563022788,
  'creation_moment': 'Sat Jul 13 15:59:48 2019',
  'generation': True,
  'provider': '12a32350-a56e-11e9-9d53-701ce71deb6a',
  'hash_type': 'sha256',
  'encoding': 'utf_8',
  'security': True,
  'status': True},
 'body': {'proof_index': 2,
  'proof_path': ((1,
    '77c3f71453eb7ae4e7ca41d8d5bf241e9c64dd60886692c9a1ef886d2d7ff58d'),
   (-1, '4b3184b045fe0817855faa5a364a159389cf52a1161aa627495911c75a53fa17'),
   (1, 'a1e98774ac9834bc287ebe729e2ff5a1d89d6679b9426304071dc01546e978af'),
   (1, '0190cf8cbf99445db248af0cf325020bb96899b1a31dbd9062c5bc5df6649a50'),
   (-1, '391439096b73f6075078f0172117e1d4757afa6192526a38d5473bbdc5dabfc1'),
   (1, '66951981875c2a5f94b2e6faf02a0419a3242b825998805fdd094a761ddd180a'),
   (1, 'bb6387d5ae9ebe242e29200cee60b04a9439055c5ab95d36259946c0ed56b7ef'),
   (1, '07782a72e2be80c48c9533a9e0ed8d0ec9b47fec4e2d0388967f80ce23de72ef'),
   (1, '3c3afa8902dc28e816e0dbb67b0052de23c6cee37cc333f388985d1ad77de288'),
   (1, '50762ce9874169e792a50ad072b960c712b78822d24756faf9f389fafa009cb6'),
   (-1, 'c5725a8e2daac91792a85dd7f202c8e38a1fb8634fe749d42b50f8a622645f27'))}}

By the way, an identical error occurs if you try to validate a proof constructed by means of the from_json kwarg.

The Proof constructor is now fixed and the problem does not arise (corresponding tests also modified and passed). Here your steps reproduced (note also the slight differences in the 4.0.1b syntax) leading to the expected results:

In [1]: from pymerkle import MerkleTree, validateProof                                                                                                    

In [2]: tree = MerkleTree()                                                                                                                               

In [3]: for _ in range(665): 
   ...:     tree.encryptRecord(bytes('%d-th record' % _, 'utf-8')) 
   ...:                                                                                                                                                   

In [4]: _audit = tree.auditProof('12-th record')                                                                                                          

In [5]: validateProof(target=tree.rootHash, proof=_audit)                                                                                                 
Out[5]: True

In [6]:                                                                                                                                                   

In [6]: x = _audit.serialize()                                                                                                                        

In [7]: y = _audit.JSONstring()                                                                                                                       

In [8]: from pymerkle.proof import Proof                                                                                                                  

In [9]:                                                                                                                                                   

In [9]: _audit_x = Proof(from_dict=x)                                                                                                              

In [10]: _audit_y = Proof(from_json=y)                                                                                                             

In [11]:                                                                                                                                                  

In [11]: validateProof(target=tree.rootHash, proof=_audit_x)                                                                                           
Out[11]: True

In [12]: validateProof(target=tree.rootHash, proof=_audit_y)                                                                                           
Out[12]: True

In [13]:      

Thanks a lot for reporting the bug.