raiden-ts
Version:
Raiden Light Client Typescript/Javascript SDK
181 lines • 11.4 kB
JavaScript
;
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