예제 #1
0
    def test_add_subscriber(self):
        """Test adding a subscriber, who has no known blocks.

        This scenerio is valid for subscribers who have never connected and
        would need to receive all deltas since the genesis block.

        On registration, the subscriber should receive one event, comprised
        of the state changes for the genesis block.
        """
        mock_service = Mock()
        block_tree_manager = BlockTreeManager()

        delta_store = StateDeltaStore(DictDatabase())

        delta_processor = StateDeltaProcessor(
            service=mock_service,
            state_delta_store=delta_store,
            block_store=block_tree_manager.block_store)

        delta_store.save_state_deltas(
            block_tree_manager.chain_head.state_root_hash, [
                StateChange(address='deadbeef0000000',
                            value='my_genesis_value'.encode(),
                            type=StateChange.SET),
                StateChange(address='a14ea01',
                            value='some other state value'.encode(),
                            type=StateChange.SET)
            ])

        delta_processor.add_subscriber('test_conn_id', [], ['deadbeef'])

        self.assertEqual(['test_conn_id'], delta_processor.subscriber_ids)

        # test that it catches up, and receives the events from the chain head.
        # In this case, it should just be once, as the chain head is the
        # genesis block
        chain_head = block_tree_manager.chain_head
        mock_service.send.assert_called_with(
            validator_pb2.Message.STATE_DELTA_EVENT,
            StateDeltaEvent(block_id=chain_head.identifier,
                            block_num=chain_head.block_num,
                            state_root_hash=chain_head.state_root_hash,
                            previous_block_id=chain_head.previous_block_id,
                            state_changes=[
                                StateChange(address='deadbeef0000000',
                                            value='my_genesis_value'.encode(),
                                            type=StateChange.SET)
                            ]).SerializeToString(),
            connection_id='test_conn_id')
예제 #2
0
    def test_state_store_get_and_set(self):
        """Tests that we correctly get and set state changes to a
        StateDeltaStore.

        This tests sets a list of state change values and then gets them back,
        ensuring that the data is the same.
        """

        database = DictDatabase()

        delta_store = StateDeltaStore(database)

        changes = [
            StateChange(address='a100000' + str(i),
                        value=str(i).encode(),
                        type=StateChange.SET) for i in range(0, 10)
        ]

        delta_store.save_state_deltas('my_state_root_hash', changes)

        stored_changes = delta_store.get_state_deltas('my_state_root_hash')
        # This is a list-like repeated field, but to make it comparable we'll
        # have to generate a new list
        stored_changes = [c for c in stored_changes]

        self.assertEqual(changes, stored_changes)
예제 #3
0
    def test_publish_deltas(self):
        """Tests that a subscriber filtering on an address prefix receives only
        the changes in an event that match.
        """
        mock_service = Mock()
        block_tree_manager = BlockTreeManager()

        database = DictDatabase()
        delta_store = StateDeltaStore(database)

        delta_processor = StateDeltaProcessor(
            service=mock_service,
            state_delta_store=delta_store,
            block_store=block_tree_manager.block_store)

        delta_processor.add_subscriber(
            'test_conn_id', [block_tree_manager.chain_head.identifier],
            ['deadbeef'])

        next_block = block_tree_manager.generate_block()
        # State added during context squash for our block
        delta_store.save_state_deltas(next_block.header.state_root_hash, [
            StateChange(address='deadbeef01',
                        value='my_state_Value'.encode(),
                        type=StateChange.SET),
            StateChange(address='a14ea01',
                        value='some other state value'.encode(),
                        type=StateChange.SET)
        ])

        # call to publish deltas for that block to the subscribers
        delta_processor.publish_deltas(next_block)

        mock_service.send.assert_called_with(
            validator_pb2.Message.STATE_DELTA_EVENT,
            StateDeltaEvent(
                block_id=next_block.identifier,
                block_num=next_block.header.block_num,
                state_root_hash=next_block.header.state_root_hash,
                previous_block_id=next_block.header.previous_block_id,
                state_changes=[
                    StateChange(address='deadbeef01',
                                value='my_state_Value'.encode(),
                                type=StateChange.SET)
                ]).SerializeToString(),
            connection_id='test_conn_id')
예제 #4
0
    def test_get_events_ignore_bad_blocks(self):
        """Tests that the StateDeltaGetEventsHandler will return a response
        containing only the events for blocks that exists.
        """
        block_tree_manager = BlockTreeManager()

        delta_store = StateDeltaStore(DictDatabase())

        delta_store.save_state_deltas(
            block_tree_manager.chain_head.state_root_hash, [
                StateChange(address='deadbeef0000000',
                            value='my_genesis_value'.encode(),
                            type=StateChange.SET),
                StateChange(address='a14ea01',
                            value='some other state value'.encode(),
                            type=StateChange.SET)
            ])

        handler = StateDeltaGetEventsHandler(block_tree_manager.block_store,
                                             delta_store)

        request = StateDeltaGetEventsRequest(
            block_ids=[
                block_tree_manager.chain_head.identifier, 'somebadblockid'
            ],
            address_prefixes=['deadbeef']).SerializeToString()

        response = handler.handle('test_conn_id', request)
        self.assertEqual(HandlerStatus.RETURN, response.status)
        self.assertEqual(StateDeltaGetEventsResponse.OK,
                         response.message_out.status)

        chain_head = block_tree_manager.chain_head
        self.assertEqual([
            StateDeltaEvent(block_id=chain_head.identifier,
                            block_num=chain_head.block_num,
                            state_root_hash=chain_head.state_root_hash,
                            previous_block_id=chain_head.previous_block_id,
                            state_changes=[
                                StateChange(address='deadbeef0000000',
                                            value='my_genesis_value'.encode(),
                                            type=StateChange.SET)
                            ])
        ], [event for event in response.message_out.events])
예제 #5
0
    def test_receipt_store_get_and_set(self):
        """Tests that we correctly get and set state changes to a ReceiptStore.

        This test sets a list of receipts and then gets them back, ensuring that
        the data is the same.
        """

        receipt_store = TransactionReceiptStore(DictDatabase())

        receipts = []
        for i in range(10):
            state_changes = []
            events = []
            data = []

            for j in range(10):
                string = str(j)
                byte = string.encode()

                state_changes.append(
                    StateChange(address='a100000' + string,
                                value=byte,
                                type=StateChange.SET))
                events.append(
                    Event(
                        event_type="test",
                        data=byte,
                        attributes=[Event.Attribute(key=string,
                                                    value=string)]))
                data.append(
                    TransactionReceipt.Data(data_type="test", data=byte))

            receipts.append(
                TransactionReceipt(state_changes=state_changes,
                                   events=events,
                                   data=data))

        for i in range(len(receipts)):
            receipt_store.put(str(i), receipts[i])

        for i in range(len(receipts)):
            stored_receipt = receipt_store.get(str(i))

            self.assertEqual(stored_receipt.state_changes,
                             receipts[i].state_changes)
            self.assertEqual(stored_receipt.events, receipts[i].events)
            self.assertEqual(stored_receipt.data, receipts[i].data)
예제 #6
0
    def test_state_delta_events(self):
        """Test that sawtooth/state-delta events are generated correctly."""
        gen_data = [
            [("a", b"a", StateChange.SET), ("b", b"b", StateChange.DELETE)],
            [("a", b"a", StateChange.DELETE), ("d", b"d", StateChange.SET)],
            [("e", b"e", StateChange.SET)],
        ]
        change_sets = [[
            StateChange(address=address, value=value, type=change_type)
            for address, value, change_type in state_changes
        ] for state_changes in gen_data]
        receipts = [
            TransactionReceipt(state_changes=state_changes)
            for state_changes in change_sets
        ]
        extractor = ReceiptEventExtractor(receipts)

        factory = EventFilterFactory()
        events = extractor.extract([
            EventSubscription(event_type="sawtooth/state-delta",
                              filters=[factory.create("address", "a")]),
            EventSubscription(
                event_type="sawtooth/state-delta",
                filters=[
                    factory.create("address", "[ce]", EventFilter.REGEX_ANY)
                ],
            )
        ])
        self.assertEqual(events, [
            Event(
                event_type="sawtooth/state-delta",
                attributes=[
                    Event.Attribute(key="address", value=address)
                    for address in ["e", "d", "a", "b"]
                ],
                data=StateChangeList(state_changes=[
                    change_sets[2][0],
                    change_sets[1][1],
                    change_sets[1][0],
                    change_sets[0][1],
                ]).SerializeToString(),
            )
        ])
예제 #7
0
    def do_receipt_store_get_and_set():
        receipt_store = TransactionReceiptStore(DictDatabase())

        receipts = []
        for i in range(10):
            state_changes = []
            events = []
            data = []

            for j in range(10):
                string = str(j)
                byte = string.encode()

                state_changes.append(
                    StateChange(
                        address='a100000' + string,
                        value=byte,
                        type=StateChange.SET))
                events.append(
                    Event(
                        event_type="test",
                        data=byte,
                        attributes=[Event.Attribute(key=string,
                                                    value=string)]))
                data.append(byte)

            receipts.append(
                TransactionReceipt(
                    state_changes=state_changes, events=events, data=data))

        for i, receipt in enumerate(receipts):
            receipt_store.put(str(i), receipt)

        for i, receipt in enumerate(receipts):
            stored_receipt = receipt_store.get(str(i))

            assert stored_receipt.state_changes == receipt.state_changes
예제 #8
0
        def _squash(state_root, context_ids, persist, clean_up):
            contexts_in_chain = deque()
            contexts_in_chain.extend(context_ids)
            context_ids_already_searched = []
            context_ids_already_searched.extend(context_ids)

            # There is only one exit condition and that is when all the
            # contexts have been accessed once.
            updates = dict()
            deletes = set()
            while contexts_in_chain:
                current_c_id = contexts_in_chain.popleft()
                current_context = self._contexts[current_c_id]
                if not current_context.is_read_only():
                    current_context.make_read_only()

                addresses_w_values = current_context.get_all_if_set()
                for add, val in addresses_w_values.items():
                    # Since we are moving backwards through the graph of
                    # contexts, only update if the address hasn't been set
                    # or deleted
                    if add not in updates and add not in deletes:
                        updates[add] = val

                addresses_w_values = current_context.get_all_if_deleted()
                for add, _ in addresses_w_values.items():
                    # Since we are moving backwards through the graph of
                    # contexts, only add to deletes if the address hasn't been
                    # previously deleted or set in the graph
                    if add not in updates and add not in deletes:
                        deletes.add(add)

                for c_id in current_context.base_contexts:
                    if c_id not in context_ids_already_searched:
                        contexts_in_chain.append(c_id)
                        context_ids_already_searched.append(c_id)

            tree = MerkleDatabase(self._database, state_root)

            # filter the delete list to just those items in the tree
            deletes = [addr for addr in deletes if addr in tree]

            if not updates and not deletes:
                state_hash = state_root
            else:
                virtual = not persist
                state_hash = tree.update(updates, deletes, virtual=virtual)
                if persist:
                    # save the state changes to the state_delta_store
                    changes = [StateChange(address=addr,
                                           value=value,
                                           type=StateChange.SET)
                               for addr, value in updates.items()] +\
                              [StateChange(address=addr,
                                           type=StateChange.DELETE)
                               for addr in deletes]
                    self._state_delta_store.save_state_deltas(
                        state_hash, changes)

            if clean_up:
                self.delete_contexts(context_ids_already_searched)
            return state_hash