Example #1
0
    def test_make_tree_with_add_leaf_hex(self):
        tree = MerkleTree()
        tree.add_leaf(
            'a292780cc748697cb499fdcc8cb89d835609f11e502281dfe3f6690b1cc23dcb')
        tree.add_leaf(
            'cb4990b9a8936bbc137ddeb6dcab4620897b099a450ecdc5f3e86ef4b3a7135c')
        tree.make_tree()

        self.assertEqual(tree.get_merkle_root(), self.mRoot,
                         'merkle root value should be correct')

        hashes = []
        hashes.append(
            'a292780cc748697cb499fdcc8cb89d835609f11e502281dfe3f6690b1cc23dcb')
        hashes.append(
            'cb4990b9a8936bbc137ddeb6dcab4620897b099a450ecdc5f3e86ef4b3a7135c')

        tree = MerkleTree()
        tree.add_leaves(hashes)
        tree.make_tree()
        targetProof0 = tree.get_proof(0)
        targetProof1 = tree.get_proof(1)

        self.assertEqual(tree.get_merkle_root(), self.mRoot,
                         'merkle root value should be correct')
        self.assertEqual(len(targetProof0), 1,
                         'merkle root value should be correct')
        self.assertEqual(len(targetProof1), 1,
                         'merkle root value should be correct')
Example #2
0
    def test_make_tree_using_md5(self):
        tree = MerkleTree(md5)
        tree.add_leaves([self.bLeftmd5, self.bRightmd5])
        tree.make_tree()

        self.assertEqual(tree.get_merkle_root(), self.mRootmd5,
                         'merkle root value should be correct')
Example #3
0
    def make_tree_with_add_leaf_buffers(self):
        tree = MerkleTree()
        tree.add_leaf(self.bLeft)
        tree.add_leaf(self.bRight)
        tree.make_tree()

        self.assertEqual(tree.get_merkle_root(), self.mRoot,
                         'merkle root value should be correct')
Example #4
0
 def test_proof_single_true(self):
     tree = MerkleTree()
     tree.add_leaf('test', True)
     tree.make_tree()
     proof = tree.get_proof(0)
     target = sha256('test')
     self.assertTrue(
         tree.validate_proof(proof, target, tree.get_merkle_root()))
Example #5
0
    def test_large_tree(self):
        tree = MerkleTree()
        for i in range(10000):
            tree.add_leaf((str(i)), True)

        tree.make_tree()
        ans = 'e08a41fa2a658af6f552d22570da0e9511230e4c81d421ca7f206e76770045d6'
        self.assertEqual(tree.get_merkle_root(), ans)
Example #6
0
 def test_proof_single_false(self):
     tree = MerkleTree()
     tree.add_leaf('test', True)
     tree.make_tree()
     proof = tree.get_proof(1)
     self.assertFalse(
         tree.validate_proof(proof, sha256('test9'),
                             tree.get_merkle_root()))
Example #7
0
 def test_make_tree_add_leaves_hex(self):
     tree = MerkleTree()
     tree.add_leaves([
         'a292780cc748697cb499fdcc8cb89d835609f11e502281dfe3f6690b1cc23dcb',
         'cb4990b9a8936bbc137ddeb6dcab4620897b099a450ecdc5f3e86ef4b3a7135c'
     ])
     tree.make_tree()
     self.assertEqual(tree.get_merkle_root(), self.mRoot,
                      'merkle root value should be correct')
Example #8
0
    def test_two_even_items(self):
        tree = MerkleTree()

        tree.add_leaf('test', True)
        tree.add_leaf(tree.hash_f('test2'))
        tree.make_tree()

        ans = 'a0f6cfae7a24aaf251208954f67cbc0d9b87fb19e07d89a6d157fcce5ca558e9'
        self.assertEqual(tree.get_merkle_root(), ans)
Example #9
0
    def test_make_tree_with_5_leaves_at_once_needing_hashing(self):
        tree = MerkleTree()
        tree.add_leaves(['a', 'b', 'c', 'd', 'e'], True)
        tree.make_tree()

        self.assertEqual(
            tree.get_merkle_root(),
            'd71f8983ad4ee170f8129f1ebcdd7440be7798d8e1c80420bf11f1eced610dba',
            'merkle root value should be correct')
Example #10
0
    def test_tree_odd_items(self):
        tree = MerkleTree()

        tree.add_leaf('test', True)
        tree.add_leaf('test2', True)
        tree.add_leaf('test3', True)
        tree.make_tree()

        ans = 'ab56cfafe1f1a8c4d5a526a754a07513ab92266ccdc25295e5c6e468b7e8a807'
        self.assertEqual(tree.get_merkle_root(), ans)
Example #11
0
    def make_tree_with_1_leaf(self):
        tree = MerkleTree()
        tree.add_leaves([
            'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb'
        ])
        tree.make_tree()

        self.assertEqual(
            tree.get_merkle_root(),
            'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb',
            'merkle root value should be correct')
Example #12
0
    def test_proof_true(self):
        tree = MerkleTree()

        tree.add_leaf('test', True)
        tree.add_leaf('test2', True)
        tree.add_leaf('test3', True)
        tree.make_tree()

        proof = tree.get_proof(0)
        self.assertTrue(
            tree.validate_proof(proof, sha256('test'), tree.get_merkle_root()))
Example #13
0
    def test_proof_false(self):
        tree = MerkleTree()

        tree.add_leaf('test1', True)
        tree.add_leaf('test2', True)
        tree.add_leaf('test3', True)
        tree.make_tree()

        proof = tree.get_proof(4)
        result = tree.validate_proof(proof, sha256('test'),
                                     tree.get_merkle_root())
        self.assertFalse(result)
Example #14
0
    def test_make_tree_with_5_leaves(self):
        tree = MerkleTree()
        tree.add_leaves([
            'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb',
            '3e23e8160039594a33894f6564e1b1348bbd7a0088d42c4acb73eeaed59c009d',
            '2e7d2c03a9507ae265ecf5b5356885a53393a2029d241394997265a1a25aefc6',
            '18ac3e7343f016890c510e93f935261169d9e3f565436429830faf0934f4f8e4',
            '3f79bb7b435b05321651daefd374cdc681dc06faa65e374e38337b88ca046dea'
        ])
        tree.make_tree()

        self.assertEqual(
            tree.get_merkle_root(),
            'd71f8983ad4ee170f8129f1ebcdd7440be7798d8e1c80420bf11f1eced610dba',
            'merkle root value should be correct')
    def test_make_SHA3_512_tree_with_2leaves(self):
        tree = MerkleTree({'hashType': 'SHA3-512'})
        tree.add_leaves([
            '004a237ea808cd9375ee9db9f85625948a890c54e2c30f736f54c969074eb56f0ff3d43dafb4b40d5d974acc1c2a68c046fa4d7c2c20cab6df956514040d0b8b',
            '0b43a85d08c05252d0e23c96bc6b1bda11dfa787049ff452b3c86f4c6135e870c058c05131f199ef8619cfac937a736bbc936a667e4d96a5bf68e4056ce5fdce'
        ])
        tree.make_tree()

        self.validate_merkle_root(tree.get_merkle_root(),
                                  '3dff3f19b67628591d294cba2c07ed20d20d83e1624af8c1dca8fcf096127b9f86435e2d6a84ca4cee526525cacd1c628bf06ee938983413afafbb4598c5862a')

        self.validate_proof_array(tree.get_proof(0)[0]['right'],
                                  '0b43a85d08c05252d0e23c96bc6b1bda11dfa787049ff452b3c86f4c6135e870c058c05131f199ef8619cfac937a736bbc936a667e4d96a5bf68e4056ce5fdce')

        self.validate_proof(tree, 0,
                            '004a237ea808cd9375ee9db9f85625948a890c54e2c30f736f54c969074eb56f0ff3d43dafb4b40d5d974acc1c2a68c046fa4d7c2c20cab6df956514040d0b8b',
                            '3dff3f19b67628591d294cba2c07ed20d20d83e1624af8c1dca8fcf096127b9f86435e2d6a84ca4cee526525cacd1c628bf06ee938983413afafbb4598c5862a')
    def test_make_SHA3_224_tree_with_2_leaves(self):
        tree = MerkleTree({'hashType': 'SHA3-224'})
        tree.add_leaves([
            '6ed712b9472b671fd70bb950dc4ccfce197c92a7969f6bc2aa6b6d9f',
            '08db5633d406804d044a3e67683e179b5ee51249ed2139c239d1e65a'
        ])
        tree.make_tree()

        self.validate_merkle_root(tree.get_merkle_root(), '674bc9f53d5c666174cdd3ccb9df04768dfb7759655e7d937aef0c3a')

        def validate_proof_array(self, proof_value, expected):
            self.assertEqual(proof_value, expected, 'proof array should be correct')(tree.get_proof(0)[0]['right'],
                                                                                     '08db5633d406804d044a3e67683e179b5ee51249ed2139c239d1e65a')

        self.validate_proof(tree, 0,
                            '6ed712b9472b671fd70bb950dc4ccfce197c92a7969f6bc2aa6b6d9f',
                            '674bc9f53d5c666174cdd3ccb9df04768dfb7759655e7d937aef0c3a')
    def test_make_SHA3_384_tree_with_2_leaves(self):
        tree = MerkleTree({'hashType': 'SHA3-384'})
        tree.add_leaves([
            'e222605f939aa69b964a0a03d7075676bb3dbb40c3bd10b22f0adcb149434e7c1085c206f0e3371470a49817aa6d5b16',
            'ae331b6f8643ed7e404471c81be9a74f73fc84ffd5140a0ec9aa8596fa0d0a2ded5f7b780bb2fbfc4e2226ee2a04a2fa'
        ])
        tree.make_tree()

        self.validate_merkle_root(tree.get_merkle_root(),
                                  'bd54df0015fa0d4fee713fbf5c8ae232c93239c75fb9d41c7dd7a9278711764a6ee83c81766b3945ed94030254537b57')

        self.validate_proof_array(tree.get_proof(0)[0]['right'],
                                  'ae331b6f8643ed7e404471c81be9a74f73fc84ffd5140a0ec9aa8596fa0d0a2ded5f7b780bb2fbfc4e2226ee2a04a2fa')

        self.validate_proof(tree, 0,
                            'e222605f939aa69b964a0a03d7075676bb3dbb40c3bd10b22f0adcb149434e7c1085c206f0e3371470a49817aa6d5b16',
                            'bd54df0015fa0d4fee713fbf5c8ae232c93239c75fb9d41c7dd7a9278711764a6ee83c81766b3945ed94030254537b57')
    def test_make_SHA3_256_tree_with_2_leaves(self):
        tree = MerkleTree({'hashType': 'SHA3-256'})
        tree.add_leaves([
            '1d7d4ea1cc029ca460e486642830c284657ea0921235c46298b51f0ed1bb7bf7',
            '89b9e14eae37e999b096a6f604adefe7feea4dc240ccecb5e4e92785cffc7070'
        ])
        tree.make_tree()

        self.validate_merkle_root(tree.get_merkle_root(),
                                  '6edf674f5ce762e096c3081aee2a0a977732e07f4d704baf34f5e3804db03343')

        self.validate_proof_array(tree.get_proof(0)[0]['right'],
                                  '89b9e14eae37e999b096a6f604adefe7feea4dc240ccecb5e4e92785cffc7070')

        self.validate_proof(tree, 0,
                            '1d7d4ea1cc029ca460e486642830c284657ea0921235c46298b51f0ed1bb7bf7',
                            '6edf674f5ce762e096c3081aee2a0a977732e07f4d704baf34f5e3804db03343')
Example #19
0
    def test_make_SHA224_tree_with_2_leaves(self):
        tree = MerkleTree(sha224)
        tree.add_leaves([
            '90a3ed9e32b2aaf4c61c410eb925426119e1a9dc53d4286ade99a809',
            '35f757ad7f998eb6dd3dd1cd3b5c6de97348b84a951f13de25355177'
        ])
        tree.make_tree()

        self.validate_merkle_root(
            tree.get_merkle_root(),
            'f48bc49bb77d3a3b1c8f8a70db693f41d879189cd1919f8326067ad7')

        self.validate_proof_array(
            tree.get_proof(0)[0]['right'],
            '35f757ad7f998eb6dd3dd1cd3b5c6de97348b84a951f13de25355177')

        self.validate_proof(
            tree, 0,
            '90a3ed9e32b2aaf4c61c410eb925426119e1a9dc53d4286ade99a809',
            'f48bc49bb77d3a3b1c8f8a70db693f41d879189cd1919f8326067ad7')
Example #20
0
    def test_make_SHA256_tree_with_2_leaves(self):
        tree = MerkleTree()
        tree.add_leaves([
            '1516f000de6cff5c8c63eef081ebcec2ad2fdcf7034db16045d024a90341e07d',
            'e20af19f85f265579ead2578859bf089c92b76a048606983ad83f27ba8f32f1a'
        ])
        tree.make_tree()

        self.validate_merkle_root(
            tree.get_merkle_root(),
            '77c654b3d1605f78ed091cbd420c939c3feff7d57dc30c171fa45a5a3c81fd7d')

        self.validate_proof_array(
            tree.get_proof(0)[0]['right'],
            'e20af19f85f265579ead2578859bf089c92b76a048606983ad83f27ba8f32f1a')

        self.validate_proof(
            tree, 0,
            '1516f000de6cff5c8c63eef081ebcec2ad2fdcf7034db16045d024a90341e07d',
            '77c654b3d1605f78ed091cbd420c939c3feff7d57dc30c171fa45a5a3c81fd7d'
        ),
Example #21
0
    def test_make_SHA512_tree_with_2_leaves(self):
        tree = MerkleTree(sha512)
        tree.add_leaves([
            'c0a8907588c1da716ce31cbef05da1a65986ec23afb75cd42327634dd53d754be6c00a22d6862a42be5f51187a8dff695c530a797f7704e4eb4b473a14ab416e',
            'df1e07eccb2a2d4e1b30d11e646ba13ddc426c1aefbefcff3639405762f216fdcc40a684f3d1855e6d465f99fd9547e53fa8a485f18649fedec5448b45963976'
        ])
        tree.make_tree()

        self.validate_merkle_root(
            tree.get_merkle_root(),
            'd9d27704a3a785d204257bfa2b217a1890e55453b6686f091fa1be8aa2b265bc06c285a909459996e093546677c3f392458d7b1fc34a994a86689ed4100e8337'
        )

        self.validate_proof_array(
            tree.get_proof(0)[0]['right'],
            'df1e07eccb2a2d4e1b30d11e646ba13ddc426c1aefbefcff3639405762f216fdcc40a684f3d1855e6d465f99fd9547e53fa8a485f18649fedec5448b45963976'
        )

        self.validate_proof(
            tree, 0,
            'c0a8907588c1da716ce31cbef05da1a65986ec23afb75cd42327634dd53d754be6c00a22d6862a42be5f51187a8dff695c530a797f7704e4eb4b473a14ab416e',
            'd9d27704a3a785d204257bfa2b217a1890e55453b6686f091fa1be8aa2b265bc06c285a909459996e093546677c3f392458d7b1fc34a994a86689ed4100e8337'
        )
Example #22
0
    def test_make_SHA384_tree_with_2_leaves(self):
        tree = MerkleTree(sha384)
        tree.add_leaves([
            '84ae8c6367d64899aef44a951edfa4833378b9e213f916c5eb8492cc37cb951c726e334dace7dbe4bb1dc80c1efe33d0',
            '368c89a00446010def75ad7b179cea9a3d24f8cbb7e2755a28638d194809e7b614eb45453665032860b6c1a135fb6e8b'
        ])
        tree.make_tree()

        self.validate_merkle_root(
            tree.get_merkle_root(),
            'c363aa3b824e3f3b927034fab826eff61a9bfa2030ae9fc4598992edf9f3e42f8b497d6742946caf7a771429eb1745cf'
        )

        self.validate_proof_array(
            tree.get_proof(0)[0]['right'],
            '368c89a00446010def75ad7b179cea9a3d24f8cbb7e2755a28638d194809e7b614eb45453665032860b6c1a135fb6e8b'
        )

        self.validate_proof(
            tree, 0,
            '84ae8c6367d64899aef44a951edfa4833378b9e213f916c5eb8492cc37cb951c726e334dace7dbe4bb1dc80c1efe33d0',
            'c363aa3b824e3f3b927034fab826eff61a9bfa2030ae9fc4598992edf9f3e42f8b497d6742946caf7a771429eb1745cf'
        )
Example #23
0
class BatchIssuer(Issuer):
    def __init__(self, netcode, issuing_address, certificates_to_issue,
                 connector, signer, batch_metadata, tx_cost_constants):
        Issuer.__init__(self, netcode, issuing_address, certificates_to_issue,
                        connector, signer)
        self.tree = MerkleTree(hash_f=sha256)
        self.batch_id = batch_metadata.batch_id
        self.batch_metadata = batch_metadata
        self.tx_cost_constants = tx_cost_constants

    def validate_schema(self):
        """
        Ensure certificates are valid v1.2 schema
        :return:
        """
        for _, certificate in self.certificates_to_issue.items():
            with open(certificate.signed_cert_file_name) as cert:
                cert_json = json.load(cert)
                validate_unsigned_v1_2(cert_json)

    def do_hash_certificate(self, certificate):
        """
        Hash the JSON-LD normalized certificate
        :param certificate:
        :return:
        """
        options = {
            'algorithm': 'URDNA2015',
            'format': 'application/nquads',
            'documentLoader': cached_document_loader
        }
        cert_utf8 = certificate.decode('utf-8')
        cert_json = json.loads(cert_utf8)
        normalized = jsonld.normalize(cert_json, options=options)
        hashed = sha256(normalized)
        self.tree.add_leaf(hashed, False)
        return hashed

    def calculate_cost_for_certificate_batch(self):
        """
        Per certificate, we pay 2*min_per_output (which is based on dust) + fee. Note assumes 1 input
        per tx.
        :return:
        """
        num_inputs = 1
        # output per recipient
        num_outputs = len(self.certificates_to_issue)
        # plus revocation outputs
        num_outputs += sum(1 for c in self.certificates_to_issue.values()
                           if c.revocation_key)
        # plus global revocation, change output, and OP_RETURN
        num_outputs += 3
        self.total = tx_utils.calculate_tx_total(self.tx_cost_constants,
                                                 num_inputs, num_outputs)
        return self.total

    def persist_tx(self, sent_tx_file_name, tx_id):
        Issuer.persist_tx(self, sent_tx_file_name, tx_id)
        # note that certificates are stored in an ordered dictionary, so we will iterate in the same order
        index = 0
        for uid, metadata in self.certificates_to_issue.items():
            receipt = self.tree.make_receipt(index, tx_id)

            with open(metadata.receipt_file_name, 'w') as out_file:
                out_file.write(json.dumps(receipt))

            with open(metadata.signed_cert_file_name, 'r') as in_file:
                signed_cert = json.load(in_file)

            blockchain_cert = {
                '@context': 'https://w3id.org/blockcerts/v1',
                'type': 'BlockchainCertificate',
                'document': signed_cert,
                'receipt': receipt
            }

            with open(metadata.blockchain_cert_file_name, 'w') as out_file:
                out_file.write(json.dumps(blockchain_cert))

            index += 1

    def create_transactions(self, revocation_address):
        """
        Create the batch Bitcoin transaction
        :param revocation_address:
        :return:
        """
        self.tree.make_tree()

        spendables = self.connector.get_unspent_outputs(self.issuing_address)
        if not spendables:
            error_message = 'No money to spend at address {}'.format(
                self.issuing_address)
            logging.error(error_message)
            raise InsufficientFundsError(error_message)

        last_input = spendables[-1]

        op_return_value = unhexlify(self.tree.get_merkle_root())

        tx_outs = self.build_recipient_tx_outs()
        tx_outs.append(
            tx_utils.create_transaction_output(
                revocation_address,
                self.tx_cost_constants.get_minimum_output_coin()))

        transaction = tx_utils.create_trx(op_return_value, self.total,
                                          self.issuing_address, tx_outs,
                                          last_input)

        transaction_data = TransactionData(
            uid=self.batch_id,
            tx=transaction,
            tx_input=last_input,
            op_return_value=hexlify(op_return_value),
            batch_metadata=self.batch_metadata)

        return [transaction_data]

    def build_recipient_tx_outs(self):
        """
        Creates 2 transaction outputs for each recipient: one to their public key and the other to their specific
        revocation key.
        :return:
        """
        tx_outs = []
        for _, certificate in self.certificates_to_issue.items():
            recipient_outs = [
                tx_utils.create_transaction_output(
                    certificate.public_key,
                    self.tx_cost_constants.get_minimum_output_coin())
            ]
            if certificate.revocation_key:
                recipient_outs.append(
                    tx_utils.create_transaction_output(
                        certificate.revocation_key,
                        self.tx_cost_constants.get_minimum_output_coin()))

            tx_outs = tx_outs + recipient_outs

        return tx_outs
Example #24
0
class BatchIssuer(Issuer):
    def __init__(self, config, certificates_to_issue):
        Issuer.__init__(self, config, certificates_to_issue)
        self.batch_id = '%024x' % random.randrange(16**24)
        self.tree = MerkleTree(hash_f=sha256)

    def validate_schema(self):
        """
        Ensure certificates are valid v1.2 schema
        :return:
        """
        for _, certificate in self.certificates_to_issue.items():
            with open(certificate.signed_certificate_file_name) as cert:
                cert_json = json.load(cert)
                schema_validator.validate_unsigned_v1_2(cert_json)

    def do_hash_certificate(self, certificate):
        """
        Hash the JSON-LD normalized certificate
        :param certificate:
        :return:
        """
        cert_utf8 = certificate.decode('utf-8')
        cert_json = json.loads(cert_utf8)
        normalized = jsonld.normalize(cert_json, {
            'algorithm': 'URDNA2015',
            'format': 'application/nquads'
        })
        hashed = sha256(normalized)
        self.tree.add_leaf(hashed, False)
        return hashed

    def get_cost_for_certificate_batch(self, allow_transfer):
        """
        Per certificate, we pay 2*min_per_output (which is based on dust) + fee. Note assumes 1 input
        per tx. We may also need to pay additional fees for splitting into temp addresses
        :param allow_transfer:
        :return:
        """
        num_certificates = len(self.certificates_to_issue)
        num_outputs = Issuer.get_num_outputs(num_certificates)
        return Issuer.get_cost_for_certificate_batch(num_outputs,
                                                     allow_transfer)

    def finish_tx(self, sent_tx_file_name, tx_id):
        Issuer.finish_tx(self, sent_tx_file_name, tx_id)
        # note that certificates are stored in an ordered dictionary, so we will iterate in the same order
        index = 0
        for uid, _ in self.certificates_to_issue.items():
            receipt = self.tree.make_receipt(index, tx_id)

            receipt_file_name = convert_file_name(
                self.config.receipts_file_pattern, uid)
            with open(receipt_file_name, 'w') as out_file:
                out_file.write(json.dumps(receipt))

            signed_cert_file_name = convert_file_name(
                self.config.signed_certs_file_pattern, uid)
            with open(signed_cert_file_name, 'r') as in_file:
                signed_cert = json.load(in_file)

            blockchain_cert = {
                '@context': 'https://w3id.org/blockcerts/v1',
                'type': 'BlockchainCertificate',
                'document': signed_cert,
                'receipt': receipt
            }
            blockchain_cert_file_name = convert_file_name(
                self.config.blockchain_certificates_file_pattern, uid)

            with open(blockchain_cert_file_name, 'w') as out_file:
                out_file.write(json.dumps(blockchain_cert))

            index += 1

    def create_transactions(self, revocation_address,
                            issuing_transaction_cost):
        """
        Create the batch Bitcoin transaction
        :param revocation_address:
        :param issuing_transaction_cost:
        :return:
        """
        self.tree.make_tree()

        spendables = get_unspent_outputs(self.issuing_address)
        if not spendables:
            error_message = 'No money to spend at address {}'.format(
                self.issuing_address)
            logging.error(error_message)
            raise InsufficientFundsError(error_message)

        last_input = spendables[-1]

        op_return_value = unhexlify(self.tree.get_merkle_root())

        tx_outs = self.build_recipient_tx_outs()
        tx_outs.append(
            trx_utils.create_transaction_output(
                revocation_address, issuing_transaction_cost.min_per_output))

        transaction = trx_utils.create_trx(op_return_value,
                                           issuing_transaction_cost,
                                           self.issuing_address, tx_outs,
                                           last_input)

        unsigned_tx_file_name = convert_file_name(
            self.config.unsigned_txs_file_pattern, self.batch_id)
        unsent_tx_file_name = convert_file_name(
            self.config.signed_txs_file_pattern, self.batch_id)
        sent_tx_file_name = convert_file_name(
            self.config.sent_txs_file_pattern, self.batch_id)

        transaction_data = TransactionData(
            uid=self.batch_id,
            tx=transaction,
            tx_input=last_input,
            op_return_value=hexlify(op_return_value),
            unsigned_tx_file_name=unsigned_tx_file_name,
            signed_tx_file_name=unsent_tx_file_name,
            sent_tx_file_name=sent_tx_file_name)

        return [transaction_data]

    def build_recipient_tx_outs(self):
        """
        Creates 2 transaction outputs for each recipient: one to their public key and the other to their specific
        revocation key.
        :return:
        """
        tx_outs = []
        for _, certificate in self.certificates_to_issue.items():
            tx_outs = tx_outs + trx_utils.create_recipient_outputs(
                certificate.public_key, certificate.revocation_key)

        return tx_outs
Example #25
0
class V1_2_Issuer(Issuer):
    def __init__(self, config, certificates_to_issue):
        Issuer.__init__(self, config, certificates_to_issue)
        self.batch_id = '%024x' % random.randrange(16**24)
        self.tree = MerkleTree(hash_f=sha256)

    def validate_schema(self):
        # ensure certificates are valid v1.2 schema
        for uid, certificate in self.certificates_to_issue.items():
            with open(certificate.signed_certificate_file_name) as cert:
                cert_json = json.load(cert)
                schema_validator.validate_unsigned_v1_2(cert_json)

    # TODO: duplicated with cert-verifier
    def do_hash_certificate(self, certificate):
        cert_utf8 = certificate.decode('utf-8')
        cert_json = json.loads(cert_utf8)
        normalized = jsonld.normalize(cert_json, {
            'algorithm': 'URDNA2015',
            'format': 'application/nquads'
        })
        hashed = sha256(normalized)
        self.tree.add_leaf(hashed, False)
        return hashed

    def get_cost_for_certificate_batch(self, dust_threshold,
                                       recommended_fee_per_transaction,
                                       satoshi_per_byte, allow_transfer):
        '''
        Per certificate, we pay 2*min_per_output (which is based on dust) + fee. Note assumes 1 input
        per tx. We may also need to pay additional fees for splitting into temp addresses
        '''
        num_certificates = len(self.certificates_to_issue)
        num_outputs = Issuer.get_num_outputs(num_certificates)
        return Issuer.get_cost_for_certificate_batch(
            dust_threshold, recommended_fee_per_transaction, satoshi_per_byte,
            num_outputs, allow_transfer, 1, 1)

    def finish_tx(self, sent_tx_file_name, txid):
        Issuer.finish_tx(self, sent_tx_file_name, txid)
        # note that certificates are stored in an ordered dictionary, so we will iterate in the same order
        index = 0
        for uid, certificate in self.certificates_to_issue.items():
            receipt = self.tree.make_receipt(index, txid)

            receipt_file_name = convert_file_name(
                self.config.receipts_file_pattern, uid)
            with open(receipt_file_name, 'w') as out_file:
                out_file.write(json.dumps(receipt))

            signed_cert_file_name = convert_file_name(
                self.config.signed_certs_file_pattern, uid)
            with open(signed_cert_file_name, 'r') as in_file:
                signed_cert = json.load(in_file)

            blockchain_cert = {
                '@context': 'https://w3id.org/blockcerts/v1',
                'type': 'BlockchainCertificate',
                'document': signed_cert,
                'receipt': receipt
            }
            blockchain_cert_file_name = convert_file_name(
                self.config.blockchain_certificates_file_pattern, uid)

            with open(blockchain_cert_file_name, 'w') as out_file:
                out_file.write(json.dumps(blockchain_cert))

            index += 1

    def create_transactions(self, wallet, revocation_address,
                            issuing_transaction_cost, split_input_trxs):
        # finish tree
        self.tree.make_tree()

        op_return_value = unhexlify(self.tree.get_merkle_root())

        unspent_outputs = wallet.get_unspent_outputs(self.issuing_address)
        last_output = unspent_outputs[-1]

        txouts = self.build_txouts(issuing_transaction_cost)
        txouts = txouts + [
            trx_utils.create_transaction_output(
                revocation_address, issuing_transaction_cost.min_per_output)
        ]

        tx = trx_utils.create_trx(op_return_value, issuing_transaction_cost,
                                  self.issuing_address, txouts, last_output)

        unsigned_tx_file_name = convert_file_name(
            self.config.unsigned_txs_file_pattern, self.batch_id)
        unsent_tx_file_name = convert_file_name(
            self.config.signed_txs_file_pattern, self.batch_id)
        sent_tx_file_name = convert_file_name(
            self.config.sent_txs_file_pattern, self.batch_id)

        td = TransactionData(uid=self.batch_id,
                             tx=tx,
                             tx_input=last_output,
                             op_return_value=hexlify(op_return_value),
                             unsigned_tx_file_name=unsigned_tx_file_name,
                             signed_tx_file_name=unsent_tx_file_name,
                             sent_tx_file_name=sent_tx_file_name)

        return [td]

    def build_txouts(self, issuing_transaction_cost):
        txouts = []
        for uid, certificate in self.certificates_to_issue.items():
            txouts = txouts + trx_utils.create_recipient_outputs(
                issuing_transaction_cost.min_per_output,
                certificate.public_key, certificate.revocation_key)

        return txouts
Example #26
0
 def test_make_tree_no_leaves(self):
     tree = MerkleTree()
     tree.make_tree()
     self.assertEqual(tree.get_merkle_root(), None,
                      'merkle root value should be null')
Example #27
0
 def test_make_tree_add_leaves_buffers(self):
     tree = MerkleTree()
     tree.add_leaves([self.bLeft, self.bRight])
     tree.make_tree()
     self.assertEqual(tree.get_merkle_root(), self.mRoot,
                      'merkle root value should be correct')