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'
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.