Пример #1
0
def get_blockchain_events(
    web3: Web3,
    contract_manager: ContractManager,
    chain_state: BlockchainState,
    to_block: BlockNumber,
    query_ms: bool = True,
) -> Tuple[BlockchainState, List[Event]]:
    # increment by one, as latest_known_block has been queried last time already
    from_block = BlockNumber(chain_state.latest_known_block + 1)

    # Check if the current block was already processed
    if from_block > to_block:
        return chain_state, []

    new_chain_state = deepcopy(chain_state)
    log.info('Querying new block(s)',
             from_block=from_block,
             end_block=to_block)

    # first check for new token networks and add to state
    registry_events = query_blockchain_events(
        web3=web3,
        contract_manager=contract_manager,
        contract_address=new_chain_state.token_network_registry_address,
        contract_name=CONTRACT_TOKEN_NETWORK_REGISTRY,
        topics=create_registry_event_topics(contract_manager),
        from_block=from_block,
        to_block=to_block,
    )

    events: List[Event] = []
    for event in registry_events:
        token_network_address = event['args']['token_network_address']
        token_address = event['args']['token_address']
        block_number = event['blockNumber']

        events.append(
            ReceiveTokenNetworkCreatedEvent(
                token_address=token_address,
                token_network_address=token_network_address,
                block_number=block_number,
            ))
        new_chain_state.token_network_addresses.append(
            event['args']['token_network_address'])

    # then check all token networks
    for token_network_address in new_chain_state.token_network_addresses:
        network_events = query_blockchain_events(
            web3=web3,
            contract_manager=contract_manager,
            contract_address=Address(token_network_address),
            contract_name=CONTRACT_TOKEN_NETWORK,
            topics=[None],
            from_block=from_block,
            to_block=to_block,
        )

        for event in network_events:
            event_name = event['event']

            common_infos = dict(
                token_network_address=event['address'],
                channel_identifier=event['args']['channel_identifier'],
                block_number=event['blockNumber'],
            )

            if event_name == ChannelEvent.OPENED:
                events.append(
                    ReceiveChannelOpenedEvent(
                        participant1=event['args']['participant1'],
                        participant2=event['args']['participant2'],
                        settle_timeout=event['args']['settle_timeout'],
                        **common_infos,
                    ))
            elif event_name == ChannelEvent.DEPOSIT:
                events.append(
                    ReceiveChannelNewDepositEvent(
                        participant_address=event['args']['participant'],
                        total_deposit=event['args']['total_deposit'],
                        **common_infos,
                    ))
            elif event_name == ChannelEvent.CLOSED:
                events.append(
                    ReceiveChannelClosedEvent(closing_participant=event['args']
                                              ['closing_participant'],
                                              **common_infos))
            elif event_name == ChannelEvent.BALANCE_PROOF_UPDATED:
                events.append(
                    ReceiveNonClosingBalanceProofUpdatedEvent(
                        closing_participant=event['args']
                        ['closing_participant'],
                        nonce=event['args']['nonce'],
                        **common_infos,
                    ))
            elif event_name == ChannelEvent.SETTLED:
                events.append(ReceiveChannelSettledEvent(**common_infos))

    # get events from monitoring service contract
    if query_ms:
        monitoring_events = get_monitoring_blockchain_events(
            web3=web3,
            contract_manager=contract_manager,
            chain_state=new_chain_state,
            from_block=from_block,
            to_block=to_block,
        )
        events.extend(monitoring_events)

    # commit new block number
    events.append(UpdatedHeadBlockEvent(head_block_number=to_block))

    return new_chain_state, events
Пример #2
0
def test_crash(tmpdir, get_accounts, get_private_key, mockchain):  # pylint: disable=too-many-locals
    """ Process blocks and compare results with/without crash

    A somewhat meaninful crash handling is simulated by not including the
    UpdatedHeadBlockEvent in every block.
    """
    channel_identifier = ChannelID(3)
    c1, c2 = get_accounts(2)
    token_network_address = TokenNetworkAddress(
        to_canonical_address(get_random_address()))
    balance_proof = HashedBalanceProof(
        nonce=Nonce(1),
        transferred_amount=TokenAmount(2),
        priv_key=get_private_key(c1),
        channel_identifier=channel_identifier,
        token_network_address=token_network_address,
        chain_id=ChainID(1),
        additional_hash="0x%064x" % 0,
        locked_amount=0,
        locksroot=encode_hex(LOCKSROOT_OF_NO_LOCKS),
    )
    monitor_request = balance_proof.get_monitor_request(
        get_private_key(c2),
        reward_amount=TokenAmount(0),
        msc_address=TEST_MSC_ADDRESS)

    events = [
        [
            ReceiveChannelOpenedEvent(
                token_network_address=token_network_address,
                channel_identifier=channel_identifier,
                participant1=c1,
                participant2=c2,
                settle_timeout=20,
                block_number=BlockNumber(0),
            )
        ],
        [UpdatedHeadBlockEvent(BlockNumber(1))],
        [
            ActionMonitoringTriggeredEvent(
                token_network_address=token_network_address,
                channel_identifier=channel_identifier,
                non_closing_participant=c2,
            )
        ],
        [UpdatedHeadBlockEvent(BlockNumber(3))],
    ]
    mockchain(events)

    server_private_key = get_random_privkey()

    contracts = {
        CONTRACT_TOKEN_NETWORK_REGISTRY: ContractMock(),
        CONTRACT_MONITORING_SERVICE: ContractMock(),
        CONTRACT_USER_DEPOSIT: ContractMock(),
        CONTRACT_SERVICE_REGISTRY: ContractMock(),
    }

    def new_ms(filename):
        ms = MonitoringService(
            web3=Web3Mock(),
            private_key=server_private_key,
            contracts=contracts,
            db_filename=os.path.join(tmpdir, filename),
        )
        msc = Mock()
        ms.context.monitoring_service_contract = msc
        ms.monitor_mock = msc.functions.monitor.return_value.transact  # type: ignore
        ms.monitor_mock.return_value = bytes(0)  # type: ignore
        return ms

    # initialize both monitoring services
    stable_ms = new_ms("stable.db")
    crashy_ms = new_ms("crashy.db")
    for ms in [stable_ms, crashy_ms]:
        ms.database.conn.execute(
            "INSERT INTO token_network(address) VALUES (?)",
            [to_checksum_address(token_network_address)],
        )
        ms.context.ms_state.blockchain_state.token_network_addresses = [
            token_network_address
        ]
        ms.database.upsert_monitor_request(monitor_request)
        ms.database.conn.commit()

    # process each block and compare results between crashy and stable ms
    for to_block in range(len(events)):
        crashy_ms = new_ms("crashy.db")  # new instance to simulate crash
        stable_ms.monitor_mock.reset_mock()  # clear calls from last block
        result_state: List[dict] = []
        for ms in [stable_ms, crashy_ms]:
            ms._process_new_blocks(to_block)  # pylint: disable=protected-access
            result_state.append(
                dict(
                    blockchain_state=ms.context.ms_state.blockchain_state,
                    db_dump=list(ms.database.conn.iterdump()),
                    monitor_calls=ms.monitor_mock.mock_calls,
                ))

        # both instances should have the same state after processing
        for stable_state, crashy_state in zip(result_state[0].values(),
                                              result_state[1].values()):
            # do asserts for each key separately to get better error messages
            assert stable_state == crashy_state
Пример #3
0
def get_blockchain_events(
    web3: Web3,
    token_network_addresses: List[TokenNetworkAddress],
    chain_state: BlockchainState,
    from_block: BlockNumber,
    to_block: BlockNumber,
) -> List[Event]:
    # Check if the current block was already processed
    if from_block > to_block:
        return []

    log.info(
        "Querying new block(s)",
        from_block=from_block,
        to_block=to_block,
        # When `to_block` == `from_block` we query one block, so add one
        num_blocks=to_block - from_block + 1,
    )

    # first check for new token networks and add to state
    registry_events = query_blockchain_events(
        web3=web3,
        contract_addresses=[chain_state.token_network_registry_address],
        from_block=from_block,
        to_block=to_block,
    )

    events: List[Event] = []
    for event_dict in registry_events:
        token_network_address = TokenNetworkAddress(
            to_canonical_address(event_dict["args"]["token_network_address"]))
        events.append(
            ReceiveTokenNetworkCreatedEvent(
                token_network_address=token_network_address,
                token_address=TokenAddress(
                    to_canonical_address(event_dict["args"]["token_address"])),
                block_number=event_dict["blockNumber"],
            ))
        token_network_addresses.append(token_network_address)

    # then check all token networks
    network_events = query_blockchain_events(
        web3=web3,
        contract_addresses=token_network_addresses,  # type: ignore
        from_block=from_block,
        to_block=to_block,
    )

    for event_dict in network_events:
        event = parse_token_network_event(event_dict)
        if event:
            events.append(event)

    # get events from monitoring service contract, this only queries the chain
    # if the monitor contract address is set in chain_state
    monitoring_events = get_monitoring_blockchain_events(
        web3=web3,
        monitor_contract_address=chain_state.monitor_contract_address,
        from_block=from_block,
        to_block=to_block,
    )
    events.extend(monitoring_events)

    # commit new block number
    events.append(UpdatedHeadBlockEvent(head_block_number=to_block))

    return events
Пример #4
0
def test_crash(tmpdir, mockchain):  # pylint: disable=too-many-locals
    """ Process blocks and compare results with/without crash

    A somewhat meaninful crash handling is simulated by not including the
    UpdatedHeadBlockEvent in every block.
    """
    token_address = Address("0x" + "1" * 40)
    token_network_address = TokenNetworkAddress("0x" + "2" * 40)
    channel_id = ChannelID(1)
    p1 = Address("0x" + "3" * 40)
    p2 = Address("0x" + "4" * 40)
    events = [
        [
            ReceiveTokenNetworkCreatedEvent(
                token_address=token_address,
                token_network_address=token_network_address,
                block_number=BlockNumber(1),
            )
        ],
        [UpdatedHeadBlockEvent(BlockNumber(2))],
        [
            ReceiveChannelOpenedEvent(
                token_network_address=token_network_address,
                channel_identifier=channel_id,
                participant1=p1,
                participant2=p2,
                settle_timeout=1000,
                block_number=BlockNumber(3),
            )
        ],
        [UpdatedHeadBlockEvent(BlockNumber(4))],
    ]
    mockchain(events)

    server_private_key = get_random_privkey()
    contracts = {
        CONTRACT_TOKEN_NETWORK_REGISTRY: ContractMock(),
        CONTRACT_USER_DEPOSIT: ContractMock(),
    }

    def new_service(filename):
        service = PathfindingService(
            web3=Web3Mock(),
            private_key=server_private_key,
            contracts=contracts,
            db_filename=os.path.join(tmpdir, filename),
        )
        return service

    # initialize stable service
    stable_service = new_service("stable.db")

    # process each block and compare results between crashy and stable service
    for to_block in range(len(events)):
        crashy_service = new_service(
            "crashy.db")  # new instance to simulate crash
        result_state: List[dict] = []
        for service in [stable_service, crashy_service]:
            service._process_new_blocks(BlockNumber(to_block))  # pylint: disable=protected-access
            result_state.append(
                dict(db_dump=list(service.database.conn.iterdump())))

        # both instances should have the same state after processing
        for stable_state, crashy_state in zip(result_state[0].values(),
                                              result_state[1].values()):
            # do asserts for each key separately to get better error messages
            assert stable_state == crashy_state
Пример #5
0
def test_crash(
    monitoring_service: MonitoringService,  # adds stake in ServiceRegistry
    web3,
    contracts_manager,
    server_private_key,
    token_network_registry_contract,
    monitoring_service_contract,
    user_deposit_contract,
    tmpdir,
    generate_raiden_clients,
    token_network,
):
    """ Process blocks and compare results with/without crash

    A somewhat meaninful crash handling is simulated by not including the
    UpdatedHeadBlockEvent in every block.
    """
    channel_identifier = ChannelID(3)
    c1, c2 = generate_raiden_clients(2)
    monitor_request = c2.get_monitor_request(
        balance_proof=c1.get_balance_proof(
            channel_id=channel_identifier,
            nonce=1,
            additional_hash='0x11',
            transferred_amount=2,
            locked_amount=0,
            locksroot='0x00',
        ),
        reward_amount=0,
    )

    events = [
        [
            ReceiveChannelOpenedEvent(
                token_network_address=token_network.address,
                channel_identifier=channel_identifier,
                participant1=c1.address,
                participant2=c2.address,
                settle_timeout=20,
                block_number=BlockNumber(0),
            )
        ],
        [UpdatedHeadBlockEvent(BlockNumber(1))],
        [
            ActionMonitoringTriggeredEvent(
                token_network_address=token_network.address,
                channel_identifier=channel_identifier,
                non_closing_participant=c2.address,
            )
        ],
        [UpdatedHeadBlockEvent(BlockNumber(3))],
    ]

    def new_ms(filename):
        ms = MonitoringService(
            web3=web3,
            private_key=server_private_key,
            contracts={
                CONTRACT_TOKEN_NETWORK_REGISTRY:
                token_network_registry_contract,
                CONTRACT_MONITORING_SERVICE: monitoring_service_contract,
                CONTRACT_USER_DEPOSIT: user_deposit_contract,
            },
            db_filename=os.path.join(tmpdir, filename),
        )
        ms.bcl = MockBlockchainListener(events)  # type: ignore
        msc = Mock()
        ms.context.monitoring_service_contract = msc
        ms.monitor_mock = msc.functions.monitor.return_value.transact  # type: ignore
        ms.monitor_mock.return_value = bytes(0)  # type: ignore
        return ms

    # initialize both monitoring services
    stable_ms = new_ms('stable.db')
    crashy_ms = new_ms('crashy.db')
    for ms in [stable_ms, crashy_ms]:
        ms.database.conn.execute(
            "INSERT INTO token_network(address) VALUES (?)",
            [token_network.address])
        ms.context.ms_state.blockchain_state.token_network_addresses = [
            token_network.address
        ]
        ms.database.upsert_monitor_request(monitor_request)
        ms.database.conn.commit()

    # process each block and compare results between crashy and stable ms
    for to_block in range(len(events)):
        crashy_ms = new_ms('crashy.db')  # new instance to simulate crash
        stable_ms.monitor_mock.reset_mock()  # clear calls from last block
        result_state: List[dict] = []
        for ms in [stable_ms, crashy_ms]:
            ms._process_new_blocks(to_block)
            result_state.append(
                dict(
                    blockchain_state=ms.context.ms_state.blockchain_state,
                    db_dump=list(ms.database.conn.iterdump()),
                    monitor_calls=ms.monitor_mock.mock_calls,
                ))

        # both instances should have the same state after processing
        for stable_state, crashy_state in zip(result_state[0].values(),
                                              result_state[1].values()):
            # do asserts for each key separately to get better error messages
            assert stable_state == crashy_state
def test_crash(tmpdir, mockchain):  # pylint: disable=too-many-locals
    """Process blocks and compare results with/without crash

    A somewhat meaninful crash handling is simulated by not including the
    UpdatedHeadBlockEvent in every block.
    """
    token_address = TokenAddress(bytes([1] * 20))
    token_network_address = TokenNetworkAddress(bytes([2] * 20))
    channel_id = ChannelID(1)
    p1 = Address(bytes([3] * 20))
    p2 = Address(bytes([4] * 20))
    events = [
        [
            ReceiveTokenNetworkCreatedEvent(
                token_address=token_address,
                token_network_address=token_network_address,
                settle_timeout=DEFAULT_TOKEN_NETWORK_SETTLE_TIMEOUT,
                block_number=BlockNumber(1),
            )
        ],
        [UpdatedHeadBlockEvent(BlockNumber(2))],
        [
            ReceiveChannelOpenedEvent(
                token_network_address=token_network_address,
                channel_identifier=channel_id,
                participant1=p1,
                participant2=p2,
                block_number=BlockNumber(3),
            )
        ],
        [UpdatedHeadBlockEvent(BlockNumber(4))],
    ]
    mockchain(events)

    server_private_key = PrivateKey(get_random_privkey())
    contracts = {
        CONTRACT_TOKEN_NETWORK_REGISTRY: ContractMock(),
        CONTRACT_USER_DEPOSIT: ContractMock(),
    }

    def new_service(filename):
        service = PathfindingService(
            web3=Web3Mock(),
            private_key=server_private_key,
            contracts=contracts,
            sync_start_block=BlockNumber(0),
            required_confirmations=BlockTimeout(0),
            poll_interval=0,
            db_filename=os.path.join(tmpdir, filename),
        )
        return service

    # initialize stable service
    stable_service = new_service("stable.db")

    # process each block and compare results between crashy and stable service
    for to_block in range(len(events)):
        crashy_service = new_service("crashy.db")  # new instance to simulate crash
        result_state: List[dict] = []
        for service in [stable_service, crashy_service]:
            service._process_new_blocks(BlockNumber(to_block))  # pylint: disable=protected-access
            result_state.append(dict(db_dump=list(service.database.conn.iterdump())))

        # both instances should have the same state after processing
        for stable_state, crashy_state in zip(result_state[0].values(), result_state[1].values()):
            if isinstance(stable_state, BlockchainState):
                assert stable_state.chain_id == crashy_state.chain_id
                assert (
                    stable_state.token_network_registry_address
                    == crashy_state.token_network_registry_address
                )
                assert stable_state.latest_committed_block == crashy_state.latest_committed_block
                assert (
                    stable_state.monitor_contract_address == crashy_state.monitor_contract_address
                )
                # Do not compare `current_event_filter_interval`, this is allowed to be different
            else:
                assert stable_state == crashy_state

        crashy_service.database.conn.close()  # close the db connection so we can access it again
Пример #7
0
def get_blockchain_events(
    web3: Web3,
    contract_manager: ContractManager,
    chain_state: BlockchainState,
    to_block: BlockNumber,
) -> Tuple[BlockchainState, List[Event]]:
    # increment by one, as latest_known_block has been queried last time already
    from_block = BlockNumber(chain_state.latest_known_block + 1)

    # Check if the current block was already processed
    if from_block > to_block:
        return chain_state, []

    new_chain_state = deepcopy(chain_state)
    log.info("Querying new block(s)",
             from_block=from_block,
             end_block=to_block)

    # first check for new token networks and add to state
    registry_events = query_blockchain_events(
        web3=web3,
        contract_manager=contract_manager,
        contract_address=new_chain_state.token_network_registry_address,
        contract_name=CONTRACT_TOKEN_NETWORK_REGISTRY,
        topics=create_registry_event_topics(contract_manager),
        from_block=from_block,
        to_block=to_block,
    )

    events: List[Event] = []
    for event_dict in registry_events:
        events.append(
            ReceiveTokenNetworkCreatedEvent(
                token_network_address=decode_hex(
                    event_dict["args"]["token_network_address"]),
                token_address=decode_hex(event_dict["args"]["token_address"]),
                block_number=event_dict["blockNumber"],
            ))
        new_chain_state.token_network_addresses.append(
            event_dict["args"]["token_network_address"])

    # then check all token networks
    for token_network_address in new_chain_state.token_network_addresses:
        network_events = query_blockchain_events(
            web3=web3,
            contract_manager=contract_manager,
            contract_address=Address(token_network_address),
            contract_name=CONTRACT_TOKEN_NETWORK,
            topics=[None],
            from_block=from_block,
            to_block=to_block,
        )

        for event_dict in network_events:
            event = parse_token_network_event(event_dict)
            if event:
                events.append(event)

    # get events from monitoring service contract, this only queries the chain
    # if the monitor contract address is set in chain_state
    monitoring_events = get_monitoring_blockchain_events(
        web3=web3,
        contract_manager=contract_manager,
        chain_state=new_chain_state,
        from_block=from_block,
        to_block=to_block,
    )
    events.extend(monitoring_events)

    # commit new block number
    events.append(UpdatedHeadBlockEvent(head_block_number=to_block))

    return new_chain_state, events