Esempio n. 1
0
 def __init__(self, delegate, args):
     super(NoopWorkload, self).__init__(delegate, args)
     self._urls = []
     self._lock = threading.Lock()
     self._delegate = delegate
     self._private_key = signing.generate_private_key()
     self._public_key = signing.generate_public_key(self._private_key)
Esempio n. 2
0
    def _create_key(self, key_name='validator.priv'):
        private_key = signing.generate_private_key()
        priv_file = os.path.join(self._temp_dir, key_name)
        with open(priv_file, 'w') as priv_fd:
            priv_fd.write(private_key)

        return signing.generate_public_key(private_key)
Esempio n. 3
0
    def test_add_to_finalized_scheduler(self):
        """Tests that a finalized scheduler raise exception on add_batch().

        This test creates a scheduler, finalizes it, and calls add_batch().
        The result is expected to be a SchedulerError, since adding a batch
        to a finalized scheduler is invalid.
        """
        private_key = signing.generate_private_key()
        public_key = signing.generate_public_key(private_key)

        # Finalize prior to attempting to add a batch.
        self.scheduler.finalize()

        txn, _ = create_transaction(
            payload='a'.encode(),
            private_key=private_key,
            public_key=public_key)

        batch = create_batch(
            transactions=[txn],
            private_key=private_key,
            public_key=public_key)

        # scheduler.add_batch(batch) should throw a SchedulerError due to
        # the finalized status of the scheduler.
        self.assertRaises(
            SchedulerError, lambda: self.scheduler.add_batch(batch))
Esempio n. 4
0
    def test_set_result_on_unscheduled_txn(self):
        """Tests that a scheduler will reject a result on an unscheduled
        transaction.

        Creates a batch with a single transaction, adds the batch to the
        scheduler, then immediately attempts to set the result for the
        transaction without first causing it to be scheduled (by using an
        iterator or calling next_transaction()).
        """
        private_key = signing.generate_private_key()
        public_key = signing.generate_public_key(private_key)

        txn, _ = create_transaction(
            payload='a'.encode(),
            private_key=private_key,
            public_key=public_key)

        batch = create_batch(
            transactions=[txn],
            private_key=private_key,
            public_key=public_key)

        self.scheduler.add_batch(batch)

        self.assertRaises(
            SchedulerError,
            lambda: self.scheduler.set_transaction_execution_result(
                txn.header_signature, False, None))
Esempio n. 5
0
def do_populate(args):
    private_key = signing.generate_private_key()
    public_key = signing.generate_public_key(private_key)

    words = generate_word_list(args.pool_size)

    batches = []
    total_txn_count = 0
    txns = []
    for i in range(0, len(words)):
        txn = create_intkey_transaction(verb='set',
                                        name=words[i],
                                        value=random.randint(9000, 100000),
                                        private_key=private_key,
                                        public_key=public_key)
        total_txn_count += 1
        txns.append(txn)

    batch = create_batch(transactions=txns,
                         private_key=private_key,
                         public_key=public_key)

    batches.append(batch)

    batch_list = batch_pb2.BatchList(batches=batches)

    print("Writing to {}...".format(args.output))
    with open(args.output, "wb") as fd:
        fd.write(batch_list.SerializeToString())
Esempio n. 6
0
def create_chain(num=10):
    priv_key = signer.generate_private_key()
    pub_key = signer.generate_public_key(priv_key)

    counter = 1
    previous_block_id = "0000000000000000"
    blocks = []
    while counter <= num:
        current_block_id = uuid4().hex
        txns = [t[0] for t in [create_transaction(
                payload=uuid4().hex.encode(),
                private_key=priv_key,
                public_key=pub_key) for _ in range(20)]]

        txn_ids = [t.header_signature for t in txns]
        batch = create_batch(
            transactions=txns,
            public_key=pub_key,
            private_key=priv_key)

        blk_w = create_block(
            counter,
            previous_block_id,
            current_block_id,
            batches=[batch])
        blocks.append((current_block_id, blk_w, txn_ids))

        counter += 1
        previous_block_id = current_block_id

    return blocks
Esempio n. 7
0
def do_populate(args, batches, keys):
    private_key = signing.generate_private_key()
    public_key = signing.generate_public_key(private_key)

    total_txn_count = 0
    txns = []
    for i in range(0, len(keys)):
        name = list(keys)[i]
        txn = create_intkey_transaction(
            verb='set',
            name=name,
            value=random.randint(9000, 100000),
            deps=[],
            private_key=private_key,
            public_key=public_key)
        total_txn_count += 1
        txns.append(txn)
        # Establish the signature of the txn associated with the word
        # so we can create good dependencies later
        keys[name] = txn.header_signature

    batch = create_batch(
        transactions=txns,
        private_key=private_key,
        public_key=public_key)

    batches.append(batch)
Esempio n. 8
0
    def test_authorization_challenge_submit(self):
        """
        Test the AuthorizationChallengeSubmitHandler returns an
        AuthorizationViolation and closes the connection if the permission
        verifier does not permit the public_key.
        """
        private_key = signing.generate_private_key()
        public_key = signing.generate_public_key(private_key)
        payload = os.urandom(10)

        signature = signing.sign(payload, private_key)

        auth_challenge_submit = AuthorizationChallengeSubmit(
            public_key=public_key,
            payload=payload,
            signature=signature,
            roles=[RoleType.Value("NETWORK")])

        roles = {"network": AuthorizationType.TRUST}

        network = MockNetwork(roles,
                              connection_status={
                                  "connection_id":
                                  ConnectionStatus.AUTH_CHALLENGE_REQUEST
                              })
        permission_verifer = MockPermissionVerifier(allow=False)
        gossip = MockGossip()
        handler = AuthorizationChallengeSubmitHandler(network,
                                                      permission_verifer,
                                                      gossip)
        handler_status = handler.handle(
            "connection_id", auth_challenge_submit.SerializeToString())
        self.assertEqual(handler_status.status, HandlerStatus.RETURN_AND_CLOSE)
        self.assertEqual(handler_status.message_type,
                         validator_pb2.Message.AUTHORIZATION_VIOLATION)
Esempio n. 9
0
def do_keygen(args):
    if args.key_name is not None:
        key_name = args.key_name
    else:
        key_name = getpass.getuser()

    if args.key_dir is not None:
        key_dir = args.key_dir
        if not os.path.exists(key_dir):
            raise CliException('no such directory: {}'.format(key_dir))
    else:
        key_dir = os.path.join(os.path.expanduser('~'), '.sawtooth', 'keys')
        if not os.path.exists(key_dir):
            if not args.quiet:
                print('creating key directory: {}'.format(key_dir))
            try:
                os.makedirs(key_dir)
            except IOError as e:
                raise CliException('IOError: {}'.format(str(e)))

    priv_filename = os.path.join(key_dir, key_name + '.priv')
    pub_filename = os.path.join(key_dir, key_name + '.pub')

    if not args.force:
        file_exists = False
        for filename in [priv_filename, pub_filename]:
            if os.path.exists(filename):
                file_exists = True
                print('file exists: {}'.format(filename), file=sys.stderr)
        if file_exists:
            raise CliException(
                'files exist, rerun with --force to overwrite existing files')

    private_key = signing.generate_private_key()
    public_key = signing.generate_public_key(private_key)

    try:
        priv_exists = os.path.exists(priv_filename)
        with open(priv_filename, 'w') as priv_fd:
            if not args.quiet:
                if priv_exists:
                    print('overwriting file: {}'.format(priv_filename))
                else:
                    print('writing file: {}'.format(priv_filename))
            priv_fd.write(private_key)
            priv_fd.write('\n')

        pub_exists = os.path.exists(pub_filename)
        with open(pub_filename, 'w') as pub_fd:
            if not args.quiet:
                if pub_exists:
                    print('overwriting file: {}'.format(pub_filename))
                else:
                    print('writing file: {}'.format(pub_filename))
            pub_fd.write(public_key)
            pub_fd.write('\n')

    except IOError as ioe:
        raise CliException('IOError: {}'.format(str(ioe)))
Esempio n. 10
0
    def test_set_status(self):
        """Tests that set_status() has the correct behavior.

        Basically:
            1. Adds a batch which has two transactions.
            2. Calls next_transaction() to get the first Transaction.
            3. Calls next_transaction() to verify that it returns None.
            4. Calls set_status() to mark the first transaction applied.
            5. Calls next_transaction() to  get the second Transaction.

        Step 3 returns None because the first transaction hasn't been marked
        as applied, and the SerialScheduler will only return one
        not-applied Transaction at a time.

        Step 5 is expected to return the second Transaction, not None,
        since the first Transaction was marked as applied in the previous
        step.
        """
        private_key = signing.generate_private_key()
        public_key = signing.generate_public_key(private_key)

        txns = []

        for name in ['a', 'b']:
            txn, _ = create_transaction(
                payload=name.encode(),
                private_key=private_key,
                public_key=public_key)

            txns.append(txn)

        batch = create_batch(
            transactions=txns,
            private_key=private_key,
            public_key=public_key)

        self.scheduler.add_batch(batch)

        scheduled_txn_info = self.scheduler.next_transaction()
        self.assertIsNotNone(scheduled_txn_info)
        self.assertEqual('a', scheduled_txn_info.txn.payload.decode())

        self.assertIsNone(self.scheduler.next_transaction())
        c_id = self.context_manager.create_context(
            self.first_state_root,
            base_contexts=scheduled_txn_info.base_context_ids,
            inputs=[],
            outputs=[])

        self.scheduler.set_transaction_execution_result(
            scheduled_txn_info.txn.header_signature,
            is_valid=True,
            context_id=c_id)

        scheduled_txn_info = self.scheduler.next_transaction()
        self.assertIsNotNone(scheduled_txn_info)
        self.assertEqual('b', scheduled_txn_info.txn.payload.decode())
Esempio n. 11
0
    def __init__(self, rest_endpoint):
        self.priv_key = signer.generate_private_key()
        self.pub_key = signer.generate_public_key(self.priv_key)

        self._priv_key_file = os.path.join("/tmp", uuid4().hex[:20])
        with open(self._priv_key_file, mode='w') as out:
            out.write(self.priv_key)

        self._rest_endpoint = rest_endpoint
Esempio n. 12
0
 def __init__(self, rest_endpoint):
     self.priv_key = signer.generate_private_key()
     self.pub_key = signer.generate_public_key(self.priv_key)
     self._namespace = hashlib.sha512('intkey'.encode()).hexdigest()[:6]
     self._factory = MessageFactory(
         'intkey',
         '1.0',
         self._namespace)
     self._rest = RestClient(rest_endpoint)
Esempio n. 13
0
 def setUp(self):
     self.block_store = BlockStore({})
     self.gossip = MockGossip()
     self.completer = Completer(self.block_store, self.gossip)
     self.completer._on_block_received = self._on_block_received
     self.completer._on_batch_received = self._on_batch_received
     self.private_key = signing.generate_private_key()
     self.public_key = signing.generate_public_key(self.private_key)
     self.blocks = []
     self.batches = []
Esempio n. 14
0
 def __init__(self, delegate, args):
     super(IntKeyWorkload, self).__init__(delegate, args)
     self._auth_info = args.auth_info
     self._urls = []
     self._pending_batches = {}
     self._lock = threading.Lock()
     self._delegate = delegate
     self._deps = {}
     self._private_key = signing.generate_private_key()
     self._public_key = signing.generate_public_key(self._private_key)
Esempio n. 15
0
def do_keygen(args):
    """Executes the key generation operation, given the parsed arguments.

    Args:
        args (:obj:`Namespace`): The parsed args.
    """
    if args.key_name is not None:
        key_name = args.key_name
    else:
        key_name = 'validator'

    key_dir = get_key_dir()

    if not os.path.exists(key_dir):
        raise CliException("Key directory does not exist: {}".format(key_dir))

    priv_filename = os.path.join(key_dir, key_name + '.priv')
    pub_filename = os.path.join(key_dir, key_name + '.pub')

    if not args.force:
        file_exists = False
        for filename in [priv_filename, pub_filename]:
            if os.path.exists(filename):
                file_exists = True
                print('file exists: {}'.format(filename), file=sys.stderr)
        if file_exists:
            raise CliException(
                'files exist, rerun with --force to overwrite existing files')

    private_key = signing.generate_private_key()
    public_key = signing.generate_public_key(private_key)

    try:
        priv_exists = os.path.exists(priv_filename)
        with open(priv_filename, 'w') as priv_fd:
            if not args.quiet:
                if priv_exists:
                    print('overwriting file: {}'.format(priv_filename))
                else:
                    print('writing file: {}'.format(priv_filename))
            priv_fd.write(private_key)
            priv_fd.write('\n')

        pub_exists = os.path.exists(pub_filename)
        with open(pub_filename, 'w') as pub_fd:
            if not args.quiet:
                if pub_exists:
                    print('overwriting file: {}'.format(pub_filename))
                else:
                    print('writing file: {}'.format(pub_filename))
            pub_fd.write(public_key)
            pub_fd.write('\n')

    except IOError as ioe:
        raise CliException('IOError: {}'.format(str(ioe)))
Esempio n. 16
0
    def test_transaction_order(self):
        """Tests the that transactions are returned in order added.

        Adds three batches with varying number of transactions, then tests
        that they are returned in the appropriate order when using an iterator.

        This test also creates a second iterator and verifies that both
        iterators return the same transactions.

        This test also finalizes the scheduler and verifies that StopIteration
        is thrown by the iterator.
        """
        private_key = signing.generate_private_key()
        public_key = signing.generate_public_key(private_key)

        txns = []

        for names in [['a', 'b', 'c'], ['d', 'e'], ['f', 'g', 'h', 'i']]:
            batch_txns = []
            for name in names:
                txn, _ = create_transaction(
                    payload=name.encode(),
                    private_key=private_key,
                    public_key=public_key)

                batch_txns.append(txn)
                txns.append(txn)

            batch = create_batch(
                transactions=batch_txns,
                private_key=private_key,
                public_key=public_key)

            self.scheduler.add_batch(batch)

        self.scheduler.finalize()

        iterable1 = iter(self.scheduler)
        iterable2 = iter(self.scheduler)
        for txn in txns:
            scheduled_txn_info = next(iterable1)
            self.assertEqual(scheduled_txn_info, next(iterable2))
            self.assertIsNotNone(scheduled_txn_info)
            self.assertEqual(txn.payload, scheduled_txn_info.txn.payload)
            c_id = self.context_manager.create_context(
                self.first_state_root,
                base_contexts=scheduled_txn_info.base_context_ids,
                inputs=[],
                outputs=[])
            self.scheduler.set_transaction_execution_result(
                txn.header_signature, True, c_id)

        with self.assertRaises(StopIteration):
            next(iterable1)
Esempio n. 17
0
    def _create_batches(self):
        test_yaml = self._yaml_from_file()
        priv_key = signing.generate_private_key()
        pub_key = signing.generate_public_key(priv_key)

        batches, batch_results = self._process_batches(yaml_batches=test_yaml,
                                                       priv_key=priv_key,
                                                       pub_key=pub_key)

        self._batch_results = batch_results
        self._batches = batches
Esempio n. 18
0
 def setUp(self):
     self.block_store = BlockStore(DictDatabase(
         indexes=BlockStore.create_index_configuration()))
     self.gossip = MockGossip()
     self.completer = Completer(self.block_store, self.gossip)
     self.completer._on_block_received = self._on_block_received
     self.completer._on_batch_received = self._on_batch_received
     self.private_key = signing.generate_private_key()
     self.public_key = signing.generate_public_key(self.private_key)
     self.blocks = []
     self.batches = []
Esempio n. 19
0
    def test_completion_on_last_result(self):
        """Tests the that the schedule is not marked complete until the last
        result is set.

        Adds three batches with varying number of transactions, then tests
        that they are returned in the appropriate order when using an iterator.
        Test that the value of `complete` is false until the last value.

        This test also finalizes the scheduler and verifies that StopIteration
        is thrown by the iterator, and the complete is true in the at the end.
        """
        private_key = signing.generate_private_key()
        public_key = signing.generate_public_key(private_key)

        txns = []

        for names in [['a', 'b', 'c'], ['d', 'e'], ['f', 'g', 'h', 'i']]:
            batch_txns = []
            for name in names:
                txn, _ = create_transaction(
                    payload=name.encode(),
                    private_key=private_key,
                    public_key=public_key)

                batch_txns.append(txn)
                txns.append(txn)

            batch = create_batch(
                transactions=batch_txns,
                private_key=private_key,
                public_key=public_key)

            self.scheduler.add_batch(batch)

        self.scheduler.finalize()

        iterable1 = iter(self.scheduler)
        for txn in txns:
            scheduled_txn_info = next(iterable1)
            self.assertFalse(self.scheduler.complete(block=False))

            c_id = self.context_manager.create_context(
                self.first_state_root,
                base_contexts=scheduled_txn_info.base_context_ids,
                inputs=[],
                outputs=[])
            self.scheduler.set_transaction_execution_result(
                txn.header_signature, True, c_id)

        self.assertTrue(self.scheduler.complete(block=False))

        with self.assertRaises(StopIteration):
            next(iterable1)
Esempio n. 20
0
 def setUp(self):
     self.private_key = signing.generate_private_key()
     self.public_key = signing.generate_public_key(self.private_key)
     self._identity_view_factory = MockIdentityViewFactory()
     self.permissions = {}
     self._identity_cache = IdentityCache(self._identity_view_factory,
                                          self._current_root_func)
     self.permission_verifier = \
         PermissionVerifier(
             permissions=self.permissions,
             current_root_func=self._current_root_func,
             identity_cache=self._identity_cache)
Esempio n. 21
0
    def __init__(self, with_genesis=True):
        self.block_sender = MockBlockSender()
        self.batch_sender = MockBatchSender()
        self.block_store = BlockStore(
            DictDatabase(indexes=BlockStore.create_index_configuration()))
        self.block_cache = BlockCache(self.block_store)
        self.state_db = {}

        # add the mock reference to the consensus
        consensus_setting_addr = SettingsView.setting_address(
            'sawtooth.consensus.algorithm')
        self.state_db[consensus_setting_addr] = _setting_entry(
            'sawtooth.consensus.algorithm', 'test_journal.mock_consensus')

        self.state_view_factory = MockStateViewFactory(self.state_db)
        self.signing_key = signing.generate_private_key()
        self.public_key = signing.generate_public_key(self.signing_key)

        self.identity_signing_key = signing.generate_private_key()
        chain_head = None
        if with_genesis:
            self.genesis_block = self.generate_genesis_block()
            self.set_chain_head(self.genesis_block)
            chain_head = self.genesis_block

        self.block_publisher = BlockPublisher(
            transaction_executor=MockTransactionExecutor(),
            block_cache=self.block_cache,
            state_view_factory=self.state_view_factory,
            block_sender=self.block_sender,
            batch_sender=self.block_sender,
            squash_handler=None,
            chain_head=chain_head,
            identity_signing_key=self.identity_signing_key,
            data_dir=None,
            config_dir=None,
            permission_verifier=MockPermissionVerifier(),
            check_publish_block_frequency=0.1,
            batch_observers=[])
Esempio n. 22
0
def do_generate(args):
    private_key = signing.generate_private_key()
    public_key = signing.generate_public_key(private_key)

    words = generate_word_list(args.pool_size)

    batches = []
    start = time.time()
    total_txn_count = 0
    for i in range(0, args.count):
        txns = []
        for _ in range(0, random.randint(1, args.batch_max_size)):
            txn = create_intkey_transaction(
                verb=random.choice(['inc', 'dec']),
                name=random.choice(words),
                value=1,
                private_key=private_key,
                public_key=public_key)
            total_txn_count += 1
            txns.append(txn)

        batch = create_batch(
            transactions=txns,
            private_key=private_key,
            public_key=public_key)

        batches.append(batch)

        if i % 100 == 0 and i != 0:
            stop = time.time()

            txn_count = 0
            for batch in batches[-100:]:
                txn_count += len(batch.transactions)

            fmt = 'batches {}, batch/sec: {:.2f}, txns: {}, txns/sec: {:.2f}'
            print(fmt.format(
                str(i),
                100 / (stop - start),
                str(total_txn_count),
                txn_count / (stop - start)))
            start = stop

    batch_list = batch_pb2.BatchList(batches=batches)

    print("Writing to {}...".format(args.output))
    with open(args.output, "wb") as fd:
        fd.write(batch_list.SerializeToString())
Esempio n. 23
0
    def _completion_on_finalize(self, scheduler):
        """Tests that iteration will stop when finalized is called on an
        otherwise complete scheduler.

        Notes:
            Adds one batch and transaction, then verifies the iterable returns
            that transaction.  Sets the execution result and then calls finalize.
            Since the the scheduler is complete (all transactions have had
            results set, and it's been finalized), we should get a StopIteration.
            This check is useful in making sure the finalize() can occur after
            all set_transaction_execution_result()s have been performed, because
            in a normal situation, finalize will probably occur prior to those
            calls.

        This test should work for both a serial and parallel scheduler.
        """

        private_key = signing.generate_private_key()
        public_key = signing.generate_public_key(private_key)

        txn, _ = create_transaction(
            payload='a'.encode(),
            private_key=private_key,
            public_key=public_key)

        batch = create_batch(
            transactions=[txn],
            private_key=private_key,
            public_key=public_key)

        iterable = iter(scheduler)

        scheduler.add_batch(batch)

        scheduled_txn_info = next(iterable)
        self.assertIsNotNone(scheduled_txn_info)
        self.assertEqual(txn.payload, scheduled_txn_info.txn.payload)
        scheduler.set_transaction_execution_result(
            txn.header_signature, False, None)

        scheduler.finalize()

        with self.assertRaises(StopIteration):
            next(iterable)
Esempio n. 24
0
    def _completion_on_finalize_only_when_done(self, scheduler):
        """Tests that complete will only be true when the scheduler
        has had finalize called and all txns have execution result set.

        Notes:
            Adds one batch and transaction, then verifies the iterable returns
            that transaction.  Finalizes then sets the execution result. The
            schedule should not be marked as complete until after the
            execution result is set.
            This check is useful in making sure the finalize() can occur after
            all set_transaction_execution_result()s have been performed, because
            in a normal situation, finalize will probably occur prior to those
            calls.

        This test should work for both a serial and parallel scheduler.
        """
        private_key = signing.generate_private_key()
        public_key = signing.generate_public_key(private_key)

        txn, _ = create_transaction(
            payload='a'.encode(),
            private_key=private_key,
            public_key=public_key)

        batch = create_batch(
            transactions=[txn],
            private_key=private_key,
            public_key=public_key)

        iterable = iter(scheduler)

        scheduler.add_batch(batch)

        scheduled_txn_info = next(iterable)
        self.assertIsNotNone(scheduled_txn_info)
        self.assertEqual(txn.payload, scheduled_txn_info.txn.payload)
        scheduler.finalize()
        self.assertFalse(scheduler.complete(block=False))
        scheduler.set_transaction_execution_result(
            txn.header_signature, False, None)
        self.assertTrue(scheduler.complete(block=False))

        with self.assertRaises(StopIteration):
            next(iterable)
Esempio n. 25
0
def do_generate(args, batches, keys):
    private_key = signing.generate_private_key()
    public_key = signing.generate_public_key(private_key)

    start = time.time()
    total_txn_count = 0
    for i in range(0, args.count):
        txns = []
        for _ in range(0, random.randint(1, args.max_batch_size)):
            name = random.choice(list(keys))
            txn = create_intkey_transaction(
                verb=random.choice(['inc', 'dec']),
                name=name,
                value=random.randint(1, 10),
                deps=[keys[name]],
                private_key=private_key,
                public_key=public_key)
            total_txn_count += 1
            txns.append(txn)

        batch = create_batch(
            transactions=txns,
            private_key=private_key,
            public_key=public_key)

        batches.append(batch)

        if i % 100 == 0 and i != 0:
            stop = time.time()

            txn_count = 0
            for batch in batches[-100:]:
                txn_count += len(batch.transactions)

            fmt = 'batches {}, batch/sec: {:.2f}, txns: {}, txns/sec: {:.2f}'
            print(fmt.format(
                str(i),
                100 / (stop - start),
                str(total_txn_count),
                txn_count / (stop - start)))
            start = stop
    def test_block_publisher_doesnt_finalize_block(
            self, mock_utils, mock_validator_registry_view,
            mock_consensus_state, mock_poet_enclave_factory,
            mock_consensus_state_store, mock_poet_key_state_store,
            mock_signup_info, mock_wait_certificate, mock_poet_settings_view,
            mock_block_wrapper):
        """ Test verifies that PoET Block Publisher doesn't finalize
            a candidate block that doesn't have a valid wait certificate.
        """

        # create a mock_validator_registry_view with
        # get_validator_info that does nothing
        mock_validator_registry_view.return_value.get_validator_info. \
            return_value = \
            ValidatorInfo(
                name='validator_001',
                id='validator_deadbeef',
                signup_info=SignUpInfo(
                    poet_public_key='00112233445566778899aabbccddeeff'))

        # create a mock_wait_certificate that pretends to fail
        mock_wait_certificate.create_wait_certificate.side_effect = \
            ValueError('Unit test fake failure')

        # create a mock_consensus_state that returns a mock with
        # the following settings:
        mock_state = MockConsensusState().create_mock_consensus_state()

        mock_consensus_state.consensus_state_for_block_id.return_value = \
            mock_state

        # create mock_batch_publisher
        mock_batch_publisher = mock.Mock(
            identity_signing_key=signing.generate_private_key())

        mock_block_cache = mock.MagicMock()
        mock_state_view_factory = mock.Mock()

        # create mock_block_header with the following fields
        mock_block = mock.Mock(identifier='0123456789abcdefedcba9876543210')
        mock_block.header.signer_public_key = \
            '90834587139405781349807435098745'
        mock_block.header.previous_block_id = '2'
        mock_block.header.block_num = 1
        mock_block.header.state_root_hash = '6'
        mock_block.header.batch_ids = '4'

        # check test
        with mock.patch('sawtooth_poet.poet_consensus.poet_block_publisher.'
                        'LOGGER') as mock_logger:
            block_publisher = \
                poet_block_publisher.PoetBlockPublisher(
                    block_cache=mock_block_cache,
                    state_view_factory=mock_state_view_factory,
                    batch_publisher=mock_batch_publisher,
                    data_dir=self._temp_dir,
                    config_dir=self._temp_dir,
                    validator_id='validator_deadbeef')

            with mock.patch('sawtooth_poet.poet_consensus.'
                            'poet_block_publisher.json') as _:
                self.assertFalse(
                    block_publisher.finalize_block(
                        block_header=mock_block.header))

            # Could be a hack, but verify that the appropriate log message is
            # generated - so we at least have some faith that the failure was
            # because of what we are testing and not something else.  I know
            # that this is fragile if the log message is changed, so would
            # accept any suggestions on a better way to verify that the
            # function fails for the reason we expect.

            (message, *_), _ = mock_logger.error.call_args
            self.assertTrue('Failed to create wait certificate: ' in message)
    def test_block_publisher_finalize_block(
            self, mock_utils, mock_validator_registry_view,
            mock_consensus_state, mock_poet_enclave_factory,
            mock_consensus_state_store, mock_poet_key_state_store,
            mock_signup_info, mock_wait_certificate, mock_poet_settings_view,
            mock_block_wrapper):
        """ Test verifies that PoET Block Publisher finalizes the block,
            meaning that the candidate block is good and should be generated.
        """

        # create a mock_validator_registry_view with
        # get_validator_info that does nothing
        mock_validator_registry_view.return_value.get_validator_info. \
            return_value = \
            ValidatorInfo(
                name='validator_001',
                id='validator_deadbeef',
                signup_info=SignUpInfo(
                    poet_public_key='00112233445566778899aabbccddeeff'))

        # create a mock_wait_certificate that does nothing in check_valid
        my_wait_certificate = mock.Mock()
        my_wait_certificate.check_valid.return_value = None
        mock_wait_certificate.create_wait_certificate.return_value = \
            my_wait_certificate

        # create a mock_consensus_state that returns a mock with
        # the following settings:
        mock_state = MockConsensusState().create_mock_consensus_state()

        mock_consensus_state.consensus_state_for_block_id.return_value = \
            mock_state

        # create mock_batch_publisher
        mock_batch_publisher = mock.Mock(
            identity_signing_key=signing.generate_private_key())

        mock_block_cache = mock.MagicMock()
        mock_state_view_factory = mock.Mock()

        # create mock_block_header with the following fields
        mock_block = mock.Mock(identifier='0123456789abcdefedcba9876543210')
        mock_block.header.signer_public_key = \
            '90834587139405781349807435098745'
        mock_block.header.previous_block_id = '2'
        mock_block.header.block_num = 1
        mock_block.header.state_root_hash = '6'
        mock_block.header.batch_ids = '4'

        # check test
        block_publisher = \
            poet_block_publisher.PoetBlockPublisher(
                block_cache=mock_block_cache,
                state_view_factory=mock_state_view_factory,
                batch_publisher=mock_batch_publisher,
                data_dir=self._temp_dir,
                config_dir=self._temp_dir,
                validator_id='validator_deadbeef')

        with mock.patch('sawtooth_poet.poet_consensus.'
                        'poet_block_publisher.json') as _:
            self.assertTrue(
                block_publisher.finalize_block(block_header=mock_block.header))
Esempio n. 28
0
 def setUp(self):
     self._temp_dir = tempfile.mkdtemp()
     self._identity_key = signing.generate_private_key()
Esempio n. 29
0
class _PoetEnclaveSimulator(object):
    # A lock to protect threaded access
    _lock = threading.Lock()

    # The private key we generate to sign the certificate ID when creating
    # the random wait timeout value
    _seal_private_key = signing.generate_private_key()
    _seal_public_key = signing.generate_public_key(_seal_private_key)

    # The basename and enclave measurement values we will put into and verify
    # are in the enclave quote in the attestation verification report.
    __VALID_BASENAME__ = \
        bytes.fromhex(
            'b785c58b77152cbe7fd55ee3851c4990'
            '00000000000000000000000000000000')
    __VALID_ENCLAVE_MEASUREMENT__ = \
        bytes.fromhex(
            'c99f21955e38dbb03d2ca838d3af6e43'
            'ef438926ed02db4cc729380c8c7a174e')

    # We use the report private key PEM to create the private key used to
    # sign attestation verification reports.  On the flip side, the report
    # public key PEM is used to create the public key used to verify the
    # signature on the attestation verification reports.
    __REPORT_PRIVATE_KEY_PEM__ = \
        '-----BEGIN PRIVATE KEY-----\n' \
        'MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCsy/NmLwZP6Uj0\n' \
        'p5mIiefgK8VOK7KJ34g3h0/X6aFOd/Ff4j+e23wtQpkxsjVHWLM5SjElGhfpVDhL\n' \
        '1WAMsQI9bpCWR4sjV6p7gOJhv34nkA2Grj5eSHCAJRQXCl+pJ9dYIeKaNoaxkdtq\n' \
        '+Xme//ohtkkv/ZjMTfsjMl0RLXokJ+YhSuTpNSovRaCtZfLB5MihVJuV3Qzb2ROh\n' \
        'KQxcuyPy9tBtOIrBWJaFiXOLRxAijs+ICyzrqUBbRfoAztkljIBx9KNItHiC4zPv\n' \
        'o6DxpGSO2yMQSSrs13PkfyGWVZSgenEYOouEz07X+H5B29PPuW5mCl4nkoH3a9gv\n' \
        'rI6VLEx9AgMBAAECggEAImfFge4RCq4/eX85gcc7pRXyBjuLJAqe+7d0fWAmXxJg\n' \
        'vB+3XTEEi5p8GDoMg7U0kk6kdGe6pRnAz9CffEduU78FCPcbzCCzcD3cVWwkeUok\n' \
        'd1GQV4OC6vD3DBNjsrGdHg45KU18CjUphCZCQhdjvXynG+gZmWxZecuYXkg4zqPT\n' \
        'LwOkcdWBPhJ9CbjtiYOtKDZbhcbdfnb2fkxmvnAoz1OWNfVFXh+x7651FrmL2Pga\n' \
        'xGz5XoxFYYT6DWW1fL6GNuVrd97wkcYUcjazMgunuUMC+6XFxqK+BoqnxeaxnsSt\n' \
        'G2r0sdVaCyK1sU41ftbEQsc5oYeQ3v5frGZL+BgrYQKBgQDgZnjqnVI/B+9iarx1\n' \
        'MjAFyhurcKvFvlBtGKUg9Q62V6wI4VZvPnzA2zEaR1J0cZPB1lCcMsFACpuQF2Mr\n' \
        '3VDyJbnpSG9q05POBtfLjGQdXKtGb8cfXY2SwjzLH/tvxHm3SP+RxvLICQcLX2/y\n' \
        'GTJ+mY9C6Hs6jIVLOnMWkRWamQKBgQDFITE3Qs3Y0ZwkKfGQMKuqJLRw29Tyzw0n\n' \
        'XKaVmO/pEzYcXZMPBrFhGvdmNcJLo2fcsmGZnmit8RP4ChwHUlD11dH1Ffqw9FWc\n' \
        '387i0chlE5FhQPirSM8sWFVmjt2sxC4qFWJoAD/COQtKHgEaVKVc4sH/yRostL1C\n' \
        'r+7aWuqzhQKBgQDcuC5LJr8VPGrbtPz1kY3mw+r/cG2krRNSm6Egj6oO9KFEgtCP\n' \
        'zzjKQU9E985EtsqNKI5VdR7cLRLiYf6r0J6j7zO0IAlnXADP768miUqYDuRw/dUw\n' \
        'JsbwCZneefDI+Mp325d1/egjla2WJCNqUBp4p/Zf62f6KOmbGzzEf6RuUQKBgG2y\n' \
        'E8YRiaTOt5m0MXUwcEZk2Hg5DF31c/dkalqy2UYU57aPJ8djzQ8hR2x8G9ulWaWJ\n' \
        'KiCm8s9gaOFNFt3II785NfWxPmh7/qwmKuUzIdWFNxAsbHQ8NvURTqyccaSzIpFO\n' \
        'hw0inlhBEBQ1cB2r3r06fgQNb2BTT0Itzrd5gkNVAoGBAJcMgeKdBMukT8dKxb4R\n' \
        '1PgQtFlR3COu2+B00pDyUpROFhHYLw/KlUv5TKrH1k3+E0KM+winVUIcZHlmFyuy\n' \
        'Ilquaova1YSFXP5cpD+PKtxRV76Qlqt6o+aPywm81licdOAXotT4JyJhrgz9ISnn\n' \
        'J13KkHoAZ9qd0rX7s37czb3O\n' \
        '-----END PRIVATE KEY-----'

    _report_private_key = \
        serialization.load_pem_private_key(
            __REPORT_PRIVATE_KEY_PEM__.encode(),
            password=None,
            backend=backends.default_backend())

    # The anti-sybil ID for this particular validator.  This will get set when
    # the enclave is initialized
    _anti_sybil_id = None

    @classmethod
    def initialize(cls, config_dir, data_dir):
        # See if our configuration file exists.  If so, then we are going to
        # see if there is a configuration value for the validator ID.  If so,
        # then we'll use that when constructing the simulated anti-Sybil ID.
        # Otherwise, we are going to fall back on trying to create one that is
        # unique.
        validator_id = datetime.datetime.now().isoformat()

        config_file = os.path.join(config_dir, 'poet_enclave_simulator.toml')
        if os.path.exists(config_file):
            LOGGER.info('Loading PoET enclave simulator config from : %s',
                        config_file)

            try:
                with open(config_file) as fd:
                    toml_config = toml.loads(fd.read())
            except IOError as e:
                LOGGER.info(
                    'Error loading PoET enclave simulator configuration: %s',
                    e)
                LOGGER.info('Continuing with default configuration')

            invalid_keys = set(toml_config.keys()).difference(['validator_id'])
            if invalid_keys:
                LOGGER.warning(
                    'Ignoring invalid keys in PoET enclave simulator config: '
                    '%s', ', '.join(sorted(list(invalid_keys))))

            validator_id = toml_config.get('validator_id', validator_id)

        LOGGER.debug('PoET enclave simulator creating anti-Sybil ID from: %s',
                     validator_id)

        # Create an anti-Sybil ID that is unique for this validator
        cls._anti_sybil_id = hashlib.sha256(validator_id.encode()).hexdigest()

    @classmethod
    def shutdown(cls):
        pass

    @classmethod
    def get_enclave_measurement(cls):
        return cls.__VALID_ENCLAVE_MEASUREMENT__.hex()

    @classmethod
    def get_enclave_basename(cls):
        return cls.__VALID_BASENAME__.hex()

    @classmethod
    def create_signup_info(cls, originator_public_key_hash, nonce):
        with cls._lock:
            # First we need to create a public/private key pair for the PoET
            # enclave to use.
            # Counter ID is a placeholder for a hardware counter in a TEE.
            poet_private_key = signing.generate_private_key()
            poet_public_key = \
                signing.generate_public_key(poet_private_key)
            counter_id = None

            # Simulate sealing (encrypting) the signup data.
            signup_data = {
                'poet_private_key': poet_private_key,
                'poet_public_key': poet_public_key,
                'counter_id': counter_id
            }
            sealed_signup_data = \
                base64.b64encode(
                    dict2json(signup_data).encode()).decode('utf-8')

            # Build up a fake SGX quote containing:
            # 1. The basename
            # 2. The report body that contains:
            #    a. The enclave measurement
            #    b. The report data SHA256(SHA256(OPK)|PPK)
            sgx_basename = \
                sgx_structs.SgxBasename(name=cls.__VALID_BASENAME__)
            sgx_measurement = \
                sgx_structs.SgxMeasurement(
                    m=cls.__VALID_ENCLAVE_MEASUREMENT__)

            hash_input = \
                '{0}{1}'.format(
                    originator_public_key_hash.upper(),
                    poet_public_key.upper()).encode()
            report_data = hashlib.sha256(hash_input).digest()
            sgx_report_data = sgx_structs.SgxReportData(d=report_data)
            sgx_report_body = \
                sgx_structs.SgxReportBody(
                    mr_enclave=sgx_measurement,
                    report_data=sgx_report_data)

            sgx_quote = \
                sgx_structs.SgxQuote(
                    basename=sgx_basename,
                    report_body=sgx_report_body)

            # Create a fake PSE manifest.  A base64 encoding of the
            # originator public key hash should suffice.
            pse_manifest = \
                base64.b64encode(originator_public_key_hash.encode())

            timestamp = datetime.datetime.now().isoformat()

            # Fake our "proof" data.
            verification_report = {
                'epidPseudonym':
                cls._anti_sybil_id,
                'id':
                base64.b64encode(
                    hashlib.sha256(
                        timestamp.encode()).hexdigest().encode()).decode(),
                'isvEnclaveQuoteStatus':
                'OK',
                'isvEnclaveQuoteBody':
                base64.b64encode(sgx_quote.serialize_to_bytes()).decode(),
                'pseManifestStatus':
                'OK',
                'pseManifestHash':
                hashlib.sha256(base64.b64decode(pse_manifest)).hexdigest(),
                'nonce':
                nonce,
                'timestamp':
                timestamp
            }

            # Serialize the verification report, sign it, and then put
            # in the proof data
            verification_report_json = dict2json(verification_report)
            signature = \
                cls._report_private_key.sign(
                    verification_report_json.encode(),
                    padding.PKCS1v15(),
                    hashes.SHA256())

            proof_data_dict = {
                'evidence_payload': {
                    'pse_manifest': pse_manifest.decode()
                },
                'verification_report': verification_report_json,
                'signature': base64.b64encode(signature).decode()
            }
            proof_data = dict2json(proof_data_dict)

            return \
                EnclaveSignupInfo(
                    poet_public_key=signup_data['poet_public_key'],
                    proof_data=proof_data,
                    anti_sybil_id=cls._anti_sybil_id,
                    sealed_signup_data=sealed_signup_data)

    @classmethod
    def deserialize_signup_info(cls, serialized_signup_info):
        return \
            EnclaveSignupInfo.signup_info_from_serialized(
                serialized_signup_info=serialized_signup_info)

    @classmethod
    def unseal_signup_data(cls, sealed_signup_data):
        """

        Args:
            sealed_signup_data: Sealed signup data that was returned
                previously in a EnclaveSignupInfo object from a call to
                create_signup_info

        Returns:
            A string The hex encoded PoET public key that was extracted from
            the sealed data
        """

        # Reverse the process we used in creating "sealed" signup info.
        # Specifically, we will do a base 64 decode, which gives us JSON
        # we can convert back to a dictionary we can use to get the
        # data we need
        signup_data = \
            json2dict(base64.b64decode(sealed_signup_data.encode()).decode())
        return signup_data.get('poet_public_key')

    @classmethod
    def release_signup_data(cls, sealed_signup_data):
        """

        Args:
            sealed_signup_data: Sealed signup data that was returned
                previously in a EnclaveSignupInfo object from a call to
                create_signup_info
        """
        # This is a standin method to release enclave resources associated
        # with this signup. This is not currently relevant to the simulator
        # but it must match the interface with the HW enclave.
        pass

    @classmethod
    def create_wait_timer(cls, sealed_signup_data, validator_address,
                          previous_certificate_id, local_mean,
                          minimum_wait_time):
        with cls._lock:
            # Extract keys from the 'sealed' signup data
            signup_data = \
                json2dict(
                    base64.b64decode(sealed_signup_data.encode()).decode())
            poet_private_key = signup_data['poet_private_key']

            if poet_private_key is None:
                raise \
                    ValueError(
                        'Invalid signup data. No poet private key.')

            # In a TEE implementation we would increment the HW counter here.
            # We can't usefully simulate a HW counter though.

            # Create some value from the cert ID.  We are just going to use
            # the seal key to sign the cert ID.  We will then use the
            # low-order 64 bits to change that to a number [0, 1]
            tag = \
                base64.b64decode(
                    signing.sign(
                        previous_certificate_id,
                        cls._seal_private_key))

            tagd = float(struct.unpack('Q', tag[-8:])[0]) / (2**64 - 1)

            # Now compute the duration with a minimum wait time guaranteed
            duration = minimum_wait_time - local_mean * math.log(tagd)

            # Create and sign the wait timer
            wait_timer = \
                EnclaveWaitTimer(
                    validator_address=validator_address,
                    duration=duration,
                    previous_certificate_id=previous_certificate_id,
                    local_mean=local_mean)
            wait_timer.signature = \
                signing.sign(
                    wait_timer.serialize(),
                    poet_private_key)

            return wait_timer

    @classmethod
    def deserialize_wait_timer(cls, serialized_timer, signature):
        return \
            EnclaveWaitTimer.wait_timer_from_serialized(
                serialized_timer=serialized_timer,
                signature=signature)

    @classmethod
    def create_wait_certificate(cls, sealed_signup_data, wait_timer,
                                block_hash):
        with cls._lock:
            # Extract keys from the 'sealed' signup data
            if sealed_signup_data is None:
                raise ValueError('Sealed Signup Data is None')
            signup_data = \
                json2dict(
                    base64.b64decode(sealed_signup_data.encode()).decode())
            poet_private_key = signup_data['poet_private_key']
            poet_public_key = signup_data['poet_public_key']

            if poet_private_key is None or poet_public_key is None:
                raise \
                    ValueError(
                        'Invalid signup data. No poet key(s).')

            # Several criteria need to be met before we can create a wait
            # certificate:
            # 1. This signup data was used to sign this timer.
            #    i.e. the key sealed / unsealed by the TEE signed this
            #    wait timer.
            # 2. This timer has expired
            # 3. This timer has not timed out
            #
            # In a TEE implementation we would check HW counter agreement.
            # We can't usefully simulate a HW counter though.
            # i.e. wait_timer.counter_value == signup_data.counter.value

            #
            # Note - we make a concession for the genesis block (i.e., a wait
            # timer for which the previous certificate ID is the Null
            # identifier) in that we don't require the timer to have expired
            # and we don't worry about the timer having timed out.

            if wait_timer is None or \
                    not signing.verify(
                        wait_timer.serialize(),
                        wait_timer.signature,
                        poet_public_key):
                raise \
                    ValueError(
                        'Validator is not using the current wait timer')

            is_not_genesis_block = \
                (wait_timer.previous_certificate_id !=
                 NULL_BLOCK_IDENTIFIER)

            now = time.time()
            expire_time = \
                wait_timer.request_time + \
                wait_timer.duration

            if is_not_genesis_block and now < expire_time:
                raise \
                    ValueError(
                        'Cannot create wait certificate because timer has '
                        'not expired')

            time_out_time = \
                wait_timer.request_time + \
                wait_timer.duration + \
                TIMER_TIMEOUT_PERIOD

            if is_not_genesis_block and time_out_time < now:
                raise \
                    ValueError(
                        'Cannot create wait certificate because timer '
                        'has timed out')

            # Create a random nonce for the certificate.  For our "random"
            # nonce we will take the timer signature, concat that with the
            # current time, JSON-ize it and create a SHA-256 hash over it.
            # Probably not considered random by security professional
            # standards, but it is good enough for the simulator.
            random_string = \
                dict2json({
                    'wait_timer_signature': wait_timer.signature,
                    'now': datetime.datetime.utcnow().isoformat()
                })
            nonce = hashlib.sha256(random_string.encode()).hexdigest()

            # First create a new enclave wait certificate using the data
            # provided and then sign the certificate with the PoET private key
            wait_certificate = \
                EnclaveWaitCertificate.wait_certificate_with_wait_timer(
                    wait_timer=wait_timer,
                    nonce=nonce,
                    block_hash=block_hash)
            wait_certificate.signature = \
                signing.sign(
                    wait_certificate.serialize(),
                    poet_private_key)

            # In a TEE implementation we would increment the HW counter here
            # to prevent replay.
            # We can't usefully simulate a HW counter though.

            return wait_certificate

    @classmethod
    def deserialize_wait_certificate(cls, serialized_certificate, signature):
        return \
            EnclaveWaitCertificate.wait_certificate_from_serialized(
                serialized_certificate=serialized_certificate,
                signature=signature)

    @classmethod
    def verify_wait_certificate(cls, certificate, poet_public_key):
        # Since the signing module uses a hex-encoded string as the canonical
        # format for public keys and we should be handed a public key that was
        # part of signup information created by us, don't bother decoding
        # the public key.
        if not \
            signing.verify(
                certificate.serialize(),
                certificate.signature,
                poet_public_key):
            raise ValueError('Wait certificate signature does not match')
Esempio n. 30
0
    def create_signup_info(cls, originator_public_key_hash, nonce):
        with cls._lock:
            # First we need to create a public/private key pair for the PoET
            # enclave to use.
            # Counter ID is a placeholder for a hardware counter in a TEE.
            poet_private_key = signing.generate_private_key()
            poet_public_key = \
                signing.generate_public_key(poet_private_key)
            counter_id = None

            # Simulate sealing (encrypting) the signup data.
            signup_data = {
                'poet_private_key': poet_private_key,
                'poet_public_key': poet_public_key,
                'counter_id': counter_id
            }
            sealed_signup_data = \
                base64.b64encode(
                    dict2json(signup_data).encode()).decode('utf-8')

            # Build up a fake SGX quote containing:
            # 1. The basename
            # 2. The report body that contains:
            #    a. The enclave measurement
            #    b. The report data SHA256(SHA256(OPK)|PPK)
            sgx_basename = \
                sgx_structs.SgxBasename(name=cls.__VALID_BASENAME__)
            sgx_measurement = \
                sgx_structs.SgxMeasurement(
                    m=cls.__VALID_ENCLAVE_MEASUREMENT__)

            hash_input = \
                '{0}{1}'.format(
                    originator_public_key_hash.upper(),
                    poet_public_key.upper()).encode()
            report_data = hashlib.sha256(hash_input).digest()
            sgx_report_data = sgx_structs.SgxReportData(d=report_data)
            sgx_report_body = \
                sgx_structs.SgxReportBody(
                    mr_enclave=sgx_measurement,
                    report_data=sgx_report_data)

            sgx_quote = \
                sgx_structs.SgxQuote(
                    basename=sgx_basename,
                    report_body=sgx_report_body)

            # Create a fake PSE manifest.  A base64 encoding of the
            # originator public key hash should suffice.
            pse_manifest = \
                base64.b64encode(originator_public_key_hash.encode())

            timestamp = datetime.datetime.now().isoformat()

            # Fake our "proof" data.
            verification_report = {
                'epidPseudonym':
                cls._anti_sybil_id,
                'id':
                base64.b64encode(
                    hashlib.sha256(
                        timestamp.encode()).hexdigest().encode()).decode(),
                'isvEnclaveQuoteStatus':
                'OK',
                'isvEnclaveQuoteBody':
                base64.b64encode(sgx_quote.serialize_to_bytes()).decode(),
                'pseManifestStatus':
                'OK',
                'pseManifestHash':
                hashlib.sha256(base64.b64decode(pse_manifest)).hexdigest(),
                'nonce':
                nonce,
                'timestamp':
                timestamp
            }

            # Serialize the verification report, sign it, and then put
            # in the proof data
            verification_report_json = dict2json(verification_report)
            signature = \
                cls._report_private_key.sign(
                    verification_report_json.encode(),
                    padding.PKCS1v15(),
                    hashes.SHA256())

            proof_data_dict = {
                'evidence_payload': {
                    'pse_manifest': pse_manifest.decode()
                },
                'verification_report': verification_report_json,
                'signature': base64.b64encode(signature).decode()
            }
            proof_data = dict2json(proof_data_dict)

            return \
                EnclaveSignupInfo(
                    poet_public_key=signup_data['poet_public_key'],
                    proof_data=proof_data,
                    anti_sybil_id=cls._anti_sybil_id,
                    sealed_signup_data=sealed_signup_data)