Newer
Older
package refit.client;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.List;
import refit.config.REFITConfig;
import refit.message.REFITMessageAuthentication;
import refit.message.REFITMessageType;
import refit.message.REFITReplyBase;
import refit.message.REFITUniqueID;
import refit.util.REFITBallotBox;
import refit.util.REFITIntervalStatistics;
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
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 + " " + replies);
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
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);
}
}
}