Exemplo n.º 1
0
def create_transaction(payload, private_key, public_key, inputs=None,
                        outputs=None, dependencies=None):
    addr = '000000' + hashlib.sha512(payload).hexdigest()[:64]

    if inputs is None:
        inputs = [addr]

    if outputs is None:
        outputs = [addr]

    if dependencies is None:
        dependencies = []

    header = transaction_pb2.TransactionHeader(
        signer_pubkey=public_key,
        family_name='scheduler_test',
        family_version='1.0',
        inputs=inputs,
        outputs=outputs,
        dependencies=dependencies,
        nonce=str(time.time()),
        payload_encoding="application/cbor",
        payload_sha512=hashlib.sha512(payload).hexdigest(),
        batcher_pubkey=public_key)

    header_bytes = header.SerializeToString()

    signature = signing.sign(header_bytes, private_key)

    transaction = transaction_pb2.Transaction(
        header=header_bytes,
        payload=payload,
        header_signature=signature)

    return transaction, header
Exemplo n.º 2
0
    def do_batch_by_transaction_id_multiple_txn_ids():

        transaction = transaction_pb2.Transaction(header_signature="123")
        batch = batch_pb2.Batch(header_signature="abc",
                                transactions=[transaction])
        testResponder.completer.add_batch(batch)
        # Request transactions 123 and 456
        message = network_pb2.GossipBatchByTransactionIdRequest(
            ids=["123", "456"], time_to_live=1)
        testResponder.batch_by_txn_request_handler.handle(
            "Connection_1", message.SerializeToString())
        testResponder.batch_request_handler.handle("Connection_1",
                                                   message.SerializeToString())

        # Respond with a BatchResponse for transaction 123
        testResponder.assert_message_sent(
            connection_id="Connection_1",
            message_type=validator_pb2.Message.GOSSIP_BATCH_RESPONSE)

        # Broadcast a BatchByTransactionIdRequest for just 456
        after_message = \
            network_pb2.GossipBatchByTransactionIdRequest(
                ids=["456"],
                time_to_live=0)

        testResponder.assert_message_was_broadcasted(
            after_message,
            validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST)

        # And set a pending request for 456
        testResponder.assert_request_pending(requested_id="456",
                                             connection_id="Connection_1")
Exemplo n.º 3
0
def create_transaction(name, private_key, public_key):
    payload = name
    addr = '000000' + hashlib.sha512(name.encode()).hexdigest()

    header = transaction_pb2.TransactionHeader(
        signer_pubkey=public_key,
        family_name='scheduler_test',
        family_version='1.0',
        inputs=[addr],
        outputs=[addr],
        dependencies=[],
        payload_encoding="application/cbor",
        payload_sha512=hashlib.sha512(payload.encode()).hexdigest(),
        batcher_pubkey=public_key)

    header_bytes = header.SerializeToString()

    signature = bitcoin.ecdsa_sign(
        header_bytes,
        private_key)

    transaction = transaction_pb2.Transaction(
        header=header_bytes,
        payload=payload.encode(),
        header_signature=signature)

    return transaction
Exemplo n.º 4
0
    def do_responder_batch_response_txn_handler():
        transaction = transaction_pb2.Transaction(header_signature="123")
        batch = batch_pb2.Batch(header_signature="abc",
                                transactions=[transaction])

        response_message = network_pb2.GossipBatchResponse(
            content=batch.SerializeToString())

        request_message = \
            network_pb2.GossipBatchByTransactionIdRequest(
                ids=["123"],
                time_to_live=1)

        # Send BatchByTransaciontIdRequest for txn "123" and add it to the
        # pending request cache
        testResponder.batch_request_handler.handle(
            "Connection_2", request_message.SerializeToString())

        testResponder.assert_request_pending(requested_id="123",
                                             connection_id="Connection_2")

        # Send Batch Response that contains the batch that has txn "123"
        testResponder.batch_response_handler.handle(
            "Connection_1", (batch, response_message.SerializeToString()))

        # Handle the the BatchResponse Message. Since Connection_2 had
        # requested the txn_id in the batch but it could not be fulfilled at
        # that time of the request the received BatchResponse is forwarded to
        # Connection_2
        testResponder.assert_message_sent(
            connection_id="Connection_2",
            message_type=validator_pb2.Message.GOSSIP_BATCH_RESPONSE)
        # The request for transaction_id "123" from "Connection_2" is no
        # longer pending it should be removed from the pending request cache.
        testResponder.assert_request_not_pending(requested_id="123")
Exemplo n.º 5
0
    def do_batch_by_transaction_id_response_handler():
        before_message = network_pb2.GossipBatchByTransactionIdRequest(
            ids=["123"], nonce="1", time_to_live=1)

        after_message = network_pb2.GossipBatchByTransactionIdRequest(
            ids=["123"], nonce="1", time_to_live=0)

        testResponder.batch_by_txn_request_handler.handle(
            "Connection_1", before_message.SerializeToString())

        # If we cannot respond to the request, broadcast batch request and add
        # to pending request
        testResponder.assert_message_was_broadcasted(
            after_message,
            validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST)
        testResponder.assert_request_pending(requested_id="123",
                                             connection_id="Connection_1")
        testResponder.assert_message_not_sent(connection_id="Connection_1")

        # Add the batch to the completer and resend the
        # BatchByTransactionIdRequest
        message = network_pb2.GossipBatchByTransactionIdRequest(ids=["123"],
                                                                nonce="2",
                                                                time_to_live=1)
        transaction = transaction_pb2.Transaction(header_signature="123")
        batch = batch_pb2.Batch(header_signature="abc",
                                transactions=[transaction])
        testResponder.completer.add_batch(batch)
        testResponder.batch_request_handler.handle("Connection_1",
                                                   message.SerializeToString())

        # Check that the a Batch Response was sent back to "Connection_1"
        testResponder.assert_message_sent(
            connection_id="Connection_1",
            message_type=validator_pb2.Message.GOSSIP_BATCH_RESPONSE)
Exemplo n.º 6
0
    def test_batch_by_transaction_id_response_handler(self):
        """
        Test that the BatchByTransactionIdResponderHandler correctly broadcasts
        a received request that the Responder cannot respond to, or sends a
        GossipBatchResponse back to the connection_id the handler received
        the request from.
        """
        # The completer does not have the requested batch with the transaction
        message = network_pb2.GossipBatchByTransactionIdRequest(ids=["123"],
                                                                node_id=b"1")
        self.batch_by_txn_request_handler.handle("Connection_1",
                                                 message.SerializeToString())

        # If we cannot respond to the request, broadcast batch request and add
        # to pending request
        self.assert_message_was_broadcasted(
            message,
            validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST)
        self.assert_request_pending(requested_id="123",
                                    connection_id="Connection_1")
        self.assert_message_not_sent(connection_id="Connection_1")

        # Add the batch to the completer and resend the
        # BatchByTransactionIdRequest
        transaction = transaction_pb2.Transaction(header_signature="123")
        batch = batch_pb2.Batch(header_signature="abc",
                                transactions=[transaction])
        self.completer.add_batch(batch)
        self.batch_request_handler.handle("Connection_1",
                                          message.SerializeToString())

        # Check that the a Batch Response was sent back to "Connection_1"
        self.assert_message_sent(
            connection_id="Connection_1",
            message_type=validator_pb2.Message.GOSSIP_BATCH_RESPONSE)
Exemplo n.º 7
0
def test_responder_batch_response_txn_handler():
    """
    Test that the ResponderBatchResponseHandler, after receiving a Batch
    Response, checks to see if the responder has any pending request for
    that transactions in the batch and forwards the response on to the
    connection_id that had them.
    """

    testResponder = TestResponder()

    transaction = transaction_pb2.Transaction(header_signature="123")
    batch = batch_pb2.Batch(header_signature="abc", transactions=[transaction])

    response_message = network_pb2.GossipBatchResponse(
        content=batch.SerializeToString())

    request_message = \
        network_pb2.GossipBatchByTransactionIdRequest(
            ids=["123"],
            time_to_live=1)

    # Send BatchByTransaciontIdRequest for txn "123" and add it to the
    # pending request cache
    testResponder.batch_request_handler.handle(
        "Connection_2", request_message.SerializeToString())

    # Send Batch Response that contains the batch that has txn "123"
    testResponder.batch_response_handler.handle(
        "Connection_1", (batch, response_message.SerializeToString()))
Exemplo n.º 8
0
def test_batch_by_transaction_id_multiple_txn_ids():
    """
    Test that the BatchByTransactionIdResponderHandler correctly broadcasts
    a new request with only the transaction_ids that the Responder cannot
    respond to, and sends a GossipBatchResponse for the transactions_id
    requests that can be satisfied.
    """
    # Add batch that has txn 123
    testResponder = TestResponder()

    transaction = transaction_pb2.Transaction(header_signature="123")
    batch = batch_pb2.Batch(header_signature="abc", transactions=[transaction])
    testResponder.completer.add_batch(batch)
    # Request transactions 123 and 456
    message = network_pb2.GossipBatchByTransactionIdRequest(ids=["123", "456"],
                                                            time_to_live=1)
    testResponder.batch_by_txn_request_handler.handle(
        "Connection_1", message.SerializeToString())
    testResponder.batch_request_handler.handle("Connection_1",
                                               message.SerializeToString())

    # Respond with a BatchResponse for transaction 123

    # Broadcast a BatchByTransactionIdRequest for just 456
    after_message = \
        network_pb2.GossipBatchByTransactionIdRequest(
            ids=["456"],
            time_to_live=0)
Exemplo n.º 9
0
def test_batch_by_transaction_id_response_handler():
    """
    Test that the BatchByTransactionIdResponderHandler correctly broadcasts
    a received request that the Responder cannot respond to, or sends a
    GossipBatchResponse back to the connection_id the handler received
    the request from.
    """
    # The completer does not have the requested batch with the transaction
    testResponder = TestResponder()

    before_message = network_pb2.GossipBatchByTransactionIdRequest(
        ids=["123"], nonce="1", time_to_live=1)

    after_message = network_pb2.GossipBatchByTransactionIdRequest(
        ids=["123"], nonce="1", time_to_live=0)

    testResponder.batch_by_txn_request_handler.handle(
        "Connection_1", before_message.SerializeToString())

    # If we cannot respond to the request, broadcast batch request and add
    # to pending request
    # BatchByTransactionIdRequest
    message = network_pb2.GossipBatchByTransactionIdRequest(ids=["123"],
                                                            nonce="2",
                                                            time_to_live=1)
    transaction = transaction_pb2.Transaction(header_signature="123")
    batch = batch_pb2.Batch(header_signature="abc", transactions=[transaction])
    testResponder.completer.add_batch(batch)
    testResponder.batch_request_handler.handle("Connection_1",
                                               message.SerializeToString())
Exemplo n.º 10
0
    def test_responder_batch_response_txn_handler(self):
        """
        Test that the ResponderBatchResponseHandler, after receiving a Batch
        Response, checks to see if the responder has any pending request for
        that transactions in the batch and forwards the response on to the
        connection_id that had them.
        """
        transaction = transaction_pb2.Transaction(header_signature="123")
        batch = batch_pb2.Batch(
            header_signature="abc", transactions=[transaction])

        response_message = network_pb2.GossipBatchResponse(
            content=batch.SerializeToString())

        request_message = \
            network_pb2.GossipBatchByTransactionIdRequest(
                ids=["123"])

        # Send BatchByTransaciontIdRequest for txn "123" and add it to the
        # pending request cache
        self.batch_request_handler.handle(
            "Connection_2", request_message.SerializeToString())

        self.assert_request_pending(
            requested_id="123", connection_id="Connection_2")

        # Send Batch Response that contains the batch that has txn "123"
        self.batch_response_handler.handle(
            "Connection_1", response_message.SerializeToString())

        # Handle the the BatchResponse Message. Since Connection_2 had
        # requested the txn_id in the batch but it could not be fulfilled at
        # that time of the request the received BatchResponse is forwarded to
        # Connection_2
        self.assert_message_sent(
            connection_id="Connection_2",
            message_type=validator_pb2.Message.GOSSIP_BATCH_RESPONSE
            )
        # The request for transaction_id "123" from "Connection_2" is no
        # longer pending it should be removed from the pending request cache.
        self.assert_request_not_pending(requested_id="123")
Exemplo n.º 11
0
    def test_batch_by_transaction_id_multiple_txn_ids(self):
        """
        Test that the BatchByTransactionIdResponderHandler correctly broadcasts
        a new request with only the transaction_ids that the Responder cannot
        respond to, and sends a GossipBatchResponse for the transactions_id
        requests that can be satisfied.
        """
        # Add batch that has txn 123
        transaction = transaction_pb2.Transaction(header_signature="123")
        batch = batch_pb2.Batch(header_signature="abc",
                                transactions=[transaction])
        self.completer.add_batch(batch)
        # Request transactions 123 and 456
        message = network_pb2.GossipBatchByTransactionIdRequest(
            ids=["123", "456"], time_to_live=1)
        self.batch_by_txn_request_handler.handle("Connection_1",
                                                 message.SerializeToString())
        self.batch_request_handler.handle("Connection_1",
                                          message.SerializeToString())

        # Respond with a BatchResponse for transaction 123
        self.assert_message_sent(
            connection_id="Connection_1",
            message_type=validator_pb2.Message.GOSSIP_BATCH_RESPONSE)

        # Broadcast a BatchByTransactionIdRequest for just 456
        after_message = \
            network_pb2.GossipBatchByTransactionIdRequest(
                ids=["456"],
                time_to_live=0)

        self.assert_message_was_broadcasted(
            after_message,
            validator_pb2.Message.GOSSIP_BATCH_BY_TRANSACTION_ID_REQUEST)

        # And set a pending request for 456
        self.assert_request_pending(requested_id="456",
                                    connection_id="Connection_1")
Exemplo n.º 12
0
    def _register_signup_information(self, block_header, poet_enclave_module):
        # Create signup information for this validator, putting the block ID
        # of the block previous to the block referenced by block_header in the
        # nonce.  Block ID is better than wait certificate ID for testing
        # freshness as we need to account for non-PoET blocks.
        public_key_hash = \
            hashlib.sha256(
                block_header.signer_pubkey.encode()).hexdigest()
        nonce = SignupInfo.block_id_to_nonce(block_header.previous_block_id)
        signup_info = \
            SignupInfo.create_signup_info(
                poet_enclave_module=poet_enclave_module,
                originator_public_key_hash=public_key_hash,
                nonce=nonce)

        # Create the validator registry payload
        payload = \
            vr_pb.ValidatorRegistryPayload(
                verb='register',
                name='validator-{}'.format(block_header.signer_pubkey[:8]),
                id=block_header.signer_pubkey,
                signup_info=vr_pb.SignUpInfo(
                    poet_public_key=signup_info.poet_public_key,
                    proof_data=signup_info.proof_data,
                    anti_sybil_id=signup_info.anti_sybil_id,
                    nonce=nonce),
            )
        serialized = payload.SerializeToString()

        # Create the address that will be used to look up this validator
        # registry transaction.  Seems like a potential for refactoring..
        validator_entry_address = \
            PoetBlockPublisher._validator_registry_namespace + \
            hashlib.sha256(block_header.signer_pubkey.encode()).hexdigest()

        # Create a transaction header and transaction for the validator
        # registry update amd then hand it off to the batch publisher to
        # send out.
        output_addresses = \
            [validator_entry_address,
             PoetBlockPublisher._validator_map_address]
        input_addresses = \
            output_addresses + \
            [SettingsView.setting_address(
                'sawtooth.poet.report_public_key_pem'),
             SettingsView.setting_address(
                 'sawtooth.poet.valid_enclave_measurements'),
             SettingsView.setting_address(
                 'sawtooth.poet.valid_enclave_basenames')]

        header = \
            txn_pb.TransactionHeader(
                signer_pubkey=block_header.signer_pubkey,
                family_name='sawtooth_validator_registry',
                family_version='1.0',
                inputs=input_addresses,
                outputs=output_addresses,
                dependencies=[],
                payload_sha512=hashlib.sha512(serialized).hexdigest(),
                batcher_pubkey=block_header.signer_pubkey,
                nonce=time.time().hex().encode()).SerializeToString()
        signature = \
            signing.sign(header, self._batch_publisher.identity_signing_key)

        transaction = \
            txn_pb.Transaction(
                header=header,
                payload=serialized,
                header_signature=signature)

        LOGGER.info(
            'Register Validator Name=%s, ID=%s...%s, PoET public key=%s...%s, '
            'Nonce=%s',
            payload.name,
            payload.id[:8],
            payload.id[-8:],
            payload.signup_info.poet_public_key[:8],
            payload.signup_info.poet_public_key[-8:],
            nonce)

        self._batch_publisher.send([transaction])

        # Store the key state so that we can look it up later if need be and
        # set the new key as our active key
        LOGGER.info(
            'Save key state PPK=%s...%s => SSD=%s...%s',
            signup_info.poet_public_key[:8],
            signup_info.poet_public_key[-8:],
            signup_info.sealed_signup_data[:8],
            signup_info.sealed_signup_data[-8:])
        self._poet_key_state_store[signup_info.poet_public_key] = \
            PoetKeyState(
                sealed_signup_data=signup_info.sealed_signup_data,
                has_been_refreshed=False)
        self._poet_key_state_store.active_key = signup_info.poet_public_key
Exemplo n.º 13
0
def do_create(args):
    """Executes the `poet registration` subcommand.

    This command generates a validator registry transaction and saves it to a
    file, whose location is determined by the args.  The signup data, generated
    by the selected enclave, is also stored in a well-known location.
    """
    signer = _read_signer(args.key)
    public_key = signer.get_public_key().as_hex()

    public_key_hash = sha256(public_key.encode()).hexdigest()

    with PoetEnclaveModuleWrapper(
            enclave_module=args.enclave_module,
            config_dir=config.get_config_dir(),
            data_dir=config.get_data_dir()) as poet_enclave_module:
        signup_info = SignupInfo.create_signup_info(
            poet_enclave_module=poet_enclave_module,
            originator_public_key_hash=public_key_hash,
            nonce=SignupInfo.block_id_to_nonce(args.block))

    print('Writing key state for PoET public key: {}...{}'.format(
        signup_info.poet_public_key[:8], signup_info.poet_public_key[-8:]))

    # Store the newly-created PoET key state, associating it with its
    # corresponding public key
    poet_key_state_store = \
        PoetKeyStateStore(
            data_dir=config.get_data_dir(),
            validator_id=public_key)
    poet_key_state_store[signup_info.poet_public_key] = \
        PoetKeyState(
            sealed_signup_data=signup_info.sealed_signup_data,
            has_been_refreshed=False)

    # Create the validator registry payload
    payload = \
        vr_pb.ValidatorRegistryPayload(
            verb='register',
            name='validator-{}'.format(public_key[:8]),
            id=public_key,
            signup_info=vr_pb.SignUpInfo(
                poet_public_key=signup_info.poet_public_key,
                proof_data=signup_info.proof_data,
                anti_sybil_id=signup_info.anti_sybil_id,
                nonce=SignupInfo.block_id_to_nonce(args.block)))
    serialized = payload.SerializeToString()

    # Create the address that will be used to look up this validator
    # registry transaction.  Seems like a potential for refactoring..
    validator_entry_address = \
        VR_NAMESPACE + sha256(public_key.encode()).hexdigest()

    # Create a transaction header and transaction for the validator
    # registry update amd then hand it off to the batch publisher to
    # send out.
    output_addresses = [validator_entry_address, VALIDATOR_MAP_ADDRESS]
    input_addresses = \
        output_addresses + \
        [SettingsView.setting_address('sawtooth.poet.report_public_key_pem'),
         SettingsView.setting_address('sawtooth.poet.'
                                      'valid_enclave_measurements'),
         SettingsView.setting_address('sawtooth.poet.valid_enclave_basenames')]

    header = \
        txn_pb.TransactionHeader(
            signer_public_key=public_key,
            family_name='sawtooth_validator_registry',
            family_version='1.0',
            inputs=input_addresses,
            outputs=output_addresses,
            dependencies=[],
            payload_sha512=sha512(serialized).hexdigest(),
            batcher_public_key=public_key,
            nonce=time.time().hex().encode()).SerializeToString()
    signature = signer.sign(header)

    transaction = \
        txn_pb.Transaction(
            header=header,
            payload=serialized,
            header_signature=signature)

    batch = _create_batch(signer, [transaction])
    batch_list = batch_pb.BatchList(batches=[batch])
    try:
        print('Generating {}'.format(args.output))
        with open(args.output, 'wb') as batch_file:
            batch_file.write(batch_list.SerializeToString())
    except IOError as e:
        raise CliException('Unable to write to batch file: {}'.format(str(e)))
Exemplo n.º 14
0
def make_batch(batch_id, txn_id):
    transaction = transaction_pb2.Transaction(header_signature=txn_id)
    batch = batch_pb2.Batch(header_signature=batch_id,
                            transactions=[transaction])

    return batch
Exemplo n.º 15
0
def do_genesis(args):
    """Executes the `poet genesis` subcommand.

    This command generates a validator registry transaction and saves it to a
    file, whose location is determined by the args.  The signup data, generated
    by the selected enclave, is also stored in a well-known location.
    """
    if args.enclave_module == 'simulator':
        module_name = SIMUATOR_MODULE
    elif args.enclave_module == 'sgx':
        module_name = SGX_MODULE
    else:
        raise AssertionError('Unknown enclave module: {}'.format(
            args.enclave_module))

    try:
        poet_enclave_module = importlib.import_module(module_name)
    except ImportError as e:
        raise AssertionError(str(e))

    poet_enclave_module.initialize(**{})

    pubkey, signing_key = _read_signing_keys(args.key)

    public_key_hash = sha256(pubkey.encode()).hexdigest()
    signup_info = SignupInfo.create_signup_info(
        poet_enclave_module=poet_enclave_module,
        validator_address=pubkey,
        originator_public_key_hash=public_key_hash,
        nonce=NULL_BLOCK_IDENTIFIER)

    print('Writing key state for PoET public key: {}...{}'.format(
        signup_info.poet_public_key[:8], signup_info.poet_public_key[-8:]))

    # Store the newly-created PoET key state, associating it with its
    # corresponding public key
    poet_key_state_store = \
        PoetKeyStateStore(
            data_dir=config.get_data_dir(),
            validator_id=pubkey)
    poet_key_state_store[signup_info.poet_public_key] = \
        PoetKeyState(
            sealed_signup_data=signup_info.sealed_signup_data,
            has_been_refreshed=False)

    # Create the validator registry payload
    payload = \
        vr_pb.ValidatorRegistryPayload(
            verb='register',
            name='validator-{}'.format(pubkey[:8]),
            id=pubkey,
            signup_info=vr_pb.SignUpInfo(
                poet_public_key=signup_info.poet_public_key,
                proof_data=signup_info.proof_data,
                anti_sybil_id=signup_info.anti_sybil_id,
                nonce=NULL_BLOCK_IDENTIFIER))
    serialized = payload.SerializeToString()

    # Create the address that will be used to look up this validator
    # registry transaction.  Seems like a potential for refactoring..
    validator_entry_address = \
        VR_NAMESPACE + sha256(pubkey.encode()).hexdigest()

    # Create a transaction header and transaction for the validator
    # registry update amd then hand it off to the batch publisher to
    # send out.
    addresses = [validator_entry_address, VALIDATOR_MAP_ADDRESS]

    header = \
        txn_pb.TransactionHeader(
            signer_pubkey=pubkey,
            family_name='sawtooth_validator_registry',
            family_version='1.0',
            inputs=addresses,
            outputs=addresses,
            dependencies=[],
            payload_encoding="application/protobuf",
            payload_sha512=sha512(serialized).hexdigest(),
            batcher_pubkey=pubkey,
            nonce=time.time().hex().encode()).SerializeToString()
    signature = signing.sign(header, signing_key)

    transaction = \
        txn_pb.Transaction(
            header=header,
            payload=serialized,
            header_signature=signature)

    batch = _create_batch(pubkey, signing_key, [transaction])
    batch_list = batch_pb.BatchList(batches=[batch])
    try:
        print('Generating {}'.format(args.output))
        with open(args.output, 'wb') as batch_file:
            batch_file.write(batch_list.SerializeToString())
    except IOError as e:
        raise CliException('Unable to write to batch file: {}'.format(str(e)))
Exemplo n.º 16
0
    def _register_signup_information(self, block_header):
        # Find the most-recent block in the block cache, if such a block
        # exists, and get its wait certificate ID
        wait_certificate_id = NULL_BLOCK_IDENTIFIER
        most_recent_block = self._block_cache.block_store.chain_head
        if most_recent_block is not None:
            wait_certificate_id = \
                utils.deserialize_wait_certificate(
                    block=most_recent_block,
                    poet_enclave_module=self._poet_enclave_module).identifier

        # Create signup information for this validator
        public_key_hash = \
            hashlib.sha256(
                block_header.signer_pubkey.encode()).hexdigest()
        signup_info = \
            SignupInfo.create_signup_info(
                poet_enclave_module=self._poet_enclave_module,
                validator_address=block_header.signer_pubkey,
                originator_public_key_hash=public_key_hash,
                most_recent_wait_certificate_id=wait_certificate_id)

        # Create the validator registry payload
        payload = \
            vr_pb.ValidatorRegistryPayload(
                verb='register',
                name='validator-{}'.format(block_header.signer_pubkey[-8:]),
                id=block_header.signer_pubkey,
                signup_info=vr_pb.SignUpInfo(
                    poet_public_key=signup_info.poet_public_key,
                    proof_data=signup_info.proof_data,
                    anti_sybil_id=signup_info.anti_sybil_id),
                block_num=block_header.block_num)
        serialized = payload.SerializeToString()

        # Create the address that will be used to look up this validator
        # registry transaction.  Seems like a potential for refactoring..
        validator_entry_address = \
            PoetBlockPublisher._validator_registry_namespace + \
            hashlib.sha256(block_header.signer_pubkey.encode()).hexdigest()

        # Create a transaction header and transaction for the validator
        # registry update amd then hand it off to the batch publisher to
        # send out.
        addresses = \
            [validator_entry_address,
             PoetBlockPublisher._validator_map_address]

        header = \
            txn_pb.TransactionHeader(
                signer_pubkey=block_header.signer_pubkey,
                family_name='sawtooth_validator_registry',
                family_version='1.0',
                inputs=addresses,
                outputs=addresses,
                dependencies=[],
                payload_encoding="application/protobuf",
                payload_sha512=hashlib.sha512(serialized).hexdigest(),
                batcher_pubkey=block_header.signer_pubkey,
                nonce=time.time().hex().encode()).SerializeToString()
        signature = \
            signing.sign(header, self._batch_publisher.identity_signing_key)

        transaction = \
            txn_pb.Transaction(
                header=header,
                payload=serialized,
                header_signature=signature)

        self._batch_publisher.send([transaction])

        LOGGER.info(
            'Register Validator Name=%s, ID=%s...%s, PoET public key=%s...%s',
            payload.name, payload.id[:8], payload.id[-8:],
            payload.signup_info.poet_public_key[:8],
            payload.signup_info.poet_public_key[-8:])

        # HACER: Once we have the consensus state implemented, we can
        # store this information in there.  For now, we will store in the
        # class so it persists for the lifetime of the validator.
        PoetBlockPublisher._sealed_signup_data = \
            signup_info.sealed_signup_data
        PoetBlockPublisher._poet_public_key = signup_info.poet_public_key