Timing, Attacks
[iotcloud.git] / version2 / src / java / iotcloud / PendingTransaction.java
1 package iotcloud;
2
3 import java.util.Set;
4 import java.util.Map;
5 import java.util.HashSet;
6
7 import javax.script.ScriptException;
8 import java.lang.NullPointerException;
9 import java.nio.ByteBuffer;
10
11
12 class PendingTransaction {
13
14     private Set<KeyValue> keyValueUpdateSet = null;
15     private Set<KeyValue> keyValueGuardSet = null;
16     private long arbitrator = -1;
17     private long clientLocalSequenceNumber = -1;
18     private long machineId = -1;
19
20     private int currentDataSize = 0;
21
22     public PendingTransaction(long _machineId) {
23         machineId = _machineId;
24         keyValueUpdateSet = new HashSet<KeyValue>();
25         keyValueGuardSet = new HashSet<KeyValue>();
26     }
27
28     /**
29      * Add a new key value to the updates
30      *
31      */
32     public void addKV(KeyValue newKV) {
33
34         KeyValue rmKV = null;
35
36         // Make sure there are no duplicates
37         for (KeyValue kv : keyValueUpdateSet) {
38             if (kv.getKey().equals(newKV.getKey())) {
39
40                 // Remove key if we are adding a newer version of the same key
41                 rmKV = kv;
42                 break;
43             }
44         }
45
46         // Remove key if we are adding a newer version of the same key
47         if (rmKV != null) {
48             keyValueUpdateSet.remove(rmKV);
49             currentDataSize -= rmKV.getSize();
50         }
51
52         // Add the key to the hash set
53         keyValueUpdateSet.add(newKV);
54         currentDataSize += newKV.getSize();
55     }
56
57     /**
58      * Add a new key value to the guard set
59      *
60      */
61     public void addKVGuard(KeyValue newKV) {
62         // Add the key to the hash set
63         keyValueGuardSet.add(newKV);
64         currentDataSize += newKV.getSize();
65     }
66
67     /**
68      * Checks if the arbitrator is the same
69      */
70     public boolean checkArbitrator(long arb) {
71         if (arbitrator == -1) {
72             arbitrator = arb;
73             return true;
74         }
75
76         return arb == arbitrator;
77     }
78
79     /**
80      * Get the transaction arbitrator
81      */
82     public long getArbitrator() {
83         return arbitrator;
84     }
85
86     /**
87      * Get the key value update set
88      */
89     public Set<KeyValue> getKVUpdates() {
90         return keyValueUpdateSet;
91     }
92
93     /**
94      * Get the key value update set
95      */
96     public Set<KeyValue> getKVGuard() {
97         return keyValueGuardSet;
98     }
99
100     public void setClientLocalSequenceNumber(long _clientLocalSequenceNumber) {
101         clientLocalSequenceNumber = _clientLocalSequenceNumber;
102     }
103
104     public long getClientLocalSequenceNumber() {
105         return clientLocalSequenceNumber;
106     }
107
108     public long getMachineId() {
109         return machineId;
110     }
111
112     public boolean evaluateGuard(Map<IoTString, KeyValue> keyValTableCommitted, Map<IoTString, KeyValue> keyValTableSpeculative, Map<IoTString, KeyValue> keyValTablePendingTransSpeculative) {
113         for (KeyValue kvGuard : keyValueGuardSet) {
114
115             // First check if the key is in the speculative table, this is the value of the latest assumption
116             KeyValue kv = keyValTablePendingTransSpeculative.get(kvGuard.getKey());
117
118
119             if (kv == null) {
120                 // if it is not in the pending trans table then check the speculative table and use that
121                 // value as our latest assumption
122                 kv = keyValTableSpeculative.get(kvGuard.getKey());
123             }
124
125
126             if (kv == null) {
127                 // if it is not in the speculative table then check the committed table and use that
128                 // value as our latest assumption
129                 kv = keyValTableCommitted.get(kvGuard.getKey());
130             }
131
132             if (kvGuard.getValue() != null) {
133                 if ((kv == null) || (!kvGuard.getValue().equals(kv.getValue()))) {
134                     return false;
135                 }
136             } else {
137                 if (kv != null) {
138                     return false;
139                 }
140             }
141         }
142         return true;
143     }
144
145     public Transaction createTransaction() {
146
147         Transaction newTransaction = new Transaction();
148         int transactionPartCount = 0;
149
150         // Convert all the data into a byte array so we can start partitioning
151         byte[] byteData = convertDataToBytes();
152
153         int currentPosition = 0;
154         int remaining = byteData.length;
155
156         while (remaining > 0) {
157
158             Boolean isLastPart = false;
159             // determine how much to copy
160             int copySize = TransactionPart.MAX_NON_HEADER_SIZE;
161             if (remaining <= TransactionPart.MAX_NON_HEADER_SIZE) {
162                 copySize = remaining;
163                 isLastPart = true; // last bit of data so last part
164             }
165
166             // Copy to a smaller version
167             byte[] partData = new byte[copySize];
168             System.arraycopy(byteData, currentPosition, partData, 0, copySize);
169
170             TransactionPart part = new TransactionPart(null, machineId, arbitrator, clientLocalSequenceNumber, transactionPartCount, partData, isLastPart);
171             newTransaction.addPartEncode(part);
172
173             // Update position, count and remaining
174             currentPosition += copySize;
175             transactionPartCount++;
176             remaining -= copySize;
177         }
178
179         // Add the Guard Conditions
180         for (KeyValue kv : keyValueGuardSet) {
181             newTransaction.addGuardKV(kv);
182         }
183
184         //  Add the updates
185         for (KeyValue kv : keyValueUpdateSet) {
186             newTransaction.addUpdateKV(kv);
187         }
188
189         return newTransaction;
190     }
191
192     private byte[] convertDataToBytes() {
193
194         // Calculate the size of the data
195         int sizeOfData = 2 * Integer.BYTES; // Number of Update KV's and Guard KV's
196         sizeOfData += currentDataSize;
197
198         // Data handlers and storage
199         byte[] dataArray = new byte[sizeOfData];
200         ByteBuffer bbEncode = ByteBuffer.wrap(dataArray);
201
202         // Encode the size of the updates and guard sets
203         bbEncode.putInt(keyValueGuardSet.size());
204         bbEncode.putInt(keyValueUpdateSet.size());
205
206         // Encode all the guard conditions
207         for (KeyValue kv : keyValueGuardSet) {
208             kv.encode(bbEncode);
209         }
210
211         // Encode all the updates
212         for (KeyValue kv : keyValueUpdateSet) {
213             kv.encode(bbEncode);
214         }
215
216         return bbEncode.array();
217     }
218 }