UNPKG

raiden-ts

Version:

Raiden Light Client Typescript/Javascript SDK

181 lines 11.4 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.pfsServiceRegistryMonitorEpic = exports.pfsFeeUpdateEpic = exports.pfsCapacityUpdateEpic = exports.pfsRequestEpic = void 0; const bignumber_1 = require("@ethersproject/bignumber"); const isEmpty_1 = __importDefault(require("lodash/isEmpty")); const isEqual_1 = __importDefault(require("lodash/isEqual")); const pickBy_1 = __importDefault(require("lodash/pickBy")); const rxjs_1 = require("rxjs"); const operators_1 = require("rxjs/operators"); const actions_1 = require("../../channels/actions"); const state_1 = require("../../channels/state"); const utils_1 = require("../../channels/utils"); const constants_1 = require("../../constants"); const actions_2 = require("../../messages/actions"); const types_1 = require("../../messages/types"); const utils_2 = require("../../messages/utils"); const utils_3 = require("../../transfers/utils"); const actions_3 = require("../../transport/actions"); const utils_4 = require("../../transport/utils"); const actions_4 = require("../../utils/actions"); const ethers_1 = require("../../utils/ethers"); const rx_1 = require("../../utils/rx"); const actions_5 = require("../actions"); const types_2 = require("../types"); const helpers_1 = require("./helpers"); /** * Check if a transfer can be made and return a set of paths for it. * * @param action$ - Observable of pathFind.request actions * @param state$ - Observable of RaidenStates * @param deps - RaidenEpicDeps object * @returns Observable of pathFind.{success|failure} actions */ function pfsRequestEpic(action$, {}, deps) { return action$.pipe((0, rx_1.dispatchRequestAndGetResponse)(actions_3.matrixPresence, (dispatch) => action$.pipe((0, operators_1.filter)((0, actions_4.isActionOf)(actions_5.pathFind.request)), (0, operators_1.concatMap)((action) => dispatch(actions_3.matrixPresence.request(undefined, { address: action.meta.target })).pipe((0, operators_1.withLatestFrom)(deps.latest$), (0, operators_1.mergeMap)(([targetPresence, latest]) => (0, helpers_1.getRoute$)(action, deps, latest, targetPresence)), (0, operators_1.withLatestFrom)(deps.latest$), (0, operators_1.mergeMap)(([route, { state }]) => (0, helpers_1.validateRoute$)([action, route], state, deps)), (0, operators_1.catchError)((err) => (0, rxjs_1.of)(actions_5.pathFind.failure(err, action.meta)))))))); } exports.pfsRequestEpic = pfsRequestEpic; /** * Sends a [[PFSCapacityUpdate]] to PFSs on new deposit on our side of channels * * @param action$ - Observable of channelDeposit.success actions * @param state$ - Observable of RaidenStates * @param deps - Epics dependencies * @param deps.log - Logger instance * @param deps.address - Our address * @param deps.network - Current Network * @param deps.signer - Signer instance * @param deps.config$ - Config observable * @returns Observable of messageServiceSend.request actions */ function pfsCapacityUpdateEpic({}, state$, { log, address, network, signer, config$ }) { return state$.pipe((0, utils_1.groupChannel)(), (0, operators_1.mergeMap)((grouped$) => grouped$.pipe((0, operators_1.pairwise)(), // skips first emission on startup (0, operators_1.withLatestFrom)(config$), // ignore actions if channel not open or while/if pfs is disabled (0, operators_1.filter)(([[, channel], { pfsMode }]) => channel.state === state_1.ChannelState.open && pfsMode !== types_2.PfsMode.disabled), (0, operators_1.debounce)(([[prev, cur], { httpTimeout }]) => cur.own.locks.length > prev.own.locks.length || cur.partner.locks.length > prev.partner.locks.length ? // if either lock increases, a transfer is pending, debounce by httpTimeout=30s (0, rxjs_1.timer)(httpTimeout) : (0, rxjs_1.of)(1)), (0, operators_1.switchMap)(([[, channel], { revealTimeout }]) => { const tokenNetwork = channel.tokenNetwork; const partner = channel.partner.address; const { ownCapacity, partnerCapacity } = (0, utils_1.channelAmounts)(channel); const message = { type: types_1.MessageType.PFS_CAPACITY_UPDATE, canonical_identifier: { chain_identifier: bignumber_1.BigNumber.from(network.chainId), token_network_address: tokenNetwork, channel_identifier: bignumber_1.BigNumber.from(channel.id), }, updating_participant: address, other_participant: partner, updating_nonce: channel.own.balanceProof.nonce, other_nonce: channel.partner.balanceProof.nonce, updating_capacity: ownCapacity, other_capacity: partnerCapacity, reveal_timeout: bignumber_1.BigNumber.from(revealTimeout), }; const msgId = (0, utils_3.makeMessageId)().toString(); return (0, rxjs_1.defer)(() => (0, utils_2.signMessage)(signer, message, { log })).pipe((0, operators_1.map)((signed) => actions_2.messageServiceSend.request({ message: signed }, { service: types_2.Service.PFS, msgId })), (0, operators_1.catchError)((err) => { log.error('Error trying to generate & sign PFSCapacityUpdate', err); return rxjs_1.EMPTY; })); })))); } exports.pfsCapacityUpdateEpic = pfsCapacityUpdateEpic; /** * When monitoring a channel (either a new channel or a previously monitored one), send a matching * PFSFeeUpdate to PFSs, so they can pick us for mediation * * @param action$ - Observable of channelMonitored actions * @param state$ - Observable of RaidenStates * @param deps - Raiden epic dependencies * @param deps.log - Logger instance * @param deps.address - Our address * @param deps.network - Current network * @param deps.signer - Signer instance * @param deps.config$ - Config observable * @param deps.mediationFeeCalculator - Calculator for mediation fees schedule * @returns Observable of messageServiceSend.request actions */ function pfsFeeUpdateEpic({}, state$, { log, address, network, signer, config$, mediationFeeCalculator }) { return state$.pipe((0, utils_1.groupChannel)(), (0, operators_1.mergeMap)((grouped$) => (0, rxjs_1.combineLatest)([ grouped$, config$.pipe((0, rx_1.pluckDistinct)('caps')), config$.pipe((0, rx_1.pluckDistinct)('mediationFees')), ]).pipe((0, operators_1.filter)(([, caps]) => !!(0, utils_4.getCap)(caps, constants_1.Capabilities.MEDIATE)), (0, operators_1.map)(([channel, , mediationFees]) => { const schedule = mediationFeeCalculator.schedule(mediationFees, channel); // using channel feeSchedule above, build a PFSFeeUpdate's schedule payload return [channel, schedule]; }), // reactive on channel state and config changes, distinct on schedule's payload (0, operators_1.distinctUntilChanged)(([, sched1], [, sched2]) => (0, isEqual_1.default)(sched1, sched2)), (0, operators_1.switchMap)(([channel, schedule]) => { const message = { type: types_1.MessageType.PFS_FEE_UPDATE, canonical_identifier: { chain_identifier: bignumber_1.BigNumber.from(network.chainId), token_network_address: channel.tokenNetwork, channel_identifier: bignumber_1.BigNumber.from(channel.id), }, updating_participant: address, timestamp: (0, helpers_1.makeTimestamp)(), fee_schedule: schedule, }; const msgId = (0, utils_3.makeMessageId)().toString(); const meta = { service: types_2.Service.PFS, msgId }; return (0, rxjs_1.from)((0, utils_2.signMessage)(signer, message, { log })).pipe((0, operators_1.map)((signed) => actions_2.messageServiceSend.request({ message: signed }, meta)), (0, operators_1.catchError)((err) => { log.error('Error trying to generate & sign PFSFeeUpdate', err); return rxjs_1.EMPTY; })); }), (0, operators_1.takeUntil)(grouped$.pipe((0, operators_1.filter)((channel) => channel.state !== state_1.ChannelState.open))))), (0, rx_1.completeWith)(state$)); } exports.pfsFeeUpdateEpic = pfsFeeUpdateEpic; /** * Fetch & monitors ServiceRegistry's RegisteredService events, keep track of valid_till expiration * and aggregate list of valid service addresses * * Notice this epic only deals with the events & addresses, and don't fetch URLs, which need to be * fetched on-demand through [[pfsInfo]] & [[pfsListInfo]]. * * @param action$ - Observable of RaidenActions * @param state$ - Observable of RaidenStates * @param deps - RaidenEpicDeps object * @param deps.provider - Provider instance * @param deps.serviceRegistryContract - ServiceRegistry contract instance * @param deps.contractsInfo - Contracts info mapping * @param deps.config$ - Config observable * @param deps.init$ - Init$ tasks subject * @returns Observable of servicesValid actions */ function pfsServiceRegistryMonitorEpic(action$, state$, { provider, serviceRegistryContract, contractsInfo, config$, init$ }) { const blockNumber$ = action$.pipe((0, operators_1.filter)(actions_1.newBlock.is), (0, operators_1.pluck)('payload', 'blockNumber')); return state$.pipe((0, operators_1.first)(), (0, operators_1.switchMap)(({ services: initialServices }) => { const initSub = new rxjs_1.AsyncSubject(); init$.next(initSub); return (0, ethers_1.fromEthersEvent)(provider, serviceRegistryContract.filters.RegisteredService(null, null, null, null), { // if initialServices is empty, fetch since registry deploy block, else, resetEventsBlock fromBlock: (0, isEmpty_1.default)(initialServices) ? contractsInfo.TokenNetworkRegistry.block_number : undefined, confirmations: config$.pipe((0, operators_1.pluck)('confirmationBlocks')), blockNumber$, onPastCompleted: () => { initSub.next(null); initSub.complete(); }, }).pipe((0, operators_1.withLatestFrom)(state$, config$), (0, operators_1.filter)(([{ blockNumber: eventBlock }, { blockNumber }, { confirmationBlocks }]) => !!eventBlock && eventBlock + confirmationBlocks <= blockNumber), (0, operators_1.pluck)(0), (0, operators_1.map)((0, ethers_1.logToContractEvent)(serviceRegistryContract)), (0, operators_1.withLatestFrom)(state$), // merge new entry with stored state (0, operators_1.map)(([[service, valid_till], { services }]) => ({ ...services, [service]: valid_till.toNumber() * 1000, })), (0, operators_1.startWith)(initialServices), // switchMap with newBlock events ensure this filter gets re-evaluated every block // and filters out entries which aren't valid anymore (0, operators_1.switchMap)((services) => action$.pipe((0, operators_1.filter)(actions_1.newBlock.is), (0, operators_1.startWith)(true), (0, operators_1.map)(() => (0, pickBy_1.default)(services, (till) => Date.now() < till))))); }), (0, operators_1.distinctUntilChanged)(isEqual_1.default), (0, operators_1.map)((valid) => (0, actions_5.servicesValid)(valid)), (0, rx_1.completeWith)(action$)); } exports.pfsServiceRegistryMonitorEpic = pfsServiceRegistryMonitorEpic; //# sourceMappingURL=pathfinding.js.map