Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package refit.client;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.List;
import refit.config.REFITConfig;
import refit.config.REFITLogger;
import refit.message.REFITMessageAuthentication;
import refit.message.REFITMessageType;
import refit.message.REFITReplyBase;
import refit.message.REFITUniqueID;
import refit.util.REFITBallotBox;
import refit.util.REFITIntervalStatistics;
public class REFITReplyCertificate {
public final REFITBallotBox<Short, ByteBuffer, REFITReplyBase> replies;
private static final int STABILITY_THRESHOLD = (REFITConfig.AUTHENTICATE_MESSAGES) ? REFITConfig.FAULTS_TO_TOLERATE + 1 : 1;
private static final int READOPT_STABILITY_THRESHOLD = (REFITConfig.AUTHENTICATE_MESSAGES)
? (REFITConfig.EXEC_GROUP_SIZE + REFITConfig.FAULTS_TO_TOLERATE + 1 + 1) / 2
: (REFITConfig.EXEC_GROUP_SIZE + 1 + 1) / 2;
private final REFITMessageAuthentication messageAuthentication;
private REFITUniqueID uid;
public REFITReplyBase result;
private final boolean[] legitimateSenders;
private long requestTime;
private short[] replyTimes = new short[REFITConfig.TOTAL_NR_OF_REPLICAS];
private boolean isTotalOrder;
private boolean isAck;
private final REFITIntervalStatistics statistics;
public REFITReplyCertificate(REFITMessageAuthentication messageAuthentication, REFITIntervalStatistics statistics, boolean[] legitimateSenders) {
this.replies = new REFITBallotBox<>(REFITConfig.USE_PBFT_READ_OPTIMIZATION ? READOPT_STABILITY_THRESHOLD : STABILITY_THRESHOLD);
this.messageAuthentication = messageAuthentication;
this.statistics = statistics;
this.legitimateSenders = legitimateSenders;
}
public void init(REFITUniqueID uid, boolean isTotalOrder, boolean isAck) {
replies.clear();
this.uid = uid;
result = null;
requestTime = System.currentTimeMillis();
Arrays.fill(replyTimes, REFITConfig.PROBE_TIMEOUT);
this.isTotalOrder = isTotalOrder;
this.isAck = isAck;
}
public boolean add(REFITReplyBase reply) {
long replyTime = System.currentTimeMillis();
// Check reply
if (reply == null) return false;
if (!uid.equals(reply.uid)) return false;
if (isAck && reply.type != REFITMessageType.ACK_REPLY || !isAck && reply.type != REFITMessageType.REPLY)
return false;
if (!messageAuthentication.verifyUnicastMAC(reply, legitimateSenders)) return false;
// Prepare vote
ByteBuffer vote = ByteBuffer.wrap(reply.getPayloadHash());
// Check whether the reply is a late full reply
ByteBuffer decision = replies.getDecision();
if (reply.isFullReply() && vote.equals(decision)) {
result = reply;
return true;
}
// Cast vote
boolean success = replies.add(reply.from, vote, reply);
if (success) {
replyTimes[reply.from] = (short) (replyTime - requestTime);
checkResult();
}
return success;
}
public boolean isStable() {
return (result != null);
}
public short[] getReplyTimes() {
return replyTimes;
}
public int[] getPerExecutorViewId() {
int[] perExecutorViewId = new int[REFITConfig.EXEC_GROUP_SIZE];
for (REFITReplyBase reply : replies.getDecidingBallots()) {
perExecutorViewId[reply.from] = reply.viewID();
}
return perExecutorViewId;
}
private void checkResult() {
if (isStable()) return;
if (REFITConfig.ENABLE_DEBUG_CHECKS && isTotalOrder && replies.votesDiffer())
REFITLogger.logWarning("[REPLY]", "received different results for request " + uid);
List<REFITReplyBase> correctReplies = replies.getDecidingBallots();
if (correctReplies == null) return;
for (REFITReplyBase reply : correctReplies) {
if (!reply.isFullReply()) continue;
result = reply;
break;
}
if (REFITConfig.CLIENT_REPLY_STATISTICS) updateStats(correctReplies);
}
public boolean isCompletable() {
if (isStable()) {
return true;
}
int remainingVotes = REFITConfig.EXEC_GROUP_SIZE - replies.getVoteCount();
int largestBallot = replies.getLargestBallotSize();
return remainingVotes + largestBallot >= replies.getDecisionThreshold();
}
private void updateStats(List<REFITReplyBase> correctReplies) {
for (REFITReplyBase reply : correctReplies) {
statistics.trackReply(reply.from);
}
}
}