UNPKG

7.35 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const log_1 = require("../common/log");
4const ilp_protocol_ccp_1 = require("ilp-protocol-ccp");
5const MINIMUM_UPDATE_INTERVAL = 150;
6const MAX_EPOCHS_PER_UPDATE = 50;
7class CcpSender {
8 constructor({ accountId, plugin, forwardingRoutingTable, getOwnAddress, getAccountRelation, routeExpiry, routeBroadcastInterval }) {
9 this.mode = ilp_protocol_ccp_1.Mode.MODE_IDLE;
10 this.lastKnownEpoch = 0;
11 this.lastUpdate = 0;
12 this.scheduleRouteUpdate = () => {
13 if (this.sendRouteUpdateTimer) {
14 clearTimeout(this.sendRouteUpdateTimer);
15 this.sendRouteUpdateTimer = undefined;
16 }
17 if (this.mode !== ilp_protocol_ccp_1.Mode.MODE_SYNC) {
18 return;
19 }
20 const lastUpdate = this.lastUpdate;
21 const nextEpoch = this.lastKnownEpoch;
22 let delay;
23 if (nextEpoch < this.forwardingRoutingTable.currentEpoch) {
24 delay = 0;
25 }
26 else {
27 delay = this.routeBroadcastInterval - (Date.now() - lastUpdate);
28 }
29 delay = Math.max(MINIMUM_UPDATE_INTERVAL, delay);
30 this.log.trace('scheduling next route update. accountId=%s delay=%s currentEpoch=%s peerHasEpoch=%s', this.accountId, delay, this.forwardingRoutingTable.currentEpoch, this.lastKnownEpoch);
31 this.sendRouteUpdateTimer = setTimeout(() => {
32 this.sendSingleRouteUpdate()
33 .then(() => this.scheduleRouteUpdate())
34 .catch((err) => {
35 const errInfo = (err instanceof Object && err.stack) ? err.stack : err;
36 this.log.debug('failed to broadcast route information to peer. peer=%s error=%s', this.accountId, errInfo);
37 });
38 }, delay);
39 this.sendRouteUpdateTimer.unref();
40 };
41 this.plugin = plugin;
42 this.forwardingRoutingTable = forwardingRoutingTable;
43 this.log = log_1.create(`ccp-sender[${accountId}]`);
44 this.accountId = accountId;
45 this.getOwnAddress = getOwnAddress;
46 this.getAccountRelation = getAccountRelation;
47 this.routeExpiry = routeExpiry;
48 this.routeBroadcastInterval = routeBroadcastInterval;
49 }
50 stop() {
51 if (this.sendRouteUpdateTimer) {
52 clearTimeout(this.sendRouteUpdateTimer);
53 }
54 }
55 getAccountId() {
56 return this.accountId;
57 }
58 getLastUpdate() {
59 return this.lastUpdate;
60 }
61 getLastKnownEpoch() {
62 return this.lastKnownEpoch;
63 }
64 getMode() {
65 return this.mode;
66 }
67 getStatus() {
68 return {
69 epoch: this.lastKnownEpoch,
70 mode: ilp_protocol_ccp_1.ModeReverseMap[this.mode]
71 };
72 }
73 handleRouteControl({ mode, lastKnownRoutingTableId, lastKnownEpoch, features }) {
74 if (this.mode !== mode) {
75 this.log.trace('peer requested changing routing mode. oldMode=%s newMode=%s', ilp_protocol_ccp_1.ModeReverseMap[this.mode], ilp_protocol_ccp_1.ModeReverseMap[mode]);
76 }
77 this.mode = mode;
78 if (lastKnownRoutingTableId !== this.forwardingRoutingTable.routingTableId) {
79 this.log.trace('peer has old routing table id, resetting lastKnownEpoch to zero. theirTableId=%s correctTableId=%s', lastKnownRoutingTableId, this.forwardingRoutingTable.routingTableId);
80 this.lastKnownEpoch = 0;
81 }
82 else {
83 this.log.trace('peer epoch set. epoch=%s currentEpoch=%s', this.accountId, lastKnownEpoch, this.forwardingRoutingTable.currentEpoch);
84 this.lastKnownEpoch = lastKnownEpoch;
85 }
86 if (this.mode === ilp_protocol_ccp_1.Mode.MODE_SYNC) {
87 this.scheduleRouteUpdate();
88 }
89 else {
90 if (this.sendRouteUpdateTimer) {
91 clearTimeout(this.sendRouteUpdateTimer);
92 this.sendRouteUpdateTimer = undefined;
93 }
94 }
95 }
96 async sendSingleRouteUpdate() {
97 this.lastUpdate = Date.now();
98 if (!this.plugin.isConnected()) {
99 this.log.debug('cannot send routes, plugin not connected (yet).');
100 return;
101 }
102 const nextRequestedEpoch = this.lastKnownEpoch;
103 const allUpdates = this.forwardingRoutingTable.log
104 .slice(nextRequestedEpoch, nextRequestedEpoch + MAX_EPOCHS_PER_UPDATE);
105 const toEpoch = nextRequestedEpoch + allUpdates.length;
106 const relation = this.getAccountRelation(this.accountId);
107 function isRouteUpdate(update) {
108 return !!update;
109 }
110 const updates = allUpdates
111 .filter(isRouteUpdate)
112 .map((update) => {
113 if (!update.route)
114 return update;
115 if (update.route.nextHop === this.accountId ||
116 (relation === 'parent' &&
117 ['peer', 'parent'].indexOf(this.getAccountRelation(update.route.nextHop)) !== -1)) {
118 return Object.assign({}, update, { route: undefined });
119 }
120 else {
121 return update;
122 }
123 });
124 const newRoutes = [];
125 const withdrawnRoutes = [];
126 for (const update of updates) {
127 if (update.route) {
128 newRoutes.push({
129 prefix: update.prefix,
130 nextHop: update.route.nextHop,
131 path: update.route.path,
132 auth: update.route.auth
133 });
134 }
135 else {
136 withdrawnRoutes.push({
137 prefix: update.prefix,
138 epoch: update.epoch
139 });
140 }
141 }
142 this.log.trace('broadcasting routes to peer. speaker=%s peer=%s fromEpoch=%s toEpoch=%s routeCount=%s unreachableCount=%s', this.getOwnAddress(), this.accountId, this.lastKnownEpoch, toEpoch, newRoutes.length, withdrawnRoutes.length);
143 const routeUpdate = {
144 speaker: this.getOwnAddress(),
145 routingTableId: this.forwardingRoutingTable.routingTableId,
146 holdDownTime: this.routeExpiry,
147 currentEpochIndex: this.forwardingRoutingTable.currentEpoch,
148 fromEpochIndex: this.lastKnownEpoch,
149 toEpochIndex: toEpoch,
150 newRoutes: newRoutes.map(r => (Object.assign({}, r, { nextHop: undefined, auth: r.auth, props: [] }))),
151 withdrawnRoutes: withdrawnRoutes.map(r => r.prefix)
152 };
153 const previousNextRequestedEpoch = this.lastKnownEpoch;
154 this.lastKnownEpoch = toEpoch;
155 const timeout = this.routeBroadcastInterval;
156 const timerPromise = new Promise((resolve, reject) => {
157 const timer = setTimeout(() => reject(new Error('route update timed out.')), timeout);
158 timer.unref();
159 });
160 try {
161 await Promise.race([
162 this.plugin.sendData(ilp_protocol_ccp_1.serializeCcpRouteUpdateRequest(routeUpdate)),
163 timerPromise
164 ]);
165 }
166 catch (err) {
167 this.lastKnownEpoch = previousNextRequestedEpoch;
168 throw err;
169 }
170 }
171}
172exports.default = CcpSender;
173//# sourceMappingURL=ccp-sender.js.map
\No newline at end of file