Coverage Summary for Class: Stats (org.ethereum.net.server)
Class |
Method, %
|
Line, %
|
Stats |
0%
(0/11)
|
0%
(0/62)
|
Stats$1 |
0%
(0/1)
|
0%
(0/1)
|
Total |
0%
(0/12)
|
0%
(0/63)
|
1 package org.ethereum.net.server;
2
3 import co.rsk.net.messages.MessageType;
4 import com.google.common.annotations.VisibleForTesting;
5
6 /**
7 * The general idea here is that a score is of the form:
8 * score = type * (a * x + b * y)
9 * where type is a value that depends on the message type, x and y are constants
10 * and a and b are calculated depending on the behaviour of the peer
11 * for a we use time between messages and for b the ratio of imported best vs other imported block messages
12 *
13 * The alternative of updating the score based on the previous score is hard. It becomes really difficult
14 * to predict the behavior of the network and make updates on the algorithms.
15 *
16 * This methodology is simpler. We select easy things to evaluate and put a weight on them, Then we can
17 * improve the system by iterating over the constants or changing how a particular value is calculated
18 *
19 * Some values are estimated using exponential moving average. Basically we say
20 * x_n = x_(n-1) + alpha * (x_(n-1) - x)
21 * where x is the new measure of such value
22 *
23 * Negative scores represent rejected messages
24 */
25 public class Stats {
26
27 // last message timestamp in ms
28 private long lastMessage;
29
30 // Current minute messages counter
31 private long minute;
32 // Reject messages over this treshold
33 // Is calculated using Exponential moving average
34 private long perMinuteThreshold;
35
36 // events counters
37 // 100% heuristics
38 private long importedBest;
39 private long importedNotBest;
40
41 // vars that get updated with EMA
42 // msg per minutes and "average" time between messages
43 private double mpm;
44 private double avg; //in ms
45
46 // how fast avg and mpm update
47 private double alpha_m;
48 private double alpha_a;
49
50 // scores for blocks and others
51 private double maxBlock;
52 private double maxOther;
53
54 public Stats() {
55 avg = 500;
56 alpha_m = 0.3;
57 alpha_a = 0.03;
58
59 perMinuteThreshold = 1000;
60
61 maxBlock = 200;
62 maxOther = 100;
63 mpm = 1;
64 }
65
66
67 public synchronized double update(long timestamp, MessageType type) {
68 long min = timestamp / 60000;
69 long delta = timestamp - lastMessage;
70 if (delta <= 0) {
71 delta = 1;
72 }
73
74 if (min == lastMessage / 60000) {
75 minute++;
76 } else {
77 // reset to 0 if it passed more than a minute from previous message
78 if ((timestamp - lastMessage) / 60000 > 1) {
79 mpm = 0;
80 }
81 mpm += alpha_m * ((double)minute - mpm);
82 minute = 0;
83 }
84
85 if (minute > perMinuteThreshold) {
86 return -1;
87 }
88
89
90 avg += alpha_a * (delta - avg);
91
92 double res = score(type);
93 lastMessage = timestamp;
94
95 return res;
96 }
97
98 public double score(MessageType type) {
99 double a = avg / 1000;
100 a = (a > 1) ? 1 : a;
101
102 double b = (5 * importedBest) / (double)(importedNotBest + importedBest + 10);
103 b = (b > 1) ? 1 : b;
104 double res = a * maxOther + b * maxBlock;
105
106 res *= priority(type);
107
108 // mpm and avg are kind of redundant here
109 // both somehow count messages and reduce the score based on that
110 double m = 1 + mpm / 90;
111 if (m > 3) {
112 m = 3;
113 }
114
115 res /= m;
116 return res;
117 }
118
119 private double priority(MessageType type) {
120
121 switch (type) {
122 case TRANSACTIONS:
123 return 2;
124 case BLOCK_MESSAGE:
125 return 10;
126 case STATUS_MESSAGE:
127 return 1;
128 case NEW_BLOCK_HASHES:
129 return 3;
130 case GET_BLOCK_MESSAGE:
131 return 1;
132 case BODY_REQUEST_MESSAGE:
133 return 1;
134 case BLOCK_REQUEST_MESSAGE:
135 return 1;
136 case BODY_RESPONSE_MESSAGE:
137 return 1;
138 case BLOCK_RESPONSE_MESSAGE:
139 return 15;
140 case NEW_BLOCK_HASH_MESSAGE:
141 return 3;
142 case SKELETON_REQUEST_MESSAGE:
143 return 1;
144 case SKELETON_RESPONSE_MESSAGE:
145 return 3;
146 case BLOCK_HASH_REQUEST_MESSAGE:
147 return 1;
148 case BLOCK_HASH_RESPONSE_MESSAGE:
149 return 3;
150 case BLOCK_HEADERS_REQUEST_MESSAGE:
151 return 0.5;
152 case BLOCK_HEADERS_RESPONSE_MESSAGE:
153 return 5;
154 }
155 return 0.0;
156 }
157 public synchronized void imported(boolean best) {
158 if (best) {
159 importedBest++;
160 } else {
161 importedNotBest++;
162 }
163 }
164
165
166 @Override
167 public String toString() {
168 return "Stats{" +
169 "lastMessage=" + lastMessage +
170 ", minute=" + minute +
171 ", perMinuteThreshold=" + perMinuteThreshold +
172 ", importedBest=" + importedBest +
173 ", importedNotBest=" + importedNotBest +
174 ", mpm=" + mpm +
175 ", avg=" + avg +
176 ", alpha_m=" + alpha_m +
177 ", alpha_a=" + alpha_a +
178 ", maxBlock=" + maxBlock +
179 ", maxOther=" + maxOther +
180 '}';
181 }
182
183 @VisibleForTesting
184 public double getMpm() {
185 return mpm;
186 }
187
188
189 @VisibleForTesting
190 public long getMinute() {
191 return minute;
192 }
193
194 @VisibleForTesting
195 public void setAvg(double avg) {
196 this.avg = avg;
197 }
198
199 @VisibleForTesting
200 public void setImportedBest(int importedBest) {
201 this.importedBest = importedBest;
202 }
203
204 @VisibleForTesting
205 public void setImportedNotBest(int importedNotBest) {
206 this.importedNotBest = importedNotBest;
207 }
208
209 }