예제 #1
0
파일: router.py 프로젝트: wp4613/crossbar
        def ok(_):
            self.transports[transport_id] = router_transport
            if config['endpoint']['type'] == 'tcp':
                endpoint = 'TCP port {}'.format(config['endpoint']['port'])
                if 'portrange' in config['endpoint']:
                    transport_type = 'TCP/{} transport'.format(
                        config['endpoint']['portrange'])
                else:
                    transport_type = 'TCP/{} transport'.format(
                        config['endpoint']['port'])
            elif config['endpoint']['type'] == 'unix':
                endpoint = 'UDS path "{}"'.format(config['endpoint']['path'])
                transport_type = 'Unix domain socket transport'
            else:
                endpoint = 'unknown'
                transport_type = 'unknown'
            self.log.info(
                'Router {transport_type} started as transport "{transport_id}" and listening on {endpoint}',
                transport_type=hlval(transport_type),
                transport_id=hlid(transport_id),
                endpoint=hlval(endpoint))

            topic = '{}.on_router_transport_started'.format(self._uri_prefix)
            self.publish(topic, event, options=PublishOptions(exclude=caller))

            return router_transport.marshal()
예제 #2
0
    async def _initialize_mrealms(self, parallel=False):

        self._router_realms = {}
        self._container_workers = {}

        with self._db.begin() as txn:
            mrealms = list(self._schema.mrealms.select(txn, return_keys=False))

        if mrealms:
            self.log.info(
                'Initializing {cnt_mrealms} management realms (parallel={parallel}) ..',
                cnt_mrealms=hlval(len(mrealms)),
                parallel=hlval(parallel))
            if parallel:
                dl = []
                for mrealm in mrealms:
                    dl.append(self._initialize_mrealm(mrealm))
                if dl:
                    await DeferredList(dl)
            else:
                for mrealm in mrealms:
                    await self._initialize_mrealm(mrealm)
            self.log.info(
                'Ok, completed initializing {cnt_mrealms} management realms.',
                cnt_mrealms=hlval(len(mrealms)))
        else:
            self.log.info('No management realms no initialize. Done.')
예제 #3
0
        def _process_Market_MarketCreated(transactionHash, blockHash, args):
            #     /// Event emitted when a new market was created.
            #     event MarketCreated (bytes16 indexed marketId, uint32 marketSeq, address owner, string terms, string meta,
            #         address maker, uint256 providerSecurity, uint256 consumerSecurity, uint256 marketFee);
            self.log.info(
                '{event}: processing event (tx_hash={tx_hash}, block_hash={block_hash}) - XBR market created with ID {market_id})',
                event=hlcontract('XBRMarket.MarketCreated'),
                tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()),
                block_hash=hlid('0x' + binascii.b2a_hex(blockHash).decode()),
                market_id=hlid(uuid.UUID(bytes=args.marketId)))

            market_id = uuid.UUID(bytes=args.marketId)

            if args.terms:
                h = multihash.decode(multihash.from_b58_string(args.terms))
                if h.name != 'sha2-256':
                    self.log.warn(
                        'WARNING: XBRMarket.MarketCreated - terms "{terms}" is not an IPFS (sha2-256) b58-encoded multihash',
                        terms=hlval(args.terms))

            if args.meta:
                h = multihash.decode(multihash.from_b58_string(args.meta))
                if h.name != 'sha2-256':
                    self.log.warn(
                        'WARNING: XBRMarket.MarketCreated - meta "{meta}" is not an IPFS (sha2-256) b58-encoded multihash',
                        meta=hlval(args.meta))

            stored = False
            with self._db.begin(write=True) as txn:

                market = self._xbr.markets[txn, market_id]
                if market:
                    self.log.warn('{contract}(tx_hash={tx_hash}) record already stored in database.',
                                  contract=hlcontract('MarketCreated'),
                                  tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()))
                else:
                    market = cfxdb.xbr.market.Market()
                    market.market = market_id
                    market.timestamp = np.datetime64(time_ns(), 'ns')

                    # FIXME
                    # market.created = args.created

                    market.seq = args.marketSeq
                    market.owner = bytes(HexBytes(args.owner))
                    market.terms = args.terms
                    market.meta = args.meta
                    market.maker = bytes(HexBytes(args.maker))
                    market.provider_security = args.providerSecurity
                    market.consumer_security = args.consumerSecurity
                    market.market_fee = args.marketFee

                    self._xbr.markets[txn, market_id] = market
                    stored = True

            if stored:
                self.log.info('new {contract}(market_id={market_id}) record stored database!',
                              contract=hlcontract('MarketCreated'),
                              market_id=hlid(market_id))
예제 #4
0
        def _process_Network_MemberRegistered(transactionHash, blockHash, args):
            #     /// Event emitted when a new member joined the XBR Network.
            #     event MemberCreated (address indexed member, string eula, string profile, MemberLevel level);
            self.log.info(
                '{event}: processing event (tx_hash={tx_hash}, block_hash={block_hash}) - XBR member created at address {address})',
                event=hlcontract('XBRNetwork.MemberCreated'),
                tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()),
                block_hash=hlid('0x' + binascii.b2a_hex(blockHash).decode()),
                address=hlid(args.member))

            member_adr = bytes(HexBytes(args.member))

            if args.eula:
                h = multihash.decode(multihash.from_b58_string(args.eula))
                if h.name != 'sha2-256':
                    self.log.warn(
                        'WARNING: XBRNetwork.MemberCreated - eula "{eula}" is not an IPFS (sha2-256) b58-encoded multihash',
                        eula=hlval(args.eula))

            if args.profile:
                h = multihash.decode(multihash.from_b58_string(args.profile))
                if h.name != 'sha2-256':
                    self.log.warn(
                        'WARNING: XBRNetwork.MemberCreated - profile "{profile}" is not an IPFS (sha2-256) b58-encoded multihash',
                        eula=hlval(args.profile))

            stored = False
            with self._db.begin(write=True) as txn:

                member = self._xbr.members[txn, member_adr]
                if member:
                    self.log.warn('{contract}(tx_hash={tx_hash}) record already stored in database.',
                                  contract=hlcontract('TokenApproval'),
                                  tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()))
                else:
                    member = cfxdb.xbr.member.Member()
                    member.address = member_adr
                    member.timestamp = np.datetime64(time_ns(), 'ns')
                    member.registered = args.registered
                    member.eula = args.eula
                    member.profile = args.profile
                    member.level = args.level

                    self._xbr.members[txn, member_adr] = member
                    stored = True

            if stored:
                self.log.info('new {contract}(member_adr={member_adr}) record stored database!',
                              contract=hlcontract('MemberCreated'),
                              member_adr=hlid('0x' + binascii.b2a_hex(member_adr).decode()))
예제 #5
0
파일: router.py 프로젝트: ynohtna/crossbar
    def detach(self, session=None):
        self.log.debug('{func}(session={session})',
                       func=hltype(self.detach),
                       session=session)

        detached_session_ids = []
        if session is None:
            # detach all sessions from router
            for session in list(self._session_id_to_session.values()):
                self._detach(session)
                detached_session_ids.append(session._session_id)
        else:
            # detach single session from router
            self._detach(session)
            detached_session_ids.append(session._session_id)

        self.log.info(
            'detached session {session} from realm "{realm}" (authid="{authid}", authrole="{authrole}", detached {detached_session_ids} sessions total) {func}',
            func=hltype(self.detach),
            session=hlid(session._session_id) if session else '',
            authid=hlid(session._authid),
            authrole=hlid(session._authrole),
            detached_session_ids=hlval(len(detached_session_ids)),
            realm=hlid(session._realm))

        return detached_session_ids
예제 #6
0
파일: router.py 프로젝트: wp4613/crossbar
    def has_role(self, realm: str, authrole: str) -> bool:
        """
        Check if a role with the given name is currently running in the given realm.

        :param realm: WAMP realm (name, _not_ run-time ID).
        :type realm: str

        :param authrole: WAMP authentication role (URI, _not_ run-time ID).
        :type authrole: str

        :returns: True if realm is running.
        :rtype: bool
        """
        authrole = authrole or 'trusted'
        result = realm in self.realm_to_id and self.realm_to_id[
            realm] in self.realms
        if result:
            realm_id = self.realm_to_id[realm]
            result = (authrole in self.realms[realm_id].role_to_id
                      and self.realms[realm_id].role_to_id[authrole]
                      in self.realms[realm_id].roles)

            # note: this is to enable eg built-in "trusted" authrole
            result = result or authrole in self._service_sessions[realm]

        self.log.debug(
            '{func}(realm="{realm}", authrole="{authrole}") -> {result}',
            func=hltype(RouterController.has_role),
            realm=hlid(realm),
            authrole=hlid(authrole),
            result=hlval(result))
        return result
예제 #7
0
    def hello(self, realm, details):
        self.log.debug('{func}(realm="{realm}", details={details})',
                       func=hltype(self.hello),
                       realm=hlval(realm),
                       details=details)
        extra = details.authextra or {}

        for attr in ['proxy_authid', 'proxy_authrole', 'proxy_realm']:
            if attr not in extra:
                return types.Deny(message='missing required attribute {}'.format(attr))

        realm = extra['proxy_realm']
        details.authid = extra['proxy_authid']
        details.authrole = extra['proxy_authrole']
        details.authextra = extra.get('proxy_authextra', None)

        # remember the realm the client requested to join (if any)
        self._realm = realm
        self._authid = details.authid
        self._session_details['authmethod'] = 'anonymous'
        self._session_details['authextra'] = details.authextra
        self._authprovider = 'static'

        # FIXME: if cookie tracking is enabled, set authid to cookie value
        # self._authid = self._transport._cbtid

        principal = {'realm': realm, 'authid': details.authid, 'role': details.authrole, 'extra': details.authextra}

        error = self._assign_principal(principal)
        if error:
            return error

        return self._accept()
예제 #8
0
파일: process.py 프로젝트: wp4613/crossbar
    def onJoin(self, details):
        """
        Called when process has joined the node's management realm.
        """

        regs = yield self.register(
            self,
            prefix='{}.'.format(self._uri_prefix),
            options=RegisterOptions(details_arg='details'),
        )

        procs = []
        errors = []
        for reg in regs:
            if isinstance(reg, Failure):
                self.log.error("Failed to register management procedure: {f}",
                               f=reg,
                               log_failure=reg)
                errors.append(str(reg))
            else:
                procs.append(reg.procedure)

        if errors:
            raise ApplicationError('crossbar.error.cannot_start',
                                   'management API could not be initialized',
                                   errors=errors)
        else:
            self.log.debug(
                'Ok, registered {len_reg} management procedures on realm "{realm}" [{func}]:\n\n{procs}\n',
                len_reg=hlval(len(regs)),
                realm=hl(self.realm),
                func=hltype(self.onJoin),
                procs=hl(pformat(procs), color='white', bold=True))
        returnValue(regs)
예제 #9
0
 def restart_component():
     # Think: if this below start_component() fails,
     # we'll still schedule *exactly one* new re-start
     # attempt for it, right?
     self.log.info(
         '{func}: now restarting previously closed component {component_id} automatically .. [restart_mode={restart_mode}, was_clean={was_clean}]',
         func=hltype(_component_closed),
         component_id=hlid(component_id),
         restart_mode=hlval(self._restart_mode),
         was_clean=hlval(was_clean))
     return self.start_component(
         component_id,
         config,
         reload_modules=reload_modules,
         details=details,
     )
예제 #10
0
    def _process_block(self, w3, block_number, Events):
        """

        :param w3:
        :param block_number:
        :param Events:
        :return:
        """
        cnt = 0
        # filter by block, and XBR contract addresses
        # FIXME: potentially add filters for global data or market specific data for the markets started in this worker
        filter_params = {
            'address': [
                xbr.xbrtoken.address, xbr.xbrnetwork.address, xbr.xbrcatalog.address, xbr.xbrmarket.address,
                xbr.xbrchannel.address
            ],
            'fromBlock':
            block_number,
            'toBlock':
            block_number,
        }
        result = w3.eth.getLogs(filter_params)
        if result:
            for evt in result:
                receipt = w3.eth.getTransactionReceipt(evt['transactionHash'])
                for Event, handler in Events:
                    # FIXME: MismatchedABI pops up .. we silence this with errors=web3.logs.DISCARD
                    if hasattr(web3, 'logs') and web3.logs:
                        all_res = Event().processReceipt(receipt, errors=web3.logs.DISCARD)
                    else:
                        all_res = Event().processReceipt(receipt)
                    for res in all_res:
                        self.log.info('{handler} processing block {block_number} / txn {txn} with args {args}',
                                      handler=hl(handler.__name__),
                                      block_number=hlid(block_number),
                                      txn=hlid('0x' + binascii.b2a_hex(evt['transactionHash']).decode()),
                                      args=hlval(res.args))
                        handler(res.transactionHash, res.blockHash, res.args)
                        cnt += 1

        with self._db.begin(write=True) as txn:
            block = cfxdb.xbr.block.Block()
            block.timestamp = np.datetime64(time_ns(), 'ns')
            block.block_number = block_number
            # FIXME
            # block.block_hash = bytes()
            block.cnt_events = cnt
            self._xbr.blocks[txn, pack_uint256(block_number)] = block

        if cnt:
            self.log.info('Processed blockchain block {block_number}: processed {cnt} XBR events.',
                          block_number=hlid(block_number),
                          cnt=hlid(cnt))
        else:
            self.log.info('Processed blockchain block {block_number}: no XBR events found!',
                          block_number=hlid(block_number))

        return cnt
예제 #11
0
    def stop_web_transport_service(self, transport_id, path, details=None):
        """
        Stop a service on a Web transport.

        :param transport_id: The ID of the transport to stop the Web transport service on.
        :type transport_id: str

        :param path: The path (absolute URL, eg "/myservice1") of the service to stop.
        :type path: str

        :param details: Call details.
        :type details: :class:`autobahn.wamp.types.CallDetails`
        """
        self.log.info('{func}(transport_id={transport_id}, path="{path}")',
                      func=hltype(self.stop_web_transport_service),
                      transport_id=hlid(transport_id),
                      path=hlval(path))

        transport = self.transports.get(transport_id, None)
        if not transport or \
           not isinstance(transport, self.personality.RouterWebTransport) or \
           transport.state != self.personality.RouterTransport.STATE_STARTED:
            emsg = "Cannot stop service on Web transport: no transport with ID '{}' or transport is not a Web transport".format(
                transport_id)
            self.log.error(emsg)
            raise ApplicationError('crossbar.error.not_running', emsg)

        if path not in transport.root:
            emsg = "Cannot stop service on Web transport {}: no service running on path '{}'".format(
                transport_id, path)
            self.log.error(emsg)
            raise ApplicationError('crossbar.error.not_running', emsg)

        caller = details.caller if details else None
        self.publish(self._uri_prefix + '.on_web_transport_service_stopping',
                     transport_id,
                     path,
                     options=PublishOptions(exclude=caller))

        # now actually remove the web service. note: currently this is NOT async, but direct/sync.
        # FIXME: check that the underlying Twisted Web resource doesn't need any stopping too!
        del transport.root[path]

        on_web_transport_service_stopped = {
            'transport_id': transport_id,
            'path': path,
        }
        caller = details.caller if details else None
        self.publish(self._uri_prefix + '.on_web_transport_service_stopped',
                     transport_id,
                     path,
                     on_web_transport_service_stopped,
                     options=PublishOptions(exclude=caller))

        return on_web_transport_service_stopped
예제 #12
0
        def _process_Market_ActorJoined(transactionHash, blockHash, args):
            # Event emitted when a new actor joined a market.
            # event ActorJoined (bytes16 indexed marketId, address actor, uint8 actorType, uint joined, uint256 security, string meta);
            self.log.info(
                '{event}: processing event (tx_hash={tx_hash}, block_hash={block_hash}) - XBR market actor {actor} joined market {market_id})',
                event=hlcontract('XBRMarket.ActorJoined'),
                tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()),
                block_hash=hlid('0x' + binascii.b2a_hex(blockHash).decode()),
                actor=hlid(args.actor),
                market_id=hlid(uuid.UUID(bytes=args.marketId)))

            market_id = uuid.UUID(bytes=args.marketId)
            actor_adr = bytes(HexBytes(args.actor))
            actor_type = int(args.actorType)

            if args.meta:
                h = multihash.decode(multihash.from_b58_string(args.meta))
                if h.name != 'sha2-256':
                    self.log.warn(
                        'WARNING: XBRMarket.MarketCreated - meta "{meta}" is not an IPFS (sha2-256) b58-encoded multihash',
                        terms=hlval(args.meta))

            stored = False
            with self._db.begin(write=True) as txn:

                actor = self._xbr.actors[txn, (market_id, actor_adr, actor_type)]
                if actor:
                    self.log.warn('{contract}(tx_hash={tx_hash}) record already stored in database.',
                                  contract=hlcontract('MarketCreated'),
                                  tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()))
                else:
                    actor = cfxdb.xbr.actor.Actor()
                    actor.timestamp = np.datetime64(time_ns(), 'ns')
                    actor.market = market_id
                    actor.actor = actor_adr
                    actor.actor_type = actor_type

                    actor.joined = args.joined
                    actor.security = args.security
                    actor.meta = args.meta

                    self._xbr.actors[txn, (market_id, actor_adr, actor_type)] = actor
                    stored = True

            if stored:
                self.log.info(
                    'new {contract}(market_id={market_id}, actor_adr={actor_adr}, actor_type={actor_type}) record stored database!',
                    contract=hlcontract('ActorJoined'),
                    market_id=hlid(market_id),
                    actor_adr=hlid('0x' + binascii.b2a_hex(actor_adr).decode()),
                    actor_type=hlid(actor_type))
예제 #13
0
        def _process_Token_Approval(transactionHash, blockHash, args):
            # event Approval(address indexed from, address indexed to, uint256 value);
            self.log.info(
                '{event}: processing event (tx_hash={tx_hash}, block_hash={block_hash}) - {value} XBR token approved (on-chain) from owner {owner} to spender {spender})',
                event=hlcontract('XBRToken.Approval'),
                tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()),
                block_hash=hlid('0x' + binascii.b2a_hex(blockHash).decode()),
                value=hlval(int(args.value / 10**18)),
                owner=hlval(args.owner),
                spender=hlval(args.spender))

            stored = False
            with self._db.begin(write=True) as txn:

                transactionHash = bytes(transactionHash)

                token_approval = self._xbr.token_approvals[txn, transactionHash]
                if token_approval:
                    self.log.warn('{contract}(tx_hash={tx_hash}) record already stored in database.',
                                  contract=hlcontract('TokenApproval'),
                                  tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()))
                else:
                    token_approval = cfxdb.xbr.token.TokenApproval()

                    token_approval.tx_hash = transactionHash
                    token_approval.block_hash = bytes(blockHash)
                    token_approval.owner_address = bytes(HexBytes(args.owner))
                    token_approval.spender_address = bytes(HexBytes(args.spender))
                    token_approval.value = args.value

                    self._xbr.token_approvals[txn, transactionHash] = token_approval
                    stored = True

            if stored:
                self.log.info('new {contract}(tx_hash={tx_hash}) record stored database!',
                              contract=hlcontract('TokenApproval'),
                              tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()))
예제 #14
0
        def _process_Token_Transfer(transactionHash, blockHash, args):
            # event Transfer(address indexed from, address indexed to, uint256 value);
            self.log.info(
                '{event}: processing event (tx_hash={tx_hash}, block_hash={block_hash}) - {value} XBR token transferred (on-chain) from {_from} to {_to})',
                event=hlcontract('XBRToken.Transfer'),
                tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()),
                block_hash=hlid('0x' + binascii.b2a_hex(blockHash).decode()),
                value=hlval(int(args.value / 10**18)),
                _from=hlval(args['from']),
                _to=hlval(args.to))

            stored = False
            with self._db.begin(write=True) as txn:

                transactionHash = bytes(transactionHash)

                token_transfer = self._xbr.token_transfers[txn, transactionHash]
                if token_transfer:
                    self.log.warn('{contract}(tx_hash={tx_hash}) record already stored in database.',
                                  contract=hlcontract('TokenTransfer'),
                                  tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()))
                else:
                    token_transfer = cfxdb.xbr.token.TokenTransfer()

                    token_transfer.tx_hash = transactionHash
                    token_transfer.block_hash = bytes(blockHash)
                    token_transfer.from_address = bytes(HexBytes(args['from']))
                    token_transfer.to_address = bytes(HexBytes(args.to))
                    token_transfer.value = args.value

                    self._xbr.token_transfers[txn, transactionHash] = token_transfer
                    stored = True

            if stored:
                self.log.info('new {contract}(tx_hash={tx_hash}) record stored database!',
                              contract=hlcontract('TokenTransfer'),
                              tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()))
예제 #15
0
파일: router.py 프로젝트: wp4613/crossbar
    def has_realm(self, realm: str) -> bool:
        """
        Check if a realm with the given name is currently running.

        :param realm: Realm name (_not_ ID).
        :type realm: str

        :returns: True if realm is running.
        :rtype: bool
        """
        result = realm in self.realm_to_id and self.realm_to_id[
            realm] in self.realms
        self.log.debug('{func}(realm="{realm}") -> {result}',
                       func=hltype(RouterController.has_realm),
                       realm=hlid(realm),
                       result=hlval(result))
        return result
예제 #16
0
    def get_web_transport_service(self, transport_id, path, details=None):
        self.log.debug('{func}(transport_id={transport_id}, path="{path}")',
                       func=hltype(self.get_web_transport_service),
                       transport_id=hlid(transport_id),
                       path=hlval(path))

        transport = self.transports.get(transport_id, None)
        if not transport or \
           not isinstance(transport, self.personality.RouterWebTransport) or \
           transport.state != self.personality.RouterTransport.STATE_STARTED:
            emsg = "No transport with ID '{}' or transport is not a Web transport".format(transport_id)
            self.log.debug(emsg)
            raise ApplicationError('crossbar.error.not_running', emsg)

        if path not in transport.root:
            emsg = "Web transport {}: no service running on path '{}'".format(transport_id, path)
            self.log.debug(emsg)
            raise ApplicationError('crossbar.error.not_running', emsg)

        return transport.marshal()
예제 #17
0
    def onJoin(self, details):
        """
        Called when process has joined the node's management realm.
        """

        regs = yield self.register(
            self,
            prefix='{}.'.format(self._uri_prefix),
            options=RegisterOptions(details_arg='details'),
        )

        self.log.info(
            'Ok, registered {len_reg} management procedures on realm "{realm}".',
            len_reg=hlval(len(regs)),
            realm=hl(self.realm))
        for reg in regs:
            if isinstance(reg, Failure):
                self.log.error("Failed to register: {f}",
                               f=reg,
                               log_failure=reg)
            else:
                self.log.debug('  {proc}', proc=hlid(reg.procedure))
        returnValue(regs)
예제 #18
0
파일: router.py 프로젝트: wp4613/crossbar
    def start_router_realm_link(self,
                                realm_id,
                                link_id,
                                link_config,
                                details=None):
        """
        Start a new router link to a remote router on a (local) realm.

        The link configuration (``link_config``) must include the transport definition
        to the remote router. Here is an example:

        .. code-block:: json

            {
                "realm": "realm1",
                "transport": {
                    "type": "websocket",
                    "endpoint": {
                        "type": "tcp",
                        "host": "localhost",
                        "port": 8002
                    },
                    "url": "ws://localhost:8002/ws"
                }
            }

        :param realm_id: The ID of the (local) realm on which to start the link.
        :type realm_id: str

        :param link_id: The ID of the router link to start.
        :type link_id: str

        :param link_config: The router link configuration.
        :type link_config: dict

        :returns: The new link detail information.
        :rtype: dict
        """
        assert type(realm_id) == str
        assert type(link_id) == str
        assert type(link_config) == dict
        assert isinstance(details, CallDetails)

        self.log.info(
            '{method} Router link {link_id} starting on realm {realm_id} ..',
            link_id=hlid(link_id),
            realm_id=hlid(realm_id),
            method=hltype(RouterController.start_router_realm_link))

        try:
            if realm_id not in self.realms:
                self.log.warn('{func} realm "{realm}" not found in {realms}',
                              func=hltype(self.start_router_realm_link),
                              realm=hlval(realm_id),
                              realms=sorted(self.realms.keys()))
                raise ApplicationError('crossbar.error.no_such_object',
                                       'no realm with ID {}'.format(realm_id))

            rlink_manager = self.realms[realm_id].rlink_manager

            if link_id in rlink_manager:
                raise ApplicationError(
                    'crossbar.error.already_running',
                    'router link {} already running'.format(link_id))
            link_config = RLinkConfig.parse(self.personality,
                                            link_config,
                                            id=link_id)
            caller = SessionIdent.from_calldetails(details)
            rlink = yield rlink_manager.start_link(link_id, link_config,
                                                   caller)
            started = rlink.marshal()
        except:
            self.log.failure()
            raise
        else:
            self.publish(
                '{}.on_router_realm_link_started'.format(self._uri_prefix),
                started)

            self.log.info('Router link {link_id} started on realm {realm_id}',
                          link_id=hlid(link_id),
                          realm_id=hlid(realm_id))

            returnValue(started)
예제 #19
0
    def _monitor_blockchain(self, gateway_config, scan_from_block, period=300):
        """

        :param gateway_config:
        :param scan_from_block:
        :param period:
        :return:
        """
        w3 = make_w3(gateway_config)
        initial_delay = 2

        self.log.info(
            'Start monitoring of blockchain ({blockchain_type}) on thread {thread_id} in {initial_delay} seconds, iterating every {period} seconds  ..',
            blockchain_type=str(self._bc_gw_config['type']),
            initial_delay=hlval(initial_delay),
            period=hlval(period),
            thread_id=hlval(int(threading.get_ident())))

        # using normal blocking call here, as we are running on a background thread!
        time.sleep(initial_delay)

        def _process_Token_Transfer(transactionHash, blockHash, args):
            # event Transfer(address indexed from, address indexed to, uint256 value);
            self.log.info(
                '{event}: processing event (tx_hash={tx_hash}, block_hash={block_hash}) - {value} XBR token transferred (on-chain) from {_from} to {_to})',
                event=hlcontract('XBRToken.Transfer'),
                tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()),
                block_hash=hlid('0x' + binascii.b2a_hex(blockHash).decode()),
                value=hlval(int(args.value / 10**18)),
                _from=hlval(args['from']),
                _to=hlval(args.to))

            stored = False
            with self._db.begin(write=True) as txn:

                transactionHash = bytes(transactionHash)

                token_transfer = self._xbr.token_transfers[txn, transactionHash]
                if token_transfer:
                    self.log.warn('{contract}(tx_hash={tx_hash}) record already stored in database.',
                                  contract=hlcontract('TokenTransfer'),
                                  tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()))
                else:
                    token_transfer = cfxdb.xbr.token.TokenTransfer()

                    token_transfer.tx_hash = transactionHash
                    token_transfer.block_hash = bytes(blockHash)
                    token_transfer.from_address = bytes(HexBytes(args['from']))
                    token_transfer.to_address = bytes(HexBytes(args.to))
                    token_transfer.value = args.value

                    self._xbr.token_transfers[txn, transactionHash] = token_transfer
                    stored = True

            if stored:
                self.log.info('new {contract}(tx_hash={tx_hash}) record stored database!',
                              contract=hlcontract('TokenTransfer'),
                              tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()))

        def _process_Token_Approval(transactionHash, blockHash, args):
            # event Approval(address indexed from, address indexed to, uint256 value);
            self.log.info(
                '{event}: processing event (tx_hash={tx_hash}, block_hash={block_hash}) - {value} XBR token approved (on-chain) from owner {owner} to spender {spender})',
                event=hlcontract('XBRToken.Approval'),
                tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()),
                block_hash=hlid('0x' + binascii.b2a_hex(blockHash).decode()),
                value=hlval(int(args.value / 10**18)),
                owner=hlval(args.owner),
                spender=hlval(args.spender))

            stored = False
            with self._db.begin(write=True) as txn:

                transactionHash = bytes(transactionHash)

                token_approval = self._xbr.token_approvals[txn, transactionHash]
                if token_approval:
                    self.log.warn('{contract}(tx_hash={tx_hash}) record already stored in database.',
                                  contract=hlcontract('TokenApproval'),
                                  tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()))
                else:
                    token_approval = cfxdb.xbr.token.TokenApproval()

                    token_approval.tx_hash = transactionHash
                    token_approval.block_hash = bytes(blockHash)
                    token_approval.owner_address = bytes(HexBytes(args.owner))
                    token_approval.spender_address = bytes(HexBytes(args.spender))
                    token_approval.value = args.value

                    self._xbr.token_approvals[txn, transactionHash] = token_approval
                    stored = True

            if stored:
                self.log.info('new {contract}(tx_hash={tx_hash}) record stored database!',
                              contract=hlcontract('TokenApproval'),
                              tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()))

        def _process_Network_MemberRegistered(transactionHash, blockHash, args):
            #     /// Event emitted when a new member joined the XBR Network.
            #     event MemberCreated (address indexed member, string eula, string profile, MemberLevel level);
            self.log.info(
                '{event}: processing event (tx_hash={tx_hash}, block_hash={block_hash}) - XBR member created at address {address})',
                event=hlcontract('XBRNetwork.MemberCreated'),
                tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()),
                block_hash=hlid('0x' + binascii.b2a_hex(blockHash).decode()),
                address=hlid(args.member))

            member_adr = bytes(HexBytes(args.member))

            if args.eula:
                h = multihash.decode(multihash.from_b58_string(args.eula))
                if h.name != 'sha2-256':
                    self.log.warn(
                        'WARNING: XBRNetwork.MemberCreated - eula "{eula}" is not an IPFS (sha2-256) b58-encoded multihash',
                        eula=hlval(args.eula))

            if args.profile:
                h = multihash.decode(multihash.from_b58_string(args.profile))
                if h.name != 'sha2-256':
                    self.log.warn(
                        'WARNING: XBRNetwork.MemberCreated - profile "{profile}" is not an IPFS (sha2-256) b58-encoded multihash',
                        eula=hlval(args.profile))

            stored = False
            with self._db.begin(write=True) as txn:

                member = self._xbr.members[txn, member_adr]
                if member:
                    self.log.warn('{contract}(tx_hash={tx_hash}) record already stored in database.',
                                  contract=hlcontract('TokenApproval'),
                                  tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()))
                else:
                    member = cfxdb.xbr.member.Member()
                    member.address = member_adr
                    member.timestamp = np.datetime64(time_ns(), 'ns')
                    member.registered = args.registered
                    member.eula = args.eula
                    member.profile = args.profile
                    member.level = args.level

                    self._xbr.members[txn, member_adr] = member
                    stored = True

            if stored:
                self.log.info('new {contract}(member_adr={member_adr}) record stored database!',
                              contract=hlcontract('MemberCreated'),
                              member_adr=hlid('0x' + binascii.b2a_hex(member_adr).decode()))

        def _process_Network_MemberRetired(transactionHash, blockHash, args):
            #     /// Event emitted when a member leaves the XBR Network.
            #     event MemberRetired (address member);
            self.log.warn('_process_Network_MemberRetired not implemented')

        def _process_Market_MarketCreated(transactionHash, blockHash, args):
            #     /// Event emitted when a new market was created.
            #     event MarketCreated (bytes16 indexed marketId, uint32 marketSeq, address owner, string terms, string meta,
            #         address maker, uint256 providerSecurity, uint256 consumerSecurity, uint256 marketFee);
            self.log.info(
                '{event}: processing event (tx_hash={tx_hash}, block_hash={block_hash}) - XBR market created with ID {market_id})',
                event=hlcontract('XBRMarket.MarketCreated'),
                tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()),
                block_hash=hlid('0x' + binascii.b2a_hex(blockHash).decode()),
                market_id=hlid(uuid.UUID(bytes=args.marketId)))

            market_id = uuid.UUID(bytes=args.marketId)

            if args.terms:
                h = multihash.decode(multihash.from_b58_string(args.terms))
                if h.name != 'sha2-256':
                    self.log.warn(
                        'WARNING: XBRMarket.MarketCreated - terms "{terms}" is not an IPFS (sha2-256) b58-encoded multihash',
                        terms=hlval(args.terms))

            if args.meta:
                h = multihash.decode(multihash.from_b58_string(args.meta))
                if h.name != 'sha2-256':
                    self.log.warn(
                        'WARNING: XBRMarket.MarketCreated - meta "{meta}" is not an IPFS (sha2-256) b58-encoded multihash',
                        meta=hlval(args.meta))

            stored = False
            with self._db.begin(write=True) as txn:

                market = self._xbr.markets[txn, market_id]
                if market:
                    self.log.warn('{contract}(tx_hash={tx_hash}) record already stored in database.',
                                  contract=hlcontract('MarketCreated'),
                                  tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()))
                else:
                    market = cfxdb.xbr.market.Market()
                    market.market = market_id
                    market.timestamp = np.datetime64(time_ns(), 'ns')

                    # FIXME
                    # market.created = args.created

                    market.seq = args.marketSeq
                    market.owner = bytes(HexBytes(args.owner))
                    market.terms = args.terms
                    market.meta = args.meta
                    market.maker = bytes(HexBytes(args.maker))
                    market.provider_security = args.providerSecurity
                    market.consumer_security = args.consumerSecurity
                    market.market_fee = args.marketFee

                    self._xbr.markets[txn, market_id] = market
                    stored = True

            if stored:
                self.log.info('new {contract}(market_id={market_id}) record stored database!',
                              contract=hlcontract('MarketCreated'),
                              market_id=hlid(market_id))

        def _process_Market_MarketUpdated(transactionHash, blockHash, args):
            #     /// Event emitted when a market was updated.
            #     event MarketUpdated (bytes16 indexed marketId, uint32 marketSeq, address owner, string terms, string meta,
            #         address maker, uint256 providerSecurity, uint256 consumerSecurity, uint256 marketFee);
            self.log.warn('_process_Market_MarketUpdated not implemented')

        def _process_Market_MarketClosed(transactionHash, blockHash, args):
            #     /// Event emitted when a market was closed.
            #     event MarketClosed (bytes16 indexed marketId);
            self.log.warn('_process_Market_MarketClosed not implemented')

        def _process_Market_ActorJoined(transactionHash, blockHash, args):
            # Event emitted when a new actor joined a market.
            # event ActorJoined (bytes16 indexed marketId, address actor, uint8 actorType, uint joined, uint256 security, string meta);
            self.log.info(
                '{event}: processing event (tx_hash={tx_hash}, block_hash={block_hash}) - XBR market actor {actor} joined market {market_id})',
                event=hlcontract('XBRMarket.ActorJoined'),
                tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()),
                block_hash=hlid('0x' + binascii.b2a_hex(blockHash).decode()),
                actor=hlid(args.actor),
                market_id=hlid(uuid.UUID(bytes=args.marketId)))

            market_id = uuid.UUID(bytes=args.marketId)
            actor_adr = bytes(HexBytes(args.actor))
            actor_type = int(args.actorType)

            if args.meta:
                h = multihash.decode(multihash.from_b58_string(args.meta))
                if h.name != 'sha2-256':
                    self.log.warn(
                        'WARNING: XBRMarket.MarketCreated - meta "{meta}" is not an IPFS (sha2-256) b58-encoded multihash',
                        terms=hlval(args.meta))

            stored = False
            with self._db.begin(write=True) as txn:

                actor = self._xbr.actors[txn, (market_id, actor_adr, actor_type)]
                if actor:
                    self.log.warn('{contract}(tx_hash={tx_hash}) record already stored in database.',
                                  contract=hlcontract('MarketCreated'),
                                  tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()))
                else:
                    actor = cfxdb.xbr.actor.Actor()
                    actor.timestamp = np.datetime64(time_ns(), 'ns')
                    actor.market = market_id
                    actor.actor = actor_adr
                    actor.actor_type = actor_type

                    actor.joined = args.joined
                    actor.security = args.security
                    actor.meta = args.meta

                    self._xbr.actors[txn, (market_id, actor_adr, actor_type)] = actor
                    stored = True

            if stored:
                self.log.info(
                    'new {contract}(market_id={market_id}, actor_adr={actor_adr}, actor_type={actor_type}) record stored database!',
                    contract=hlcontract('ActorJoined'),
                    market_id=hlid(market_id),
                    actor_adr=hlid('0x' + binascii.b2a_hex(actor_adr).decode()),
                    actor_type=hlid(actor_type))

        def _process_Market_ActorLeft(transactionHash, blockHash, args):
            self.log.warn('_process_Market_ActorLeft not implemented')

        def _process_Market_ConsentSet(transactionHash, blockHash, args):
            # Event emitted when a consent is set
            # emit ConsentSet(member, updated, marketId, delegate, delegateType,
            #                 apiCatalog, consent, servicePrefix);
            self.log.info('{event}: processing event (tx_hash={tx_hash}, block_hash={block_hash}) ..',
                          event=hlcontract('XBRMarket.ConsentSet'),
                          tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()),
                          block_hash=hlid('0x' + binascii.b2a_hex(blockHash).decode()))

            catalog_oid = uuid.UUID(bytes=args.apiCatalog)
            member = uuid.UUID(bytes=args.member)
            delegate = args.delegate
            delegate_type = args.delegateType
            market_oid = uuid.UUID(bytes=args.marketId)
            with self._db.begin(write=True) as txn:
                consent = self._xbr.consents[txn, (catalog_oid, member, delegate, delegate_type, market_oid)]
                consent.synced = True

        def _process_Catalog_CatalogCreated(transactionHash, blockHash, args):
            # Event emitted when a new API catalog is created
            # emit CatalogCreated(catalogId, created, catalogSeq, owner, terms, meta);
            self.log.info('{event}: processing event (tx_hash={tx_hash}, block_hash={block_hash}) ..',
                          event=hlcontract('XBRCatalog.CatalogCreated'),
                          tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()),
                          block_hash=hlid('0x' + binascii.b2a_hex(blockHash).decode()))

            catalog_oid = uuid.UUID(bytes=args.catalogId)
            owner = bytes(HexBytes(args.owner))
            created = np.datetime64(time_ns(), 'ns')
            with self._db.begin(write=True) as txn:
                catalog = cfxdb.xbr.catalog.Catalog()
                catalog.oid = catalog_oid
                catalog.timestamp = created
                catalog.seq = args.catalogSeq
                catalog.owner = owner
                catalog.terms = args.terms
                catalog.meta = args.meta
                self._xbr.catalogs[txn, catalog_oid] = catalog

            deferToThread(self._download_ipfs_file, args.meta)

        def _process_Catalog_ApiPublished(transactionHash, blockHash, args):
            self.log.warn('_process_Catalog_ApiPublished not implemented')

        def _process_Channel_Opened(transactionHash, blockHash, args):
            # Event emitted when a new XBR data market has opened.
            # event Opened(XBRTypes.ChannelType ctype, bytes16 indexed marketId, bytes16 indexed channelId,
            #              address actor, address delegate, address marketmaker, address recipient,
            #              uint256 amount, bytes signature);
            self.log.info('{event}: processing event (tx_hash={tx_hash}, block_hash={block_hash}) ..',
                          event=hlcontract('XBRChannel.Opened'),
                          tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()),
                          block_hash=hlid('0x' + binascii.b2a_hex(blockHash).decode()))

            channel_oid = uuid.UUID(bytes=args.channelId)
            marketmaker_adr = bytes(HexBytes(args.marketmaker))
            marketmaker_adr_str = web3.Web3.toChecksumAddress(marketmaker_adr)

            # we only persist data for xbr markets operated by one of the market makers we run in this worker
            if marketmaker_adr_str not in self._maker_adr2id or self._maker_adr2id[
                    marketmaker_adr_str] not in self._makers:
                self.log.info(
                    '{event}: skipping channel (channel {channel_oid} in market with market maker address {marketmaker_adr} is in for any market in this markets worker)',
                    event=hlcontract('XBRChannel.Opened'),
                    channel_oid=hlid(channel_oid),
                    marketmaker_adr=hlid(marketmaker_adr_str))
                return

            # prepare new channel data
            channel_type = int(args.ctype)
            market_oid = uuid.UUID(bytes=args.marketId)
            actor_adr = bytes(HexBytes(args.actor))
            delegate_adr = bytes(HexBytes(args.delegate))
            recipient_adr = bytes(HexBytes(args.recipient))
            amount = int(args.amount)
            # FIXME
            # signature = bytes(HexBytes(args.signature))
            # FIXME
            # member_oid = uuid.UUID()

            # get the market maker by address
            maker = self._makers[self._maker_adr2id[marketmaker_adr_str]]

            # the market maker specific embedded database and schema
            db = maker.db
            xbrmm = maker.schema

            # depending on channel type, different database schema classes and tables are used
            if channel_type == cfxdb.xbrmm.ChannelType.PAYMENT:
                channel = cfxdb.xbrmm.PaymentChannel()
                channels = xbrmm.payment_channels
                channels_by_delegate = xbrmm.idx_payment_channel_by_delegate
                balance = cfxdb.xbrmm.PaymentChannelBalance()
                balances = xbrmm.payment_balances
            elif channel_type == cfxdb.xbrmm.ChannelType.PAYING:
                channel = cfxdb.xbrmm.PayingChannel()
                channels = xbrmm.paying_channels
                channels_by_delegate = xbrmm.idx_paying_channel_by_delegate
                balance = cfxdb.xbrmm.PayingChannelBalance()
                balances = xbrmm.paying_balances
            else:
                assert False, 'should not arrive here'

            # fill in information for newly replicated channel
            channel.market_oid = market_oid
            # FIXME
            # channel.member_oid = member_oid
            channel.channel_oid = channel_oid
            channel.timestamp = np.datetime64(time_ns(), 'ns')
            # channel.open_at = None

            # FIXME: should read that from even args after deployment of
            # https://github.com/crossbario/xbr-protocol/pull/138
            channel.seq = 1
            channel.channel_type = channel_type
            channel.marketmaker = marketmaker_adr
            channel.actor = actor_adr
            channel.delegate = delegate_adr
            channel.recipient = recipient_adr
            channel.amount = amount

            # FIXME
            channel.timeout = 0
            channel.state = cfxdb.xbrmm.ChannelState.OPEN
            # FIXME
            # channel.open_sig = signature

            # create an off-chain balance record for the channel with remaining == initial amount
            balance.remaining = channel.amount
            # FIXME: should read that from even args after deployment of
            # https://github.com/crossbario/xbr-protocol/pull/138
            balance.seq = 1

            # now store the new channel and balance in the database
            stored = False
            cnt_channels_before = 0
            cnt_channels_by_delegate_before = 0
            cnt_channels_after = 0
            cnt_channels_by_delegate_after = 0
            with db.begin(write=True) as txn:
                if channels[txn, channel_oid]:
                    self.log.warn('{event}: channel already stored in database [channel_oid={channel_oid}]',
                                  event=hlcontract('XBRChannel.Opened'),
                                  channel_oid=hlid(channel_oid))
                else:
                    cnt_channels_before = channels.count(txn)
                    cnt_channels_by_delegate_before = channels_by_delegate.count(txn)

                    # store the channel along with the off-chain balance
                    channels[txn, channel_oid] = channel
                    balances[txn, channel_oid] = balance
                    stored = True

                    cnt_channels_after = channels.count(txn)
                    cnt_channels_by_delegate_after = channels_by_delegate.count(txn)

            self.log.info(
                '{event} DB result: stored={stored}, cnt_channels_before={cnt_channels_before}, cnt_channels_by_delegate_before={cnt_channels_by_delegate_before}, cnt_channels_after={cnt_channels_after}, cnt_channels_by_delegate_after={cnt_channels_by_delegate_after}',
                event=hlcontract('XBRChannel.Opened'),
                stored=hlval(stored),
                cnt_channels_before=hlval(cnt_channels_before),
                cnt_channels_by_delegate_before=hlval(cnt_channels_by_delegate_before),
                cnt_channels_after=hlval(cnt_channels_after),
                cnt_channels_by_delegate_after=hlval(cnt_channels_by_delegate_after))
            if stored:
                # FIXME: publish WAMP event
                self.log.info(
                    '{event}: new channel stored in database [actor_adr={actor_adr}, channel_type={channel_type}, market_oid={market_oid}, member_oid={member_oid}, channel_oid={channel_oid}]',
                    event=hlcontract('XBRChannel.Opened'),
                    market_oid=hlid(market_oid),
                    # FIXME
                    member_oid=hlid(None),
                    channel_oid=hlid(channel_oid),
                    actor_adr=hlid('0x' + binascii.b2a_hex(actor_adr).decode()),
                    channel_type=hlid(channel_type))

        def _process_Channel_Closing(transactionHash, blockHash, args):
            self.log.warn('_process_Channel_Closing not implemented')

        def _process_Channel_Closed(transactionHash, blockHash, args):
            self.log.warn('_process_Channel_Closed not implemented')

        # map XBR contract log event to event processing function
        Events = [
            (xbr.xbrtoken.events.Transfer, _process_Token_Transfer),
            (xbr.xbrtoken.events.Approval, _process_Token_Approval),
            (xbr.xbrnetwork.events.MemberRegistered, _process_Network_MemberRegistered),
            (xbr.xbrnetwork.events.MemberRetired, _process_Network_MemberRetired),
            (xbr.xbrmarket.events.MarketCreated, _process_Market_MarketCreated),
            (xbr.xbrmarket.events.MarketUpdated, _process_Market_MarketUpdated),
            (xbr.xbrmarket.events.MarketClosed, _process_Market_MarketClosed),
            (xbr.xbrmarket.events.ActorJoined, _process_Market_ActorJoined),
            (xbr.xbrmarket.events.ActorLeft, _process_Market_ActorLeft),
            (xbr.xbrmarket.events.ConsentSet, _process_Market_ConsentSet),
            (xbr.xbrcatalog.events.CatalogCreated, _process_Catalog_CatalogCreated),
            (xbr.xbrcatalog.events.ApiPublished, _process_Catalog_ApiPublished),
            (xbr.xbrchannel.events.Opened, _process_Channel_Opened),
            (xbr.xbrchannel.events.Closing, _process_Channel_Closing),
            (xbr.xbrchannel.events.Closed, _process_Channel_Closed),
        ]

        # determine the block number, starting from which we scan the blockchain for XBR events
        current = w3.eth.getBlock('latest')
        last_processed = scan_from_block - 1
        with self._db.begin() as txn:
            for block_number in self._xbr.blocks.select(txn, return_values=False, reverse=True, limit=1):
                last_processed = unpack_uint256(block_number)
        if last_processed > current.number:
            raise ApplicationError(
                'wamp.error.invalid_argument',
                'last processed block number {} (or configured "scan_from" block number) is larger than then current block number {}'
                .format(last_processed, current.number))
        else:
            self.log.info(
                'Start scanning blockchain: current block is {current_block}, last processed is {last_processed} ..',
                current_block=hlval(current.number),
                last_processed=hlval(last_processed + 1))

        iteration = 1

        while not self._stop_monitor and not self._run_monitor.is_set():
            # current last block
            current = w3.eth.getBlock('latest')

            # track number of blocks processed
            cnt_blocks_success = 0
            cnt_blocks_error = 0
            cnt_xbr_events = 0

            # synchronize on-change changes locally by processing blockchain events
            if last_processed < current.number:
                while last_processed < current.number:
                    last_processed += 1
                    try:
                        self.log.info('Now processing blockchain block {last_processed} ..',
                                      last_processed=hlval(last_processed))
                        cnt_xbr_events += self._process_block(w3, last_processed, Events)
                    except:
                        self.log.failure()
                        cnt_blocks_error += 1
                    else:
                        cnt_blocks_success += 1

                self.log.info(
                    'Monitor blockchain iteration {iteration} completed: new block processed (last_processed={last_processed}, thread_id={thread_id}, period={period}, cnt_xbr_events={cnt_xbr_events}, cnt_blocks_success={cnt_blocks_success}, cnt_blocks_error={cnt_blocks_error})',
                    iteration=hlval(iteration),
                    last_processed=hlval(last_processed),
                    thread_id=hlval(int(threading.get_ident())),
                    period=hlval(period),
                    cnt_xbr_events=hlval(cnt_xbr_events, color='green') if cnt_xbr_events else hlval(cnt_xbr_events),
                    cnt_blocks_success=hlval(cnt_blocks_success, color='green')
                    if cnt_xbr_events else hlval(cnt_blocks_success),
                    cnt_blocks_error=hlval(cnt_blocks_error, color='red')
                    if cnt_blocks_error else hlval(cnt_blocks_error))
            else:
                self.log.info(
                    'Monitor blockchain iteration {iteration} completed: no new blocks found (last_processed={last_processed}, thread_id={thread_id}, period={period})',
                    iteration=hlval(iteration),
                    last_processed=hlval(last_processed),
                    thread_id=hlval(int(threading.get_ident())),
                    period=hlval(period))

            # sleep (using normal blocking call here, as we are running on a background thread!)
            self._run_monitor.wait(period)

            iteration += 1
예제 #20
0
    def start_web_transport_service(self,
                                    transport_id,
                                    path,
                                    config,
                                    details=None):
        """
        Start a service on a Web transport.

        :param transport_id: The ID of the transport to start the Web transport service on.
        :type transport_id: str

        :param path: The path (absolute URL, eg "/myservice1") on which to start the service.
        :type path: str

        :param config: The Web service configuration.
        :type config: dict

        :param details: Call details.
        :type details: :class:`autobahn.wamp.types.CallDetails`
        """
        if not isinstance(config, dict) or 'type' not in config:
            raise ApplicationError(
                'crossbar.invalid_argument',
                'config parameter must be dict with type attribute')

        self.log.info(
            'Starting "{service_type}" Web service on path "{path}" of transport "{transport_id}" {func}',
            service_type=hlval(config.get('type', 'unknown')),
            path=hlval(path),
            transport_id=hlid(transport_id),
            func=hltype(self.start_web_transport_service))

        transport = self.transports.get(transport_id, None)
        if not transport:
            emsg = 'Cannot start service on transport: no transport with ID "{}"'.format(
                transport_id)
            self.log.error(emsg)
            raise ApplicationError('crossbar.error.not_running', emsg)

        if not isinstance(transport, self.personality.RouterWebTransport):
            emsg = 'Cannot start service on transport: transport is not a Web transport (transport_type={})'.format(
                hltype(transport.__class__))
            self.log.error(emsg)
            raise ApplicationError('crossbar.error.not_running', emsg)

        if transport.state != self.personality.RouterTransport.STATE_STARTED:
            emsg = 'Cannot start service on Web transport service: transport {} is not running (transport_state={})'.format(
                transport_id,
                self.personality.RouterWebTransport.STATES.get(
                    transport.state, None))
            self.log.error(emsg)
            raise ApplicationError('crossbar.error.not_running', emsg)

        if path in transport.root:
            emsg = 'Cannot start service on Web transport "{}": a service is already running on path "{}"'.format(
                transport_id, path)
            self.log.error(emsg)
            raise ApplicationError('crossbar.error.already_running', emsg)

        caller = details.caller if details else None
        self.publish(self._uri_prefix + '.on_web_transport_service_starting',
                     transport_id,
                     path,
                     options=PublishOptions(exclude=caller))

        # now actually add the web service ..
        # note: currently this is NOT async, but direct/sync.
        webservice_factory = self.personality.WEB_SERVICE_FACTORIES[
            config['type']]

        webservice = yield maybeDeferred(webservice_factory.create, transport,
                                         path, config)
        transport.root[path] = webservice

        on_web_transport_service_started = {
            'transport_id': transport_id,
            'path': path,
            'config': config
        }
        caller = details.caller if details else None
        self.publish(self._uri_prefix + '.on_web_transport_service_started',
                     transport_id,
                     path,
                     on_web_transport_service_started,
                     options=PublishOptions(exclude=caller))

        returnValue(on_web_transport_service_started)
예제 #21
0
        def _component_closed(session, was_clean):
            """
            This is moderate hack around the fact that we don't have any way
            to "listen" for a close event on websocket or rawsocket
            objects. Also, the rawsocket implementation doesn't have
            "a" function we can wrap anyway (they are asyncio vs
            Twisted specific), so for both WebSocket and rawsocket
            cases, we actually listen on the WAMP session for
            transport close notifications.

            Ideally we'd listen for "close" on the transport but this
            works fine for cleaning up the components.
            """
            if component_id not in self.components:
                self.log.warn(
                    "Component '{id}' closed, but not in set.",
                    id=component_id,
                )
                return

            if was_clean:
                self.log.info(
                    "Closed connection to '{id}'",
                    id=component_id,
                )
            else:
                self.log.error(
                    "Lost connection to component '{id}' uncleanly",
                    id=component_id,
                )

            component = self.components[component_id]
            del self.components[component_id]
            self._publish_component_stop(component)
            component._stopped.callback(component.marshal())
            del component

            # figure out if we need to shut down the container itself or not
            if not was_clean and self._exit_mode == self.SHUTDOWN_ON_ANY_COMPONENT_FAILED:
                self.log.info(
                    "A component has failed: stopping container in exit mode <{exit_mode}> ...",
                    exit_mode=self._exit_mode,
                )
                self.shutdown()
                return

            if self._exit_mode == self.SHUTDOWN_ON_ANY_COMPONENT_STOPPED:
                self.log.info(
                    "A component has stopped: stopping container in exit mode <{exit_mode}> ...",
                    exit_mode=self._exit_mode,
                )
                self.shutdown()
                return

            if not self.components:
                if self._exit_mode == self.SHUTDOWN_ON_LAST_COMPONENT_STOPPED:
                    self.log.info(
                        "Container is hosting no more components: stopping container in exit mode <{exit_mode}> ...",
                        exit_mode=self._exit_mode,
                    )
                    self.shutdown()
                    return
                else:
                    self.log.info(
                        "Container is hosting no more components: continue running in exit mode <{exit_mode}>",
                        exit_mode=self._exit_mode,
                    )
            else:
                self.log.info(
                    "Container is still hosting {component_count} components: continue running in exit mode <{exit_mode}>",
                    exit_mode=self._exit_mode,
                    component_count=len(self.components),
                )

            # determine if we should re-start the component. Note that
            # we can only arrive here if we *didn't* decide to
            # shutdown above .. so if we have a shutdown mode of
            # SHUTDOWN_ON_ANY_COMPONENT_STOPPED will mean we never try
            # to re-start anything.
            if self._restart_mode == self.RESTART_ALWAYS or (
                    self._restart_mode == self.RESTART_FAILED
                    and not was_clean):

                def restart_component():
                    # Think: if this below start_component() fails,
                    # we'll still schedule *exactly one* new re-start
                    # attempt for it, right?
                    self.log.info(
                        '{func}: now restarting previously closed component {component_id} automatically .. [restart_mode={restart_mode}, was_clean={was_clean}]',
                        func=hltype(_component_closed),
                        component_id=hlid(component_id),
                        restart_mode=hlval(self._restart_mode),
                        was_clean=hlval(was_clean))
                    return self.start_component(
                        component_id,
                        config,
                        reload_modules=reload_modules,
                        details=details,
                    )

                # note we must yield to the reactor with
                # callLater(0, ..) to avoid infinite recursion if
                # we're stuck in a restart loop
                from twisted.internet import reactor
                reactor.callLater(0, restart_component)
            else:
                self.log.warn(
                    '{func}: component {component_id} will not be restarted automatically! [restart_mode={restart_mode}, was_clean={was_clean}]',
                    func=hltype(_component_closed),
                    component_id=hlid(component_id),
                    restart_mode=hlval(self._restart_mode),
                    was_clean=hlval(was_clean))
예제 #22
0
        def _process_Channel_Opened(transactionHash, blockHash, args):
            # Event emitted when a new XBR data market has opened.
            # event Opened(XBRTypes.ChannelType ctype, bytes16 indexed marketId, bytes16 indexed channelId,
            #              address actor, address delegate, address marketmaker, address recipient,
            #              uint256 amount, bytes signature);
            self.log.info('{event}: processing event (tx_hash={tx_hash}, block_hash={block_hash}) ..',
                          event=hlcontract('XBRChannel.Opened'),
                          tx_hash=hlid('0x' + binascii.b2a_hex(transactionHash).decode()),
                          block_hash=hlid('0x' + binascii.b2a_hex(blockHash).decode()))

            channel_oid = uuid.UUID(bytes=args.channelId)
            marketmaker_adr = bytes(HexBytes(args.marketmaker))
            marketmaker_adr_str = web3.Web3.toChecksumAddress(marketmaker_adr)

            # we only persist data for xbr markets operated by one of the market makers we run in this worker
            if marketmaker_adr_str not in self._maker_adr2id or self._maker_adr2id[
                    marketmaker_adr_str] not in self._makers:
                self.log.info(
                    '{event}: skipping channel (channel {channel_oid} in market with market maker address {marketmaker_adr} is in for any market in this markets worker)',
                    event=hlcontract('XBRChannel.Opened'),
                    channel_oid=hlid(channel_oid),
                    marketmaker_adr=hlid(marketmaker_adr_str))
                return

            # prepare new channel data
            channel_type = int(args.ctype)
            market_oid = uuid.UUID(bytes=args.marketId)
            actor_adr = bytes(HexBytes(args.actor))
            delegate_adr = bytes(HexBytes(args.delegate))
            recipient_adr = bytes(HexBytes(args.recipient))
            amount = int(args.amount)
            # FIXME
            # signature = bytes(HexBytes(args.signature))
            # FIXME
            # member_oid = uuid.UUID()

            # get the market maker by address
            maker = self._makers[self._maker_adr2id[marketmaker_adr_str]]

            # the market maker specific embedded database and schema
            db = maker.db
            xbrmm = maker.schema

            # depending on channel type, different database schema classes and tables are used
            if channel_type == cfxdb.xbrmm.ChannelType.PAYMENT:
                channel = cfxdb.xbrmm.PaymentChannel()
                channels = xbrmm.payment_channels
                channels_by_delegate = xbrmm.idx_payment_channel_by_delegate
                balance = cfxdb.xbrmm.PaymentChannelBalance()
                balances = xbrmm.payment_balances
            elif channel_type == cfxdb.xbrmm.ChannelType.PAYING:
                channel = cfxdb.xbrmm.PayingChannel()
                channels = xbrmm.paying_channels
                channels_by_delegate = xbrmm.idx_paying_channel_by_delegate
                balance = cfxdb.xbrmm.PayingChannelBalance()
                balances = xbrmm.paying_balances
            else:
                assert False, 'should not arrive here'

            # fill in information for newly replicated channel
            channel.market_oid = market_oid
            # FIXME
            # channel.member_oid = member_oid
            channel.channel_oid = channel_oid
            channel.timestamp = np.datetime64(time_ns(), 'ns')
            # channel.open_at = None

            # FIXME: should read that from even args after deployment of
            # https://github.com/crossbario/xbr-protocol/pull/138
            channel.seq = 1
            channel.channel_type = channel_type
            channel.marketmaker = marketmaker_adr
            channel.actor = actor_adr
            channel.delegate = delegate_adr
            channel.recipient = recipient_adr
            channel.amount = amount

            # FIXME
            channel.timeout = 0
            channel.state = cfxdb.xbrmm.ChannelState.OPEN
            # FIXME
            # channel.open_sig = signature

            # create an off-chain balance record for the channel with remaining == initial amount
            balance.remaining = channel.amount
            # FIXME: should read that from even args after deployment of
            # https://github.com/crossbario/xbr-protocol/pull/138
            balance.seq = 1

            # now store the new channel and balance in the database
            stored = False
            cnt_channels_before = 0
            cnt_channels_by_delegate_before = 0
            cnt_channels_after = 0
            cnt_channels_by_delegate_after = 0
            with db.begin(write=True) as txn:
                if channels[txn, channel_oid]:
                    self.log.warn('{event}: channel already stored in database [channel_oid={channel_oid}]',
                                  event=hlcontract('XBRChannel.Opened'),
                                  channel_oid=hlid(channel_oid))
                else:
                    cnt_channels_before = channels.count(txn)
                    cnt_channels_by_delegate_before = channels_by_delegate.count(txn)

                    # store the channel along with the off-chain balance
                    channels[txn, channel_oid] = channel
                    balances[txn, channel_oid] = balance
                    stored = True

                    cnt_channels_after = channels.count(txn)
                    cnt_channels_by_delegate_after = channels_by_delegate.count(txn)

            self.log.info(
                '{event} DB result: stored={stored}, cnt_channels_before={cnt_channels_before}, cnt_channels_by_delegate_before={cnt_channels_by_delegate_before}, cnt_channels_after={cnt_channels_after}, cnt_channels_by_delegate_after={cnt_channels_by_delegate_after}',
                event=hlcontract('XBRChannel.Opened'),
                stored=hlval(stored),
                cnt_channels_before=hlval(cnt_channels_before),
                cnt_channels_by_delegate_before=hlval(cnt_channels_by_delegate_before),
                cnt_channels_after=hlval(cnt_channels_after),
                cnt_channels_by_delegate_after=hlval(cnt_channels_by_delegate_after))
            if stored:
                # FIXME: publish WAMP event
                self.log.info(
                    '{event}: new channel stored in database [actor_adr={actor_adr}, channel_type={channel_type}, market_oid={market_oid}, member_oid={member_oid}, channel_oid={channel_oid}]',
                    event=hlcontract('XBRChannel.Opened'),
                    market_oid=hlid(market_oid),
                    # FIXME
                    member_oid=hlid(None),
                    channel_oid=hlid(channel_oid),
                    actor_adr=hlid('0x' + binascii.b2a_hex(actor_adr).decode()),
                    channel_type=hlid(channel_type))
예제 #23
0
파일: node.py 프로젝트: yankos/crossbar
    def load_config(self, configfile=None):
        """
        Load the node configuration of CFC itself. The base node configuration
        is built into CFC, but can be overridden (partially) and extended
        by providing a regular, local node configuration file.

        When such a file exists:

        - and it contains a controller section, these take precedence over
        the builtin values.
        - and it contains a workers section (a list of workers), these are
        _added_ to the builtin workers.
        """
        config_source = Node.CONFIG_SOURCE_EMPTY

        # 1/4: load builtin master node configuration as default
        #
        config_path = pkg_resources.resource_filename('crossbar',
                                                      self.DEFAULT_CONFIG_PATH)
        with open(config_path) as f:
            config = json.load(f)

        # no need to check the builtin config (at run-time)
        # self.personality.check_config(self.personality, config)

        # remember config source
        config_source += Node.CONFIG_SOURCE_DEFAULT
        self.log.info('Built-in master node configuration loaded')

        # 2/4: allow extending/overriding the node configuration
        # with a local master node configuration file (eg for TLS setup)
        if configfile:
            config_path = os.path.abspath(os.path.join(self._cbdir,
                                                       configfile))
            with open(config_path) as f:
                custom_config = json.load(f)

            # as an overriding config file does not need to be a fully valid config in itself,
            # do _not_ check it ..
            # self.personality.check_config(self.personality, custom_config)

            # .. instead, we merge the config from the local file into the already
            # loaded default config (see above)
            config = merge_config(config, custom_config)

            # remember config source
            config_source += Node.CONFIG_SOURCE_LOCALFILE
            self.log.info(
                'Local master node configuration merged from "{config_path}"',
                config_path=hlval(config_path))

        # 3/4: allow extending/overriding the node configuration
        # with a remote master node configuration file from blockchain/IPFS
        if config.get('controller', {}).get('blockchain', None):
            blockchain_config = config['controller']['blockchain']
            gateway_config = blockchain_config['gateway']

            # setup Web3 connection from blockchain gateway configuration
            self._w3 = make_w3(gateway_config)

            # configure new Web3 connection as provider for XBR
            xbr.setProvider(self._w3)

            if self._w3.isConnected():
                self.log.info(
                    'Connected to Ethereum network {network} at gateway "{gateway_url}"',
                    network=self._w3.net.version,
                    gateway_url=gateway_config['http'])

                # try to find node by node public key on-chain
                xbr_node_id = xbr.xbrnetwork.functions.getNodeByKey(
                    self.key.public_key()).call()
                if xbr_node_id != b'\x00' * 16:
                    assert (len(xbr_node_id) == 16)

                    # get node domain, type, configuration and license as stored on-chain
                    xbr_node_domain = xbr.xbrnetwork.functions.getNodeDomain(
                        xbr_node_id).call()
                    xbr_node_type = xbr.xbrnetwork.functions.getNodeType(
                        xbr_node_id).call()
                    xbr_node_config = xbr.xbrnetwork.functions.getNodeConfig(
                        xbr_node_id).call()

                    # FIXME: for testing use hard-coded "trial license" (no. 20010)
                    xbr_node_license = 20010
                    # xbr_node_license = xbr.xbrnetwork.functions.getNodeLicense(xbr_node_id).call()

                    self.log.info(
                        'Node is configured for XBR (xbr_node_id="{xbr_node_id}", xbr_node_domain="{xbr_node_domain}", xbr_node_type="{xbr_node_type}", xbr_node_license="{xbr_node_license}", xbr_node_config="{xbr_node_config}")',
                        xbr_node_id=hlid(
                            '0x' + binascii.b2a_hex(xbr_node_id).decode()),
                        xbr_node_domain=hlid(
                            '0x' + binascii.b2a_hex(xbr_node_domain).decode()),
                        xbr_node_type=hlid(xbr_node_type),
                        xbr_node_config=hlid(xbr_node_config),
                        xbr_node_license=hlid(xbr_node_license))

                    self._license = License(xbr_node_license)
                    self.log.info(
                        'Node is registered in the XBR network with license type={license}',
                        license=self._license.type)

                    # if a (hash of a) configuration is set on-chain for the master node, fetch the
                    # configuration content for the hash from IPFS
                    if xbr_node_config:
                        if 'IPFS_GATEWAY_URL' in os.environ:
                            ipfs_gateway_url = os.environ['IPFS_GATEWAY_URL']
                            self.log.info(
                                'Using explicit IPFS gateway URL {ipfs_gateway_url} from environment variable IPFS_GATEWAY_URL',
                                ipfs_gateway_url=hlid(ipfs_gateway_url))
                        else:
                            ipfs_gateway_url = 'https://ipfs.infura.io:5001'
                            self.log.info(
                                'Using default IPFS Infura gateway URL {ipfs_gateway_url}',
                                ipfs_gateway_url=hlid(ipfs_gateway_url))

                        ipfs_config_url = '{}/api/v0/cat?arg={}&encoding=json'.format(
                            ipfs_gateway_url, xbr_node_config)
                        resp = requests.get(ipfs_config_url)

                        xbr_node_config_data = resp.json()

                        from pprint import pprint
                        pprint(xbr_node_config_data)

                        self.personality.check_config(self.personality,
                                                      xbr_node_config_data)

                        if 'controller' not in xbr_node_config_data:
                            xbr_node_config_data['controller'] = {}

                        if 'id' not in xbr_node_config_data['controller']:
                            xbr_node_config_data['controller']['id'] = str(
                                uuid.UUID(bytes=xbr_node_id))
                            self.log.info(
                                'Deriving node ID {node_id} from XBR network node ID',
                                node_id=hlid(
                                    xbr_node_config_data['controller']['id']))
                        else:
                            self.log.info(
                                'Setting node ID {node_id} from XBR network node configuration',
                                node_id=hlid(
                                    xbr_node_config_data['controller']['id']))

                        self._config = xbr_node_config_data

                        self.log.info(
                            'Node configuration loaded from XBR network (xbr_node_id="{xbr_node_id}")',
                            xbr_node_id=hlid(
                                '0x' + binascii.b2a_hex(xbr_node_id).decode()))
                    else:
                        self.log.debug(
                            'There is no node configuration stored in XBR network (xbr_node_id="{xbr_node_id}")',
                            xbr_node_id=hlid(
                                '0x' + binascii.b2a_hex(xbr_node_id).decode()))

                    config_source = self.CONFIG_SOURCE_XBRNETWORK
                else:
                    self.log.warn(
                        'Could not find node public key on blockchain yet')
            else:
                self.log.warn('Could not connect to Ethereum blockchain')

        # 4/4: check and set the final merged master node configuration
        #
        self.personality.check_config(self.personality, config)
        self._config = config

        self.log.debug(
            'Master node config after (effective after merge):\n{config}',
            config=pformat(config))

        return config_source, config_path