class ChainPointV2(object): def __init__(self, hash_type="sha256"): self.hash_type = hash_type.lower() self.mk = MerkleTools(hash_type) '''Wraps merkletools method''' def reset_tree(self): self.mk.reset_tree() '''Wraps merkletools method''' def add_leaf(self, values, do_hash=False): self.mk.add_leaf(values, do_hash) '''Wraps merkletools method''' def get_leaf(self, index): return self.mk.get_leaf(index) '''Wraps merkletools method''' def get_leaf_count(self): return self.mk.get_leaf_count() '''Wraps merkletools method''' def get_tree_ready_state(self): return self.mk.get_tree_ready_state() '''Wraps merkletools method''' def make_tree(self): self.mk.make_tree() '''Wraps merkletools method''' def get_merkle_root(self): return self.mk.get_merkle_root() '''Wraps merkletools method''' def get_proof(self, index): return self.mk.get_proof(index) '''Wraps merkletools method''' def validate_proof(self, proof, target_hash, merkle_root): return self.mk.validate_proof(proof, target_hash, merkle_root) def get_chainpoint_hash_type(self): return CHAINPOINT_HASH_TYPES[self.hash_type] ''' Returns the chainpoint v2 blockchain receipt for specific leaf Currently only works for BTC anchors ''' def get_receipt(self, index, btc_source_id): if self.get_tree_ready_state(): return { "@context": CHAINPOINT_CONTEXT, "type": self.get_chainpoint_hash_type(), "targetHash": self.get_leaf(index), "merkleRoot": self.get_merkle_root(), "proof": self.get_proof(index), "anchors": [{ "type": "BTCOpReturn", "sourceId": btc_source_id }] } else: return None ''' Validates a chainpoint receipt. Currently only for BTC anchors receipt is the chainpoint_proof metadata from the pdf file. certificate_hash is the hash of the certificate after we removed the chainpoint_proof metadata issuer_identifier is a fixed 8 bytes issuer code that displays on the blockchain testnet specifies if testnet or mainnet was used ''' # TODO consider using exceptions instead of (bool, text) tuples; this is # really only needed for valid but soon to expire def validate_receipt(self, receipt, certificate_hash, issuer_identifier='', testnet=False): # check context and hash type if (receipt['@context'].lower() != CHAINPOINT_CONTEXT): return False, "wrong chainpoint context" if (receipt['type'] not in CHAINPOINT_HASH_TYPES.values()): return False, "type not in CHAINPOINT_HASH_TYPES" target_hash = receipt['targetHash'] merkle_root = receipt['merkleRoot'] proof = receipt['proof'] # validate actual hash if target_hash.lower() != certificate_hash.lower(): return False, "certificate hash is different than the one in receipt" # validate merkle proof if (not self.validate_proof(proof, target_hash, merkle_root)): return False, "certificate's hash is not in merkle root" txid = self.get_txid_from_receipt(receipt) # validate anchor op_return_hex = network_utils.get_op_return_hex_from_blockchain( txid, testnet) # ignore issuer_identifier for now (it is string in CRED but used to be # hex so we need a smart way to get it) -- TODO: obsolete it !!! #issuer_id_hex = utils.text_to_hex(issuer_identifier) # if op_return starts with CRED it is using the meta-protocol op_dict = cred_protocol.parse_op_return_hex(op_return_hex) if op_dict: version_hex = op_dict['version'] command_hex = op_dict['cmd'] # could check if it is equal to issuer_id_hex issuer_hex = op_dict['data']['issuer_identifier'] # get merkle root hash_hex = op_dict['data']['merkle_root'] # if issue with expiry get expiry date if command_hex == cred_protocol.hex_op('op_issue_abs_expiry'): expiry_hex = op_return_hex[96:116] else: expiry_hex = None #print(version_hex) #print(command_hex) #print(issuer_hex) #print(hash_hex) #print(merkle_root.lower()) # otherwise op_return should be fixed to 7 bytes or 14 hex chars (old prefix method) else: ignore_hex_chars = 14 hash_hex = op_return_hex[ignore_hex_chars:] if (not merkle_root.lower() == hash_hex.lower()): return False, "certificate's merkle root is different than the one in the blockchain" # only for CRED protocol certificates check expiration date if # issued with expiry date if op_dict and expiry_hex: expiry = utils.hex_to_int(expiry_hex) if expiry > int(time.time()): return True, "valid until: " + str(expiry) else: return False, "certificate expired at: " + str(expiry) return True, None # def get_op_return_hex_from_blockchain(self, txid, testnet): # # uses blockcypher API for now -- TODO: expand to consult multiple services # if testnet: # blockcypher_url = "https://api.blockcypher.com/v1/btc/test3/txs/" + txid # else: # blockcypher_url = "https://api.blockcypher.com/v1/btc/main/txs/" + txid # # response = requests.get(blockcypher_url).json() # outputs = response['outputs'] # hash_hex = "" # for o in outputs: # script = o['script'] # if script.startswith('6a'): # # when > 75 op_pushdata1 (4c) is used before length # if script.startswith('6a4c'): # # 2 for 1 byte op_return + 2 for 1 byte op_pushdata1 + 2 for 1 byte data length # ignore_hex_chars = 6 # else: # # 2 for 1 byte op_return + 2 for 1 byte data length # ignore_hex_chars = 4 # # hash_hex = script[ignore_hex_chars:] # break # return hash_hex def get_txid_from_receipt(self, receipt): # get anchor # TODO currently gets only the first valid (BTC) anchor anchors = receipt['anchors'] txid = '' for a in anchors: if a['type'] in CHAINPOINT_ANCHOR_TYPES.values(): txid = a['sourceId'] break return txid
class ChainPointV2(object): def __init__(self, hash_type="sha256"): self.hash_type = hash_type.lower() self.mk = MerkleTools(hash_type) '''Wraps merkletools method''' def reset_tree(self): self.mk.reset_tree() '''Wraps merkletools method''' def add_leaf(self, values, do_hash=False): self.mk.add_leaf(values, do_hash) '''Wraps merkletools method''' def get_leaf(self, index): return self.mk.get_leaf(index) '''Wraps merkletools method''' def get_leaf_count(self): return self.mk.get_leaf_count() '''Wraps merkletools method''' def get_tree_ready_state(self): return self.mk.get_tree_ready_state() '''Wraps merkletools method''' def make_tree(self): self.mk.make_tree() '''Wraps merkletools method''' def get_merkle_root(self): return self.mk.get_merkle_root() '''Wraps merkletools method''' def get_proof(self, index): return self.mk.get_proof(index) '''Wraps merkletools method''' def validate_proof(self, proof, target_hash, merkle_root): return self.mk.validate_proof(proof, target_hash, merkle_root) def get_chainpoint_hash_type(self): return CHAINPOINT_HASH_TYPES[self.hash_type] ''' Returns the chainpoint v2 blockchain receipt for specific leaf Currently only works for bitcoin and litecoin anchors ''' def get_receipt(self, index, source_id, chain, testnet): chain_type = utils.get_chain_type(chain, testnet) if(chain_type is not None and self.get_tree_ready_state()): return { "@context": CHAINPOINT_CONTEXT, "type": self.get_chainpoint_hash_type(), "targetHash": self.get_leaf(index), "merkleRoot": self.get_merkle_root(), "proof": self.get_proof(index), "anchors": [ { "type": CHAINPOINT_ANCHOR_TYPES[chain_type], "sourceId": source_id } ] } else: return None ''' Validates a chainpoint receipt. Currently for BTC and LTC anchors receipt is the chainpoint_proof metadata from the pdf file. certificate_hash is the hash of the certificate after we removed the chainpoint_proof metadata issuer_identifier is a fixed 8 bytes issuer code that displays on the blockchain ''' # TODO consider using exceptions instead of (bool, text) tuples; this is # really only needed for valid but soon to expire def validate_receipt(self, receipt, op_return_hex, certificate_hash, issuer_identifier=''): # check context and hash type if(receipt['@context'].lower() != CHAINPOINT_CONTEXT): return False, "wrong chainpoint context" if(receipt['type'] not in CHAINPOINT_HASH_TYPES.values()): return False, "chainpoint type not in CHAINPOINT_HASH_TYPES" # currently only one anchor at a time is allowed; thus 0 if(receipt['anchors'][0]['type'] not in CHAINPOINT_ANCHOR_TYPES.values()): return False, "anchor type not in CHAINPOINT_ANCHOR_TYPES" target_hash = receipt['targetHash'] merkle_root = receipt['merkleRoot'] proof = receipt['proof'] # validate actual hash if target_hash.lower() != certificate_hash.lower(): return False, "certificate hash is different than the one in receipt" # validate merkle proof if(not self.validate_proof(proof, target_hash, merkle_root)): return False, "certificate's hash is not in merkle root" # ignore issuer_identifier for now (it is string in CRED but used to be # hex so we need a smart way to get it) -- TODO: obsolete it !!! #issuer_id_hex = utils.text_to_hex(issuer_identifier) # if op_return starts with CRED it is using the meta-protocol op_dict = cred_protocol.parse_op_return_hex(op_return_hex) if op_dict: version_hex = op_dict['version'] command_hex = op_dict['cmd'] # could check if it is equal to issuer_id_hex issuer_hex = op_dict['data']['issuer_identifier'] # get merkle root hash_hex = op_dict['data']['merkle_root'] # if issue with expiry get expiry date if command_hex == cred_protocol.hex_op('op_issue_abs_expiry'): expiry_hex = op_return_hex[96:116] else: expiry_hex = None #print(version_hex) #print(command_hex) #print(issuer_hex) #print(hash_hex) #print(merkle_root.lower()) # otherwise op_return should be fixed to 7 bytes or 14 hex chars (old prefix method) else: ignore_hex_chars = 14 hash_hex = op_return_hex[ignore_hex_chars:] if(not merkle_root.lower() == hash_hex.lower()): return False, "certificate's merkle root is different than the one in the blockchain" # only for CRED protocol certificates check expiration date if # issued with expiry date if op_dict and expiry_hex: expiry = utils.hex_to_int(expiry_hex) if expiry > int(time.time()): return True, "valid until: " + str(expiry) else: return False, "certificate expired at: " + str(expiry) return True, None # TODO: DELETE - NOT USED ANYWHERE !! def get_chain_testnet_txid_from_receipt(self, receipt): # get anchor # TODO currently gets only the first valid anchor anchors = receipt['anchors'] chain = '' testnet = False txid = '' for a in anchors: if a['type'] in CHAINPOINT_ANCHOR_TYPES.values(): if(a['type'] == 'BTCOpReturn'): chain = 'bitcoin' testnet = False elif(a['type'] == 'LTCOpReturn'): chain = 'litecoin' testnet = False elif(a['type'] == 'BTCTestnetOpReturn'): chain = 'bitcoin' testnet = True elif(a['type'] == 'LTCTestnetOpReturn'): chain = 'litecoin' testnet = True txid = a['sourceId'] break return chain, testnet, txid
def test_add_leaf(): mt = MerkleTools() mt.add_leaf("tierion") mt.add_leaf(["bitcoin", "blockchain"]) assert mt.get_leaf_count() == 3 assert mt.is_ready is False
def test_add_leaf(): mt = MerkleTools() mt.add_leaf("tierion", do_hash=True) mt.add_leaf(["bitcoin", "blockchain"], do_hash=True) assert mt.get_leaf_count() == 3 assert mt.is_ready == False
import merkletools from merkletools import MerkleTools from hashlib import sha256 import json myMerkleT = MerkleTools(hash_type="sha256") # This will make use of SHA256 myMerkleT.add_leaf(["Security", "Room14", "Professor"], True) # My first leaves. myMerkleT.add_leaf(["Room12", "blockchain", "Python"], True) # This adds three leaves to the Tree. myMerkleT.add_leaf(["Name:Justice", "Course:Computer Science"], True) # Another three leaves. myMerkleT.make_tree() # Creating the Merkle Tree. print('') numberOfLeaves = myMerkleT.get_leaf_count() print("The number of leaves of this tree is: ", numberOfLeaves) print("root:", myMerkleT.get_merkle_root()) print("The value at leaf 2 is: ", myMerkleT.get_leaf(2)) print("The array of hash objects for the leaf at index 2 is: ", myMerkleT.get_proof(2)) print('') print('************************ Merkle Proof ****************************') print(myMerkleT.validate_proof(myMerkleT.get_proof(2), myMerkleT.get_leaf(2), myMerkleT.get_merkle_root())) # True print('************************ Merkle Proof ****************************') TestData = ["My Professor is helping me to learn well."]
from merkletools import MerkleTools mt = MerkleTools() mt.add_leaf("tierion", True) mt.add_leaf(["bitcoin", "blockchain"], True) mt.make_tree() print mt.get_leaf_count() print "root:", mt.get_merkle_root( ) # root: '777765f15d171871b00034ee55e48ffdf76afbc44ed0bcff5c82f31351d333c2ed1' print mt.get_proof( 1 ) # [{left: '2da7240f6c88536be72abe9f04e454c6478ee29709fc3729ddfb942f804fbf08'}, # {right: 'ef7797e13d3a75526946a3bcf00daec9fc9c9c4d51ddc7cc5df888f74dd434d1'}] print mt.validate_proof(mt.get_proof(1), mt.get_leaf(1), mt.get_merkle_root())