diff options
author | Florian Dold <florian.dold@gmail.com> | 2013-10-21 18:48:51 +0000 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2013-10-21 18:48:51 +0000 |
commit | 8ceb5e95568a0a5be9ca6fd28a31fe6b07e06f28 (patch) | |
tree | dffd85c26a583a68cce3435375263a51bf43618c /src/main/java/org/gnunet/voting/BallotTool.java | |
parent | 4cbf5731c126695bbd964552b92e7ae9151561e1 (diff) | |
download | gnunet-java-8ceb5e95568a0a5be9ca6fd28a31fe6b07e06f28.tar.gz gnunet-java-8ceb5e95568a0a5be9ca6fd28a31fe6b07e06f28.zip |
- use identity in voting
- group certificate tool instead of ca daemon
- consensus api fully implemented, consensus+testbed test
- authorities use consensus
- some progress on transport api
Diffstat (limited to 'src/main/java/org/gnunet/voting/BallotTool.java')
-rw-r--r-- | src/main/java/org/gnunet/voting/BallotTool.java | 502 |
1 files changed, 311 insertions, 191 deletions
diff --git a/src/main/java/org/gnunet/voting/BallotTool.java b/src/main/java/org/gnunet/voting/BallotTool.java index fe83789..452e606 100644 --- a/src/main/java/org/gnunet/voting/BallotTool.java +++ b/src/main/java/org/gnunet/voting/BallotTool.java | |||
@@ -25,221 +25,341 @@ import com.google.common.base.Charsets; | |||
25 | import com.google.common.io.ByteStreams; | 25 | import com.google.common.io.ByteStreams; |
26 | import com.google.common.io.Files; | 26 | import com.google.common.io.Files; |
27 | import com.google.common.io.OutputSupplier; | 27 | import com.google.common.io.OutputSupplier; |
28 | import org.gnunet.identity.Identity; | ||
29 | import org.gnunet.identity.IdentityCallback; | ||
30 | import org.gnunet.mesh.Mesh; | ||
31 | import org.gnunet.mesh.MeshRunabout; | ||
32 | import org.gnunet.mesh.TunnelEndHandler; | ||
33 | import org.gnunet.testbed.CompressedConfig; | ||
34 | import org.gnunet.util.AbsoluteTime; | ||
35 | import org.gnunet.util.Configuration; | ||
36 | import org.gnunet.util.PeerIdentity; | ||
28 | import org.gnunet.util.Program; | 37 | import org.gnunet.util.Program; |
29 | import org.gnunet.util.crypto.EcdsaPrivateKey; | 38 | import org.gnunet.util.crypto.EddsaSignature; |
30 | import org.gnunet.util.getopt.Argument; | 39 | import org.gnunet.util.getopt.Argument; |
31 | import org.gnunet.util.getopt.ArgumentAction; | 40 | import org.gnunet.util.getopt.ArgumentAction; |
41 | import org.gnunet.voting.messages.*; | ||
32 | 42 | ||
33 | import java.io.File; | 43 | import java.io.File; |
34 | import java.io.FileOutputStream; | 44 | import java.io.FileOutputStream; |
35 | import java.io.IOException; | 45 | import java.io.IOException; |
36 | import java.io.InputStream; | 46 | import java.io.InputStream; |
47 | import java.util.List; | ||
48 | import java.util.Random; | ||
37 | 49 | ||
38 | /** | 50 | /** |
39 | * Tool for creating, manipulating and submitting ballot files. | 51 | * Tool for creating, manipulating and submitting ballot files. |
40 | */ | 52 | */ |
41 | public class BallotTool { | 53 | public class BallotTool extends Program { |
42 | public static void main(String[] args) { | 54 | @Argument( |
43 | new Program(args) { | 55 | shortname = "e", |
44 | @Argument( | 56 | longname = "ego", |
45 | shortname = "q", | 57 | action = ArgumentAction.STORE_STRING, |
46 | longname = "query", | 58 | description = "ego to use for the operation") |
47 | action = ArgumentAction.SET, | 59 | String egoName = null; |
48 | description = "query election result") | ||
49 | boolean query = false; | ||
50 | |||
51 | @Argument( | ||
52 | shortname = "s", | ||
53 | longname = "submit", | ||
54 | action = ArgumentAction.SET, | ||
55 | description = "submit the vote to the authorities") | ||
56 | boolean submit = false; | ||
57 | |||
58 | @Argument( | ||
59 | shortname = "r", | ||
60 | longname = "register", | ||
61 | action = ArgumentAction.SET, | ||
62 | description = "register an election with the authorities") | ||
63 | boolean register = false; | ||
64 | |||
65 | @Argument( | ||
66 | shortname = "i", | ||
67 | longname = "issue", | ||
68 | action = ArgumentAction.SET, | ||
69 | description = "sign the ballot as issuer") | ||
70 | boolean issue = false; | ||
71 | |||
72 | @Argument( | ||
73 | shortname = "x", | ||
74 | longname = "select", | ||
75 | action = ArgumentAction.STORE_STRING, | ||
76 | argumentName = "CHOICE", | ||
77 | description = "select and encrypt a vote option") | ||
78 | String select = null; | ||
79 | |||
80 | @Argument( | ||
81 | shortname = "V", | ||
82 | longname = "verify", | ||
83 | action = ArgumentAction.SET, | ||
84 | description = "verify signatures in the ballot and show information") | ||
85 | boolean verify = false; | ||
86 | |||
87 | @Argument( | ||
88 | shortname = "t", | ||
89 | longname = "template", | ||
90 | action = ArgumentAction.SET, | ||
91 | description = "write a template ballot to the give ballot file") | ||
92 | boolean template = false; | ||
93 | |||
94 | @Override | ||
95 | protected String makeHelpText() { | ||
96 | return "gnunet-ballot [OPTIONS]... BALLOT [PRIVKEYFILE]\n" + | ||
97 | "Create, modify and execute operation on ballots."; | ||
98 | } | ||
99 | 60 | ||
100 | private void runTemplate(String filename) { | 61 | @Argument( |
101 | File f = new File(filename); | 62 | shortname = "q", |
102 | if (f.exists()) { | 63 | longname = "query", |
103 | System.err.println("file already exists, not overwriting"); | 64 | action = ArgumentAction.SET, |
104 | return; | 65 | description = "query election result") |
105 | } | 66 | boolean query = false; |
106 | InputStream is = getClass().getResourceAsStream("template.espec"); | ||
107 | OutputSupplier<FileOutputStream> oss = Files.newOutputStreamSupplier(f); | ||
108 | try { | ||
109 | ByteStreams.copy(is, oss); | ||
110 | } catch (IOException e) { | ||
111 | System.err.println("could not copy template file: " + e.getMessage()); | ||
112 | } | ||
113 | } | ||
114 | 67 | ||
115 | private void runIssue(String ballotFilename, String privKeyFilename) { | 68 | @Argument( |
116 | File bf = new File(ballotFilename); | 69 | shortname = "s", |
117 | if (!bf.exists()) { | 70 | longname = "submit", |
118 | System.err.println("ballot file does not exist"); | 71 | action = ArgumentAction.SET, |
119 | return; | 72 | description = "submit the vote to the authorities") |
120 | } | 73 | boolean submit = false; |
121 | File kf = new File(privKeyFilename); | ||
122 | if (!kf.exists()) { | ||
123 | System.err.println("private-key file does not exist"); | ||
124 | return; | ||
125 | } | ||
126 | Ballot b = new Ballot(ballotFilename); | ||
127 | EcdsaPrivateKey privateKey = EcdsaPrivateKey.fromFile(privKeyFilename); | ||
128 | if (privateKey == null) { | ||
129 | System.err.println("keyfile invalid"); | ||
130 | return; | ||
131 | } | ||
132 | b.issue(privateKey); | ||
133 | try { | ||
134 | Files.write(b.serialize(), bf, Charsets.UTF_8); | ||
135 | } catch (IOException e) { | ||
136 | System.err.println("could not write ballot file: " + e.getMessage()); | ||
137 | } | ||
138 | } | ||
139 | 74 | ||
140 | public void runSelect(String ballotFilename, String privKeyFilename, String choice) { | 75 | @Argument( |
141 | File bf = new File(ballotFilename); | 76 | shortname = "r", |
142 | if (!bf.exists()) { | 77 | longname = "register", |
143 | System.err.println("ballot file does not exist"); | 78 | action = ArgumentAction.SET, |
144 | return; | 79 | description = "register an election with the authorities") |
145 | } | 80 | boolean register = false; |
146 | File kf = new File(privKeyFilename); | 81 | |
147 | if (!kf.exists()) { | 82 | @Argument( |
148 | System.err.println("private-key file does not exist"); | 83 | shortname = "i", |
149 | return; | 84 | longname = "issue", |
150 | } | 85 | action = ArgumentAction.SET, |
151 | EcdsaPrivateKey privateKey = EcdsaPrivateKey.fromFile(privKeyFilename); | 86 | description = "sign the ballot as issuer (use with -e)") |
152 | if (privateKey == null) { | 87 | boolean issue = false; |
153 | System.err.println("keyfile invalid"); | ||
154 | return; | ||
155 | } | ||
156 | Ballot ballot = new Ballot(ballotFilename); | ||
157 | ballot.encodeChoice(choice, privateKey); | ||
158 | try { | ||
159 | Files.write(ballot.serialize(), bf, Charsets.UTF_8); | ||
160 | } catch (IOException e) { | ||
161 | System.err.println("can not write to ballot file"); | ||
162 | return; | ||
163 | } | ||
164 | System.out.println("vote written to ballot file"); | ||
165 | } | ||
166 | 88 | ||
89 | @Argument( | ||
90 | shortname = "x", | ||
91 | longname = "select", | ||
92 | action = ArgumentAction.STORE_STRING, | ||
93 | argumentName = "CHOICE", | ||
94 | description = "select and encrypt a vote option (use with -e)") | ||
95 | String select = null; | ||
96 | |||
97 | @Argument( | ||
98 | shortname = "V", | ||
99 | longname = "verify", | ||
100 | action = ArgumentAction.SET, | ||
101 | description = "verify signatures in the ballot and show information") | ||
102 | boolean verify = false; | ||
103 | |||
104 | @Argument( | ||
105 | shortname = "g", | ||
106 | longname = "group", | ||
107 | action = ArgumentAction.STORE_STRING, | ||
108 | description = "incorporate the group cert into the ballot") | ||
109 | String groupCertFile = null; | ||
110 | |||
111 | |||
112 | @Argument( | ||
113 | shortname = "t", | ||
114 | longname = "template", | ||
115 | action = ArgumentAction.SET, | ||
116 | description = "write a template ballot to the give ballot file") | ||
117 | boolean template = false; | ||
118 | |||
119 | private Identity.Ego ego; | ||
120 | |||
121 | private Ballot ballot; | ||
122 | private String ballotFilename; | ||
123 | |||
124 | private Mesh mesh; | ||
125 | private Mesh.Tunnel tunnel; | ||
126 | |||
127 | private PeerIdentity currentAuthority; | ||
128 | |||
129 | /** | ||
130 | * Are we finished with communicating over the mesh tunnel and don't need to worry about | ||
131 | * disconnection? | ||
132 | */ | ||
133 | private boolean tunnelCommunicationFinished; | ||
134 | |||
135 | public class BallotTunnelEndHandler implements TunnelEndHandler { | ||
136 | @Override | ||
137 | public void onTunnelEnd(Mesh.Tunnel tunnel) { | ||
138 | // FIXME | ||
139 | } | ||
140 | } | ||
141 | |||
142 | public class BallotRegisterReceiver extends MeshRunabout { | ||
143 | public void visit(BallotRegisterSuccessMessage m) { | ||
144 | System.out.println("ballot successfully registered"); | ||
145 | ballot.addRegistrationSignature(currentAuthority, m.registrationSignature); | ||
146 | writeBallot(); | ||
147 | tunnel.destroy(); | ||
148 | mesh.destroy(); | ||
149 | } | ||
150 | |||
151 | public void visit(BallotRegisterFailureMessage m) { | ||
152 | System.out.println("registering failed: " + m.reason); | ||
153 | tunnel.destroy(); | ||
154 | mesh.destroy(); | ||
155 | } | ||
156 | |||
157 | } | ||
167 | 158 | ||
168 | public void runVerify(String ballotFilename) { | 159 | public class QueryReceiver extends MeshRunabout { |
169 | File bf = new File(ballotFilename); | 160 | public void visit(QueryResponseMessage m) { |
170 | if (!bf.exists()) { | 161 | if (m.results.length != ballot.choices.size()) { |
171 | System.err.println("ballot file does not exist"); | 162 | System.out.println("failure to query result: malformed response"); |
172 | return; | 163 | } else { |
164 | System.out.println("got results:"); | ||
165 | for (int i = 0; i < m.results.length; i++) { | ||
166 | System.out.println("'" + ballot.choices.get(i) + "': " + m.results[i]); | ||
173 | } | 167 | } |
174 | Ballot ballot = new Ballot(ballotFilename); | ||
175 | System.out.print(ballot.describe()); | ||
176 | } | 168 | } |
177 | 169 | ||
178 | @Override | 170 | tunnel.destroy(); |
179 | public void run() { | 171 | mesh.destroy(); |
180 | if (template) { | 172 | } |
181 | if (this.unprocessedArgs.length != 1) { | ||
182 | System.err.println("-t/--template requires exactly one positional argument"); | ||
183 | return; | ||
184 | } | ||
185 | runTemplate(unprocessedArgs[0]); | ||
186 | return; | ||
187 | } | ||
188 | 173 | ||
189 | if (register) { | 174 | public void visit(QueryFailureMessage m) { |
190 | if (this.unprocessedArgs.length != 1) { | 175 | System.out.println("failure to query result: authority refused"); |
191 | System.err.println("-r/--register requires exactly one positional argument"); | 176 | tunnel.destroy(); |
192 | return; | 177 | mesh.destroy(); |
193 | } | 178 | } |
194 | RegisterCommand c = new RegisterCommand(getConfiguration(), unprocessedArgs[0]); | 179 | } |
195 | c.run(); | 180 | |
196 | return; | 181 | public class SubmitReceiver extends MeshRunabout { |
197 | } | 182 | |
198 | if (issue) { | 183 | public void visit(SubmitSuccessMessage m) { |
199 | if (this.unprocessedArgs.length != 2) { | 184 | System.out.println("vote successfully submitted"); |
200 | System.err.println("-i/--issue requires exactly two positional arguments"); | 185 | ballot.addConfirmation(currentAuthority, m.confirmationSig); |
201 | return; | 186 | writeBallot(); |
202 | } | 187 | tunnel.destroy(); |
203 | runIssue(unprocessedArgs[0], unprocessedArgs[1]); | 188 | mesh.destroy(); |
204 | return; | 189 | } |
205 | } | 190 | |
206 | if (select != null) { | 191 | public void visit(SubmitFailureMessage m) { |
207 | if (this.unprocessedArgs.length != 2) { | 192 | System.out.println("vote not submitted: " + m.reason); |
208 | System.err.println("-x/--select requires exactly two positional argument"); | 193 | tunnel.destroy(); |
209 | return; | 194 | mesh.destroy(); |
210 | } | 195 | } |
211 | runSelect(unprocessedArgs[0], unprocessedArgs[1], select); | 196 | } |
212 | return; | 197 | |
213 | } | 198 | @Override |
214 | if (submit) { | 199 | protected String makeHelpText() { |
215 | if (this.unprocessedArgs.length != 1) { | 200 | return "gnunet-ballot [OPTIONS]... BALLOT\n" + |
216 | System.err.println("-s/--submit requires exactly one positional argument"); | 201 | "Create, modify and execute operation on ballots."; |
217 | return; | 202 | } |
218 | } | 203 | |
219 | SubmitCommand c = new SubmitCommand(getConfiguration(), unprocessedArgs[0]); | 204 | private void runTemplate() { |
220 | c.run(); | 205 | File f = new File(ballotFilename); |
221 | return; | 206 | if (f.exists()) { |
222 | } | 207 | System.err.println("file already exists, not overwriting"); |
223 | if (verify) { | 208 | return; |
224 | if (this.unprocessedArgs.length != 1) { | 209 | } |
225 | System.err.println("-V/--verify requires exactly one positional argument"); | 210 | InputStream is = getClass().getResourceAsStream("ballot-template.espec"); |
226 | return; | 211 | OutputSupplier<FileOutputStream> oss = Files.newOutputStreamSupplier(f); |
227 | } | 212 | try { |
228 | runVerify(unprocessedArgs[0]); | 213 | ByteStreams.copy(is, oss); |
229 | return; | 214 | } catch (IOException e) { |
215 | System.err.println("could not copy template file: " + e.getMessage()); | ||
216 | } | ||
217 | } | ||
218 | |||
219 | private void writeBallot() { | ||
220 | try { | ||
221 | Files.write(ballot.serialize(), new File(ballotFilename), Charsets.UTF_8); | ||
222 | } catch (IOException e) { | ||
223 | System.err.println("could not write ballot file: " + e.getMessage()); | ||
224 | } | ||
225 | } | ||
226 | |||
227 | void doCommands() { | ||
228 | if (null != groupCertFile) { | ||
229 | Configuration groupCertConfig = new Configuration(); | ||
230 | groupCertConfig.parse(groupCertFile); | ||
231 | GroupCert groupCert = GroupCert.fromGroupCertConfig(groupCertConfig); | ||
232 | ballot.encodeGroup(groupCert); | ||
233 | writeBallot(); | ||
234 | return; | ||
235 | } | ||
236 | if (register) { | ||
237 | List<PeerIdentity> remainingAuthorities = ballot.getRemainingRegisterAuthorities(); | ||
238 | if (remainingAuthorities.isEmpty()) { | ||
239 | System.err.println("all authorities already received the ballot"); | ||
240 | return; | ||
241 | } | ||
242 | Random r = new Random(); | ||
243 | currentAuthority = remainingAuthorities.get(r.nextInt(remainingAuthorities.size())); | ||
244 | System.out.println("registering ballot with authority " + currentAuthority.toString()); | ||
245 | mesh = new Mesh(getConfiguration(), new BallotTunnelEndHandler(), new BallotRegisterReceiver()); | ||
246 | tunnel = mesh.createTunnel(currentAuthority, TallyAuthorityDaemon.MESH_PORT, true, true, null); | ||
247 | BallotRegisterRequestMessage m = new BallotRegisterRequestMessage(); | ||
248 | CompressedConfig ccfg = new CompressedConfig(ballot.toConfiguration()); | ||
249 | m.compressedBallotConfig = ccfg.compressed_data; | ||
250 | tunnel.send(m); | ||
251 | return; | ||
252 | } | ||
253 | if (issue) { | ||
254 | if (null == ego) { | ||
255 | System.out.println("no ego given"); | ||
256 | return; | ||
257 | } | ||
258 | ballot.issue(ego.getPrivateKey()); | ||
259 | writeBallot(); | ||
260 | return; | ||
261 | } | ||
262 | if (select != null) { | ||
263 | if (null == ego) { | ||
264 | System.out.println("no ego given"); | ||
265 | return; | ||
266 | } | ||
267 | ballot.encodeChoice(select, ego.getPrivateKey()); | ||
268 | writeBallot(); | ||
269 | return; | ||
270 | } | ||
271 | if (submit) { | ||
272 | List<PeerIdentity> remainingAuthorities = ballot.getRemainingSubmitAuthorities(); | ||
273 | if (remainingAuthorities.isEmpty()) { | ||
274 | System.err.println("all authorities already received the ballot"); | ||
275 | return; | ||
276 | } | ||
277 | Random r = new Random(); | ||
278 | PeerIdentity authority = remainingAuthorities.get(r.nextInt(remainingAuthorities.size())); | ||
279 | System.out.println("submitting to authority" + authority.toString()); | ||
280 | currentAuthority = authority; | ||
281 | mesh = new Mesh(cfg, new BallotTunnelEndHandler(), new SubmitReceiver()); | ||
282 | tunnel = mesh.createTunnel(authority, TallyAuthorityDaemon.MESH_PORT, true, true, null); | ||
283 | SubmitMessage m = new SubmitMessage(); | ||
284 | if (ballot.voterPub == null) { | ||
285 | throw new InvalidBallotException("no voter in ballot"); | ||
286 | } | ||
287 | m.voterPub = ballot.voterPub; | ||
288 | if (ballot.groupCert == null) { | ||
289 | throw new InvalidBallotException("no group cert in ballot"); | ||
290 | } | ||
291 | m.groupCertExpiration = ballot.groupCert.getExpiration().asMessage(); | ||
292 | m.groupCert = ballot.groupCert.getSignature(); | ||
293 | m.ballotGuid = ballot.getBallotGuid(); | ||
294 | m.choiceId = ballot.choiceId; | ||
295 | tunnel.send(m); | ||
296 | return; | ||
297 | } | ||
298 | if (verify) { | ||
299 | System.out.print(ballot.describe()); | ||
300 | return; | ||
301 | } | ||
302 | if (query) { | ||
303 | List<PeerIdentity> remainingAuthorities = ballot.getAuthorities(); | ||
304 | if (remainingAuthorities.isEmpty()) { | ||
305 | System.err.println("no authorities available"); | ||
306 | return; | ||
307 | } | ||
308 | Random r = new Random(); | ||
309 | PeerIdentity authority = remainingAuthorities.get(r.nextInt(remainingAuthorities.size())); | ||
310 | System.out.println("querying authority" + authority.toString()); | ||
311 | mesh = new Mesh(cfg, new BallotTunnelEndHandler(), new QueryReceiver()); | ||
312 | tunnel = mesh.createTunnel(authority, TallyAuthorityDaemon.MESH_PORT, true, true, null); | ||
313 | QueryMessage m = new QueryMessage(); | ||
314 | m.ballotGUID = ballot.getBallotGuid(); | ||
315 | tunnel.send(m); | ||
316 | return; | ||
317 | } | ||
318 | } | ||
319 | |||
320 | @Override | ||
321 | public void run() { | ||
322 | if (unprocessedArgs.length != 1) { | ||
323 | System.err.println("no ballot file specified"); | ||
324 | setReturnValue(1); | ||
325 | return; | ||
326 | } | ||
327 | ballotFilename = unprocessedArgs[0]; | ||
328 | if (template) { | ||
329 | runTemplate(); | ||
330 | return; | ||
331 | } | ||
332 | // all other commands need a loaded ballot file | ||
333 | File bf = new File(ballotFilename); | ||
334 | if (!bf.exists()) { | ||
335 | System.err.println("ballot file does not exist"); | ||
336 | return; | ||
337 | } | ||
338 | ballot = new Ballot(ballotFilename); | ||
339 | if (null != egoName) { | ||
340 | Identity.lookup(getConfiguration(), egoName, new IdentityCallback() { | ||
341 | @Override | ||
342 | public void onEgo(Identity.Ego ego) { | ||
343 | BallotTool.this.ego = ego; | ||
344 | doCommands(); | ||
230 | } | 345 | } |
231 | if (query) { | 346 | @Override |
232 | if (this.unprocessedArgs.length != 1) { | 347 | public void onError(String errorMessage) { |
233 | System.err.println("-q/--query requires exactly one positional argument"); | 348 | System.err.println("can't retrieve ego: " + errorMessage); |
234 | return; | 349 | setReturnValue(1); |
235 | } | ||
236 | QueryCommand c = new QueryCommand(getConfiguration(), unprocessedArgs[0]); | ||
237 | c.run(); | ||
238 | return; | ||
239 | } | 350 | } |
240 | } | 351 | }); |
241 | }.start(); | 352 | } else { |
353 | doCommands(); | ||
354 | } | ||
355 | } | ||
242 | 356 | ||
357 | private BallotTool(String[] args) { | ||
358 | super(args); | ||
243 | } | 359 | } |
244 | 360 | ||
361 | public static void main(String args[]) { | ||
362 | Program tool = new BallotTool(args); | ||
363 | tool.start(); | ||
364 | } | ||
245 | } | 365 | } |