UNPKG

raiden-ts

Version:

Raiden Light Client Typescript/Javascript SDK

164 lines 9.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.msMonitorNewBPEpic = exports.msMonitorRequestEpic = void 0; const bignumber_1 = require("@ethersproject/bignumber"); const bytes_1 = require("@ethersproject/bytes"); const constants_1 = require("@ethersproject/constants"); const rxjs_1 = require("rxjs"); const operators_1 = require("rxjs/operators"); const actions_1 = require("../../channels/actions"); const utils_1 = require("../../channels/utils"); 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 data_1 = require("../../utils/data"); const ethers_1 = require("../../utils/ethers"); const rx_1 = require("../../utils/rx"); const types_2 = require("../../utils/types"); const actions_3 = require("../actions"); const types_3 = require("../types"); /** * Makes a *Map callback which returns an observable of actions to send RequestMonitoring messages * * @param state$ - Observable of RaidenStates * @param channel - Channel state to generate a a monitoring request for * @param deps - Epics dependencies * @param deps.address - Our Address * @param deps.log - Logger instance * @param deps.network - Current network * @param deps.signer - Signer instance * @param deps.contractsInfo - Contracts info mapping * @param deps.latest$ - Latest observable * @param deps.config$ - Config observable * @returns An operator which receives prev and current Channel states and returns a cold * Observable of messageServiceSend.request actions to monitoring services */ function makeMonitoringRequest$(state$, channel, { address, log, network, signer, contractsInfo, latest$, config$ }) { const { partnerUnlocked } = (0, utils_1.channelAmounts)(channel); // give up early if nothing to lose if (partnerUnlocked.isZero()) return rxjs_1.EMPTY; return (0, rxjs_1.combineLatest)([latest$, config$]).pipe( // combineLatest + filter ensures it'll pass if anything here changes (0, operators_1.filter)(([{ udcDeposit }, { monitoringReward, rateToSvt }]) => // ignore actions while/if config.monitoringReward isn't enabled !!monitoringReward?.gt(constants_1.Zero) && // wait for udcDepost.balance >= monitoringReward, fires immediately if already udcDeposit.balance.gte(monitoringReward) && // use partner's total off & on-chain unlocked, total we'd lose if don't update BP partnerUnlocked // use rateToSvt to convert to equivalent SVT, and pass only if > monitoringReward; // default rate=MaxUint256 means it'll ALWAYS monitor if no rate is set for token .mul(rateToSvt[channel.token] ?? constants_1.MaxUint256) .div(constants_1.WeiPerEther) .gt(monitoringReward)), (0, operators_1.take)(1), // take/act on first time all conditions above pass (0, rx_1.completeWith)(state$, 10), // if conditions weren't met on shutdown, give up (0, operators_1.mergeMap)(([, { monitoringReward }]) => { const balanceProof = channel.partner.balanceProof; const balanceHash = (0, utils_2.createBalanceHash)(balanceProof); const nonClosingMessage = (0, bytes_1.concat)([ (0, data_1.encode)(channel.tokenNetwork, 20), (0, data_1.encode)(network.chainId, 32), (0, data_1.encode)(utils_2.MessageTypeId.BALANCE_PROOF_UPDATE, 32), (0, data_1.encode)(channel.id, 32), (0, data_1.encode)(balanceHash, 32), (0, data_1.encode)(balanceProof.nonce, 32), (0, data_1.encode)(balanceProof.additionalHash, 32), (0, data_1.encode)(balanceProof.signature, 65), // partner's signature for this balance proof ]); // UInt8Array of 277 bytes const msgId = (0, utils_3.makeMessageId)().toString(); // first sign the nonClosing signature, then the actual message return (0, rxjs_1.from)(signer.signMessage(nonClosingMessage)).pipe((0, operators_1.mergeMap)((nonClosingSignature) => (0, utils_2.signMessage)(signer, { type: types_1.MessageType.MONITOR_REQUEST, balance_proof: { chain_id: balanceProof.chainId, token_network_address: balanceProof.tokenNetworkAddress, channel_identifier: bignumber_1.BigNumber.from(channel.id), nonce: balanceProof.nonce, balance_hash: balanceHash, additional_hash: balanceProof.additionalHash, signature: balanceProof.signature, }, non_closing_participant: address, non_closing_signature: nonClosingSignature, monitoring_service_contract_address: contractsInfo.MonitoringService.address, reward_amount: monitoringReward, }, { log })), (0, operators_1.map)((message) => actions_2.messageServiceSend.request({ message }, { service: types_3.Service.MS, msgId }))); }), (0, rx_1.catchAndLog)({ log: log.warn }, 'Error trying to generate & sign MonitorRequest')); } /** * Handle balanceProof change from partner (received transfers) and request monitoring from MS * * @param action$ - Observable of channelDeposit.success actions * @param state$ - Observable of RaidenStates * @param deps - Epics dependencies * @returns Observable of messageServiceSend.request actions */ function msMonitorRequestEpic({}, state$, deps) { return state$.pipe((0, utils_1.groupChannel)(), (0, operators_1.withLatestFrom)(deps.config$), (0, operators_1.mergeMap)(([grouped$, { httpTimeout }]) => grouped$.pipe( // act only if partner's transferredAmount or lockedAmount changes (0, operators_1.distinctUntilChanged)((a, b) => b.partner.balanceProof.transferredAmount.eq(a.partner.balanceProof.transferredAmount) && b.partner.balanceProof.lockedAmount.eq(a.partner.balanceProof.lockedAmount) && b.partner.locks === a.partner.locks), (0, operators_1.pairwise)(), // distinctUntilChanged allows first, so pair and skips it (0, operators_1.debounce)(([prev, cur]) => // if partner lock increases, a transfer is pending, debounce by httpTimeout=30s // otherwise transfer completed, emits immediately cur.partner.locks.length > prev.partner.locks.length ? (0, rxjs_1.timer)(httpTimeout) : (0, rxjs_1.of)(1)), // switchMap may unsubscribe from previous udcDeposit wait/signature prompts if partner's // balanceProof balance changes in the meantime (0, operators_1.switchMap)(([, channel]) => makeMonitoringRequest$(state$, channel, deps))))); } exports.msMonitorRequestEpic = msMonitorRequestEpic; /** * Monitors MonitoringService contract and fires events when an MS sent a BP in our behalf. * * When this epic is subscribed (startup), it fetches events since 'provider.resetEventsBlock', * which is set to latest monitored block, so on startup we always pick up events that were fired * while offline, and keep monitoring while online, although it isn't probable that MS would quick * in while we're online, since [[channelUpdateEpic]] would update the channel ourselves. * * @param action$ - Observable of RaidenActions * @param state$ - Observable of RaidenStates * @param deps - Epics dependencies * @param deps.provider - Provider instance * @param deps.monitoringServiceContract - MonitoringService contract instance * @param deps.address - Our address * @param deps.config$ - Config observable * @param deps.init$ - Subject of initial sync tasks * @returns Observable of msBalanceProofSent actions */ function msMonitorNewBPEpic(action$, state$, { provider, monitoringServiceContract, address, config$, init$ }) { const initSub = new rxjs_1.AsyncSubject(); init$.next(initSub); return (0, ethers_1.fromEthersEvent)(provider, monitoringServiceContract.filters.NewBalanceProofReceived(null, null, null, null, null, address), { confirmations: config$.pipe((0, operators_1.pluck)('confirmationBlocks')), blockNumber$: action$.pipe((0, operators_1.filter)(actions_1.newBlock.is), (0, operators_1.pluck)('payload', 'blockNumber')), onPastCompleted: () => { initSub.next(null); initSub.complete(); }, }).pipe((0, rx_1.completeWith)(state$), (0, operators_1.map)((0, ethers_1.logToContractEvent)(monitoringServiceContract)), // should never fail, as per filter (0, operators_1.filter)(([, , , , , raidenAddress]) => raidenAddress === address), (0, operators_1.withLatestFrom)(state$, config$), (0, operators_1.map)(([[tokenNetwork, id, reward, nonce, monitoringService, , event], state, { confirmationBlocks },]) => { const channel = Object.values(state.channels) .concat(Object.values(state.oldChannels)) .find((c) => c.tokenNetwork === tokenNetwork && id.eq(c.id)); const txBlock = event.blockNumber; if (!channel || !txBlock) return; return (0, actions_3.msBalanceProofSent)({ tokenNetwork: tokenNetwork, partner: channel.partner.address, id: channel.id, reward: reward, nonce: nonce, monitoringService: monitoringService, txHash: event.transactionHash, txBlock, confirmed: txBlock + confirmationBlocks <= state.blockNumber ? true : undefined, }); }), (0, operators_1.filter)(types_2.isntNil)); } exports.msMonitorNewBPEpic = msMonitorNewBPEpic; //# sourceMappingURL=monitor.js.map