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