def test_create_wait_certificate_before_wait_timer_expires(self):
        # Need to create signup information
        SignupInfo.create_signup_info(
            validator_address='1660 Pennsylvania Avenue NW',
            originator_public_key_hash=self._originator_public_key_hash,
            most_recent_wait_certificate_id=NULL_BLOCK_IDENTIFIER)

        # Create a wait certificate for the genesis block so that we can
        # create another wait certificate that has to play by the rules.
        wt = \
            WaitTimer.create_wait_timer(
                validator_address='1660 Pennsylvania Avenue NW',
                certificates=[])
        wc = \
            WaitCertificate.create_wait_certificate(
                wait_timer=wt,
                block_hash="Reader's Digest")

        wt = \
            WaitTimer.create_wait_timer(
                validator_address='1660 Pennsylvania Avenue NW',
                certificates=[wc])
        with self.assertRaises(ValueError):
            WaitCertificate.create_wait_certificate(
                wait_timer=wt, block_hash="Reader's Digest")
    def test_create_wait_certificate_with_wrong_wait_timer(self):
        # Need to create signup information
        SignupInfo.create_signup_info(
            validator_address='1660 Pennsylvania Avenue NW',
            originator_public_key_hash=self._originator_public_key_hash,
            most_recent_wait_certificate_id=NULL_BLOCK_IDENTIFIER)

        # Create two timers and try to create the wait certificate with the
        # first one, which should fail as it is not the current wait timer
        invalid_wt = \
            WaitTimer.create_wait_timer(
                validator_address='1660 Pennsylvania Avenue NW',
                certificates=[])
        valid_wt = \
            WaitTimer.create_wait_timer(
                validator_address='1660 Pennsylvania Avenue NW',
                certificates=[])

        # Verify that we cannot create a wait certificate with the old wait
        # timer, but we can with the new one
        with self.assertRaises(ValueError):
            WaitCertificate.create_wait_certificate(
                wait_timer=invalid_wt, block_hash="Reader's Digest")

        WaitCertificate.create_wait_certificate(wait_timer=valid_wt,
                                                block_hash="Reader's Digest")
    def test_create_wait_certificate_after_wait_timer_timed_out(self):
        # Need to create signup information
        SignupInfo.create_signup_info(
            validator_address='1660 Pennsylvania Avenue NW',
            originator_public_key_hash=self._originator_public_key_hash,
            most_recent_wait_certificate_id=NULL_BLOCK_IDENTIFIER)

        # Create a wait certificate for the genesis block so that we can
        # create another wait certificate that has to play by the rules.
        wt = \
            WaitTimer.create_wait_timer(
                validator_address='1660 Pennsylvania Avenue NW',
                certificates=[])
        wc = \
            WaitCertificate.create_wait_certificate(
                wait_timer=wt,
                block_hash="Reader's Digest")

        wt = \
            WaitTimer.create_wait_timer(
                validator_address='1660 Pennsylvania Avenue NW',
                certificates=[wc])
        while not wt.has_expired(time.time()):
            time.sleep(1)
        time.sleep(WaitTimer.poet_enclave.TIMER_TIMEOUT_PERIOD + 1)

        with self.assertRaises(ValueError):
            WaitCertificate.create_wait_certificate(
                wait_timer=wt, block_hash="Reader's Digest")
    def test_create_wait_certificate(self):
        # Need to create signup information and wait timer first
        signup_info = \
            SignupInfo.create_signup_info(
                validator_address='1660 Pennsylvania Avenue NW',
                originator_public_key_hash=self._originator_public_key_hash,
                most_recent_wait_certificate_id=NULL_BLOCK_IDENTIFIER)

        wt = \
            WaitTimer.create_wait_timer(
                validator_address='1660 Pennsylvania Avenue NW',
                certificates=[])
        while not wt.has_expired(time.time()):
            time.sleep(1)

        # Now we can create a wait certificate and verify that it correlates
        # to the wait timer we just created
        wc = \
            WaitCertificate.create_wait_certificate(
                wait_timer=wt,
                block_hash="Reader's Digest")

        self.assertIsNotNone(wc)

        self.assertEqual(wc.previous_certificate_id,
                         wt.previous_certificate_id)
        self.assertAlmostEqual(wc.local_mean, wt.local_mean)
        self.assertAlmostEqual(wc.request_time, wt.request_time)
        self.assertAlmostEqual(wc.duration, wt.duration)
        self.assertEqual(wc.validator_address, wt.validator_address)
        self.assertEqual(wc.block_hash, "Reader's Digest")
        self.assertIsNotNone(wc.signature)
        self.assertIsNotNone(wc.identifier)

        # A newly-created wait certificate should be valid
        wc.check_valid([], signup_info.poet_public_key)

        # Create another wait certificate and verify it is valid also
        wt = \
            WaitTimer.create_wait_timer(
                validator_address='1660 Pennsylvania Avenue NW',
                certificates=[wc])
        while not wt.has_expired(time.time()):
            time.sleep(1)

        # Now we can create a wait certificate and verify that it correlates
        # to the wait timer we just created
        another_wc = \
            WaitCertificate.create_wait_certificate(
                wait_timer=wt,
                block_hash="Pepto Bismol")

        another_wc.check_valid([wc], signup_info.poet_public_key)
    def test_create_wait_certificate_with_reused_wait_timer(self):
        # Need to create signup information
        SignupInfo.create_signup_info(
            poet_enclave_module=self.poet_enclave_module,
            validator_address='1660 Pennsylvania Avenue NW',
            originator_public_key_hash=self._originator_public_key_hash,
            most_recent_wait_certificate_id=NULL_BLOCK_IDENTIFIER)

        # Create a wait certificate for the genesis block so that we can
        # create another wait certificate that has to play by the rules.
        wt = \
            WaitTimer.create_wait_timer(
                poet_enclave_module=self.poet_enclave_module,
                validator_address='1660 Pennsylvania Avenue NW',
                certificates=[])
        wc = \
            WaitCertificate.create_wait_certificate(
                poet_enclave_module=self.poet_enclave_module,
                wait_timer=wt,
                block_hash="Reader's Digest")

        consumed_wt = wt

        # Verify that we cannot use the consumed wait timer to create a wait
        # certificate either before or after creating a new wait timer
        with self.assertRaises(ValueError):
            WaitCertificate.create_wait_certificate(
                poet_enclave_module=self.poet_enclave_module,
                wait_timer=consumed_wt,
                block_hash="Reader's Digest")
        wt = \
            WaitTimer.create_wait_timer(
                poet_enclave_module=self.poet_enclave_module,
                validator_address='1660 Pennsylvania Avenue NW',
                certificates=[wc])
        with self.assertRaises(ValueError):
            WaitCertificate.create_wait_certificate(
                poet_enclave_module=self.poet_enclave_module,
                wait_timer=consumed_wt,
                block_hash="Reader's Digest")

        # Verify that once the new timer expires, we can create a wait
        # certificate with it
        while not wt.has_expired(time.time()):
            time.sleep(1)

        WaitCertificate.create_wait_certificate(
            poet_enclave_module=self.poet_enclave_module,
            wait_timer=wt,
            block_hash="Reader's Digest")
    def initialize_block(self, block_header):
        """Do initialization necessary for the consensus to claim a block,
        this may include initiating voting activities, starting proof of work
        hash generation, or create a PoET wait timer.

        Args:
            block_header (BlockHeader): The BlockHeader to initialize.
        Returns:
            Boolean: True if the candidate block should be built. False if
            no candidate should be built.
        """
        # HACER: Once we have PoET consensus state, we should be looking in
        # there for the sealed signup data.  For the time being, signup
        # information will be tied to the lifetime of the validator.

        # HACER: Once the genesis utility is creating the validator registry
        # transaction, we no longer need to do this.  But for now, if we are
        # creating the genesis block, we need to re-establish the state of
        # the enclave using pre-canned sealed signup data so that we can
        # create the wait timer and certificate.  Note that this is only
        # used for the genesis block.  Going forward after the genesis block,
        # all validators will use generated signup information.
        if utils.block_id_is_genesis(block_header.previous_block_id):
            LOGGER.debug(
                'Creating genesis block, so will use sealed signup data')
            SignupInfo.unseal_signup_data(
                poet_enclave_module=self._poet_enclave_module,
                validator_address=block_header.signer_pubkey,
                sealed_signup_data=PoetBlockPublisher._sealed_signup_data)

        # HACER: Otherwise, if it is not the first block and we don't already
        # have a public key, we need to create signup information and create a
        # transaction to add it to the validator registry.
        elif PoetBlockPublisher._poet_public_key is None:
            self._register_signup_information(block_header=block_header)

        # Create a list of certificates for the wait timer.  This seems to have
        # a little too much knowledge of the WaitTimer implementation, but
        # there is no use getting more than
        # WaitTimer.certificate_sample_length wait certificates.
        certificates = \
            utils.build_certificate_list(
                block_header=block_header,
                block_cache=self._block_cache,
                poet_enclave_module=self._poet_enclave_module,
                maximum_number=WaitTimer.certificate_sample_length)

        # We need to create a wait timer for the block...this is what we
        # will check when we are asked if it is time to publish the block
        self._wait_timer = \
            WaitTimer.create_wait_timer(
                poet_enclave_module=self._poet_enclave_module,
                validator_address=block_header.signer_pubkey,
                certificates=list(certificates))

        LOGGER.debug('Created wait timer: %s', self._wait_timer)

        return True
    def test_wait_certificate_serialization(self):
        # Need to create signup information and wait timer first
        signup_info = \
            SignupInfo.create_signup_info(
                validator_address='1660 Pennsylvania Avenue NW',
                originator_public_key_hash=self._originator_public_key_hash,
                most_recent_wait_certificate_id=NULL_BLOCK_IDENTIFIER)

        wt = \
            WaitTimer.create_wait_timer(
                validator_address='1660 Pennsylvania Avenue NW',
                certificates=[])
        while not wt.has_expired(time.time()):
            time.sleep(1)

        # Now we can create a wait certificate and serialize
        wc = \
            WaitCertificate.create_wait_certificate(
                wait_timer=wt,
                block_hash="Reader's Digest")

        dumped = wc.dump()

        self.assertIsNotNone(dumped.get('SerializedCertificate'))
        self.assertIsNotNone(dumped.get('Signature'))

        # Deserialize and verify that wait certificates are the same
        # and that deserialized one is valid
        wc_copy = \
            WaitCertificate.wait_certificate_from_serialized(
                dumped.get('SerializedCertificate'),
                dumped.get('Signature'))

        self.assertEqual(wc.previous_certificate_id,
                         wc_copy.previous_certificate_id)
        self.assertAlmostEqual(wc.local_mean, wc_copy.local_mean)
        self.assertAlmostEqual(wc.request_time, wc_copy.request_time)
        self.assertAlmostEqual(wc.duration, wc_copy.duration)
        self.assertEqual(wc.validator_address, wc_copy.validator_address)
        self.assertEqual(wc.block_hash, wc_copy.block_hash)
        self.assertEqual(wc.signature, wc_copy.signature)
        self.assertEqual(wc.identifier, wc_copy.identifier)

        # Serialize the copy and verify that its serialization and
        # signature are the same
        dumped_copy = wc_copy.dump()

        self.assertTrue(dumped.get('SerializedCertificate'),
                        dumped_copy.get('SerializedCertificate'))
        self.assertTrue(dumped.get('Signature'), dumped_copy.get('Signature'))

        wc_copy.check_valid([], signup_info.poet_public_key)
Example #8
0
    def check_valid(self, poet_enclave_module, certificates, poet_public_key):
        """Determines whether the wait certificate is valid.

        Args:
            poet_enclave_module (module): The module that implements the
                underlying PoET enclave.
            certificates (list): A list of historical certs.
            poet_public_key (str): The PoET public key that corresponds to
                the private key used to sign the certificate.  This is
                obtained from the signup information for the validator
                that is the originator of the block for which the wait
                certificate is associated.

        Returns:
            True if the wait certificate is valid, False otherwise.
        """
        enclave_certificate = \
            self._enclave_wait_certificate(poet_enclave_module)
        expected_mean = WaitTimer.compute_local_mean(certificates)

        if enclave_certificate.duration < WaitTimer.minimum_wait_time:
            raise \
                ValueError(
                    'Wait time less than minimum: {0} < {1}'.format(
                        enclave_certificate.duration,
                        WaitTimer.minimum_wait_time))

        if not _is_close(
                enclave_certificate.local_mean, expected_mean, abs_tol=0.001):
            raise \
                ValueError(
                    'Local mean does not match: {0} != {1}'.format(
                        enclave_certificate.local_mean,
                        expected_mean))

        if len(certificates) != 0 and \
            enclave_certificate.previous_certificate_id != \
                certificates[-1].identifier:
            raise \
                ValueError(
                    'Previous certificate ID does not match: {0} != '
                    '{1}'.format(
                        enclave_certificate.previous_certificate_id,
                        certificates[-1].identifier))

        try:
            poet_enclave_module.verify_wait_certificate(
                enclave_certificate, poet_public_key)
        except Timeout:
            raise NotAvailableException
        except requests.ConnectionError:
            raise NotAvailableException