gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r29526 - in gnunet-java: . src/main/java/org/gnunet/consens


From: gnunet
Subject: [GNUnet-SVN] r29526 - in gnunet-java: . src/main/java/org/gnunet/consensus src/main/java/org/gnunet/mesh src/main/java/org/gnunet/requests src/main/java/org/gnunet/testbed src/main/java/org/gnunet/testbed/callbacks src/main/java/org/gnunet/testbed/messages src/main/java/org/gnunet/util src/main/java/org/gnunet/voting src/main/java/org/gnunet/voting/messages src/main/resources/org/gnunet/construct src/main/resources/org/gnunet/voting src/test/java/org/gnunet/testbed
Date: Wed, 25 Sep 2013 13:32:20 +0200

Author: dold
Date: 2013-09-25 13:32:19 +0200 (Wed, 25 Sep 2013)
New Revision: 29526

Added:
   
gnunet-java/src/main/java/org/gnunet/testbed/callbacks/PeerInformationCallback.java
   gnunet-java/src/main/java/org/gnunet/testbed/callbacks/ServiceAdapter.java
   
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerGetInformationMessage.java
   
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerInformationMessage.java
   gnunet-java/src/main/java/org/gnunet/voting/Ballot.java
   gnunet-java/src/main/java/org/gnunet/voting/InvalidBallotException.java
   gnunet-java/src/main/java/org/gnunet/voting/PermissionCommand.java
   gnunet-java/src/main/java/org/gnunet/voting/QueryCommand.java
   gnunet-java/src/main/java/org/gnunet/voting/RegisterCommand.java
   gnunet-java/src/main/java/org/gnunet/voting/SubmitCommand.java
   gnunet-java/src/main/java/org/gnunet/voting/TallyAuthorityDaemon.java
   gnunet-java/src/main/java/org/gnunet/voting/messages/QueryFailureMessage.java
   gnunet-java/src/main/java/org/gnunet/voting/messages/QueryMessage.java
   
gnunet-java/src/main/java/org/gnunet/voting/messages/QueryResponseMessage.java
Removed:
   gnunet-java/src/main/java/org/gnunet/testbed/Operation.java
   
gnunet-java/src/main/java/org/gnunet/testbed/messages/CreatePeerSuccessMessage.java
   
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerConfigurationInformationMessage.java
   gnunet-java/src/main/java/org/gnunet/voting/TallyAuthorityService.java
   gnunet-java/src/main/java/org/gnunet/voting/messages/QueryResultMessage.java
   
gnunet-java/src/main/java/org/gnunet/voting/messages/QueryResultResponseMessage.java
Modified:
   gnunet-java/ISSUES
   gnunet-java/src/main/java/org/gnunet/consensus/ConcludeCallback.java
   gnunet-java/src/main/java/org/gnunet/consensus/ConcludeDoneMessage.java
   gnunet-java/src/main/java/org/gnunet/consensus/ConcludeMessage.java
   gnunet-java/src/main/java/org/gnunet/consensus/Consensus.java
   gnunet-java/src/main/java/org/gnunet/consensus/ConsensusElement.java
   gnunet-java/src/main/java/org/gnunet/consensus/InsertDoneCallback.java
   gnunet-java/src/main/java/org/gnunet/consensus/InsertElementMessage.java
   gnunet-java/src/main/java/org/gnunet/consensus/NewElementCallback.java
   gnunet-java/src/main/java/org/gnunet/consensus/NewElementMessage.java
   gnunet-java/src/main/java/org/gnunet/mesh/Mesh.java
   gnunet-java/src/main/java/org/gnunet/mesh/MeshRunabout.java
   gnunet-java/src/main/java/org/gnunet/mesh/TunnelEndHandler.java
   gnunet-java/src/main/java/org/gnunet/requests/MatchingRequestContainer.java
   gnunet-java/src/main/java/org/gnunet/testbed/CompressedConfig.java
   gnunet-java/src/main/java/org/gnunet/testbed/Controller.java
   
gnunet-java/src/main/java/org/gnunet/testbed/callbacks/OperationCompletionCallback.java
   
gnunet-java/src/main/java/org/gnunet/testbed/messages/ConnectionEventMessage.java
   
gnunet-java/src/main/java/org/gnunet/testbed/messages/ControllerInitMessage.java
   
gnunet-java/src/main/java/org/gnunet/testbed/messages/GenericOperationSuccessMessage.java
   
gnunet-java/src/main/java/org/gnunet/testbed/messages/ManagePeerServiceMessage.java
   
gnunet-java/src/main/java/org/gnunet/testbed/messages/OperationFailEventMessage.java
   
gnunet-java/src/main/java/org/gnunet/testbed/messages/OverlayConnectMessage.java
   
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerCreateSuccessMessage.java
   gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerDestroyMessage.java
   gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerEventMessage.java
   
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerReconfigureMessage.java
   gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerStartMessage.java
   gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerStopMessage.java
   gnunet-java/src/main/java/org/gnunet/util/AbsoluteTime.java
   gnunet-java/src/main/java/org/gnunet/util/CryptoECC.java
   gnunet-java/src/main/java/org/gnunet/util/HashCode.java
   gnunet-java/src/main/java/org/gnunet/util/PeerIdentity.java
   gnunet-java/src/main/java/org/gnunet/util/Program.java
   gnunet-java/src/main/java/org/gnunet/util/RunaboutMessageReceiver.java
   gnunet-java/src/main/java/org/gnunet/util/Strings.java
   gnunet-java/src/main/java/org/gnunet/voting/BallotTool.java
   
gnunet-java/src/main/java/org/gnunet/voting/messages/BallotRegisterRequestMessage.java
   
gnunet-java/src/main/java/org/gnunet/voting/messages/BallotRegisterRespondMessage.java
   
gnunet-java/src/main/java/org/gnunet/voting/messages/CertificateRequestMessage.java
   
gnunet-java/src/main/java/org/gnunet/voting/messages/CertificateResponseMessage.java
   gnunet-java/src/main/java/org/gnunet/voting/messages/SubmitMessage.java
   
gnunet-java/src/main/java/org/gnunet/voting/messages/SubmitResponseMessage.java
   gnunet-java/src/main/resources/org/gnunet/construct/MsgMap.txt
   gnunet-java/src/main/resources/org/gnunet/voting/template.espec
   gnunet-java/src/test/java/org/gnunet/testbed/TestbedTest.java
Log:
- testbed: create/destroy, start/stop, getInformation, connectOverlay 
implementation and tests
- voting: ballot serialization/deserialization, unencrypted tally authority 
implementation,
  ballot tool works for register/vote/query
- issues


Modified: gnunet-java/ISSUES
===================================================================
--- gnunet-java/ISSUES  2013-09-25 09:24:05 UTC (rev 29525)
+++ gnunet-java/ISSUES  2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,78 +1,71 @@
-* I believe that your remark in our last meeting (Ed25519 secret keys must be 
mod 2^255-19)
-  is wrong, sk only must be a bitstring and is *not* directly used to generate 
the pubkey.
-* how is the libgrypt ecc going?
-* efficient ed25519 in java?
- * seems a bit difficult due to the lack of 128-bit multiplication result
-   and no instruction level parallelism
+== testbed ==
 
+padding: PACKED / NETWORK_STRUCT don't really pack some misaligned structs:
+  struct GNUNET_TESTBED_PeerConfigurationInformationMessage
+is misaligned anyway, *but* sizeof the struct is 84, when it should be 82!
 
-* org.gnunet.util.Helper works now
-* the scheduler had to be updated:
- * the scheduler now is (or at least should be) thread safe
- * the simplest solution was to use selector.wakeup()
- * before calling select, the lock is released. this would make it possible for
-   a deadlock to occur, but Java's semantics (wakeup causes next select to 
return
-   instantly) make this impossible
-* java selectors and threads are not trivial
- * e.g. before registering a channel with a new selector in a thread,
-   one has to call wakeup on the selector
+see https://gnunet.org/bugs/view.php?id=3043
 
-=> in retrospect it might just have been easier to use a java.nio.channels.Pipe
+I managed to break testbed (this is a GNUNET_assert, no GNUNET_break)
+  Sep 24 20:06:14-146897 testbed-18586 ERROR Assertion failed at 
testing.c:1548.
+but this only occurs *after* the shutdown and when trying to reconfigure a 
peer!
 
-* java and exit values
- * test runners don't seem to like System.exit(n)
- * how should exit values be handled?
-  * either tests don't use org.gnunet.util.Program, and setExitValue remains
-  * or we don't care about the exit value
-  * or Program.start returns the exit values and the respective main() has to
-    call System.exit() with the exit value
+OPTIONS/PREFIX does not work with testbed helper
 
-* java can now start/stop controller processes, and create peers
- * start/stop etc. is next, should be trivial now ...
-* testbed now has messages and callbacks sub-package. is that ok?
+what works (and is tested) now in org.gnunet.testbed:
+ * creating and destroying peers
+ * starting and stopping peers
+ * getting peer id / configuration
+ * overlay-connecting two peers
+other stuff:
+ * does gnunet-java need barriers?
+ * what's the state of the overlay implementation
+ * does service_connect do anything other than retrieve the configuration?
+  * primary purpose is to be able to have opqueue management, right?
+ * why do we have manage service when we have arm?
+  * someone has to start arm, I suppose?
 
-* key storage in GNUnet
- * what is the best pratice w.r.t. storing private keys? what about encryption?
+== crypto ==
+two types of public keys, do I understand the reasons correctly:
+ * NaCl compatibility
+ * fastest representation for each type of operation
 
-* for specifying absolute time: how are timezones handled?
-* configuration: does GNUnet preserve the order or fields?
- * important for signing, as sometimes the serialization of certain sections
-   is signed (does that make sense?)
+why is there no way to convert from signing key -> encryption key?
+ * other way around is not possible
 
-* I think it's best to have most of the voting state centrally in the "ballot"
- * as ballot means both "Wahlzettel" and "Wahlgang/Wahl/Abstimmung", the term 
is fitting,
-   and better than "election data" or "election specification"
- * the ballot contains all PUBLIC state relevant to an issuer or voter.
-  * secret state is always passed per command line
- * for voters, the ballot contains the encrypted "choice" for the election
-   including zero knowledge proofs, ...
-  * this is important, as we may want to send the ballot to different 
authorities,
-    but not in one session. nevertheless, the nonces must be the same 
(otherwise
-    the voter would be disqualified).
- * ... as well as the authorities' submission voucher 
 
-=> as all these tasks create, use or manipulate a ballot file, the voting call 
and
-   voting tool should be merged.
+== voting ==
 
-=> I like the name 'gnunet-ballot' for the tool
+what now works:
+ * creating a ballot and registering it
+ * selecting a choice in the ballot and submitting it
+  * vote + 512-bit ballot GUID
+ * querying the results (also with the GUID)
 
-create templace (copy it from the jar file):
-  gnunet-ballot -t my_ballot_file
-sign (as issuer) and register election with authorities:
-  gnunet-ballot -r my_ballot_file my_issuer_privkey_file
-get permission to vote with the given identity:
-  gnunet-ballot -p my_ballot_file my_voter_privkey_file
-select choice and store encrypted vote in ballot file:
-  gnunet-ballot -x pancakes my_ballot_file my_voter_priv_keyfile
-submit ballot to authorities and record vouchers (anyone can do that!):
-  gnunet-ballot -s my_ballot_file
-query election (anybody can do that)
-  gnunet-ballot -q my_ballot_file
-verify ballot (show what's the state, check signatures, ...)
-  gnunet-ballot -V my_ballot_file
+next steps??
 
+what should be stored in the ballot about authorities: peer identities or peer 
public keys?
+ * pubkey makes more sense, but I have the feeling that GNUnet prefers to use
+   peer IDs whenever possible
 
-should we always send the whole (compressed) ballot,
-or only a hash and the relevant sections?
+docs of identity service say:
+  For giving names to other users and manage their public keys securely, we 
use GNS.
+In what way does GNS manage public keys? Does GNS in any way relate peer 
identities to public keys?
 
+persistent storage of tallies?
 
+now that the pubkey is smaller, are there reasons for still having
+a separate peer identity?
+ * except for that it would be a lot of work to change now
+
+the command line took looks a bit complicated now, any suggestions?
+
+asking permission to vote should not require the private key, right?
+ * how do we do this interface-wise? pubkey on the command line?
+
+
+== general java stuff ==
+what's your opinion on option types: should they be used more in gnunet-java?
+
+We have a lot of callbacks in java that all do almost the same. Should we have 
a generic callback type?
+

Modified: gnunet-java/src/main/java/org/gnunet/consensus/ConcludeCallback.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/ConcludeCallback.java        
2013-09-25 09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/consensus/ConcludeCallback.java        
2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,3 +1,23 @@
+/*
+ This file is part of GNUnet.
+ (C) 2013 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING.  If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
 package org.gnunet.consensus;
 
 public interface ConcludeCallback {

Modified: 
gnunet-java/src/main/java/org/gnunet/consensus/ConcludeDoneMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/ConcludeDoneMessage.java     
2013-09-25 09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/consensus/ConcludeDoneMessage.java     
2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,3 +1,23 @@
+/*
+ This file is part of GNUnet.
+ (C) 2013 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING.  If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
 package org.gnunet.consensus;
 
 

Modified: gnunet-java/src/main/java/org/gnunet/consensus/ConcludeMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/ConcludeMessage.java 
2013-09-25 09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/consensus/ConcludeMessage.java 
2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,3 +1,23 @@
+/*
+ This file is part of GNUnet.
+ (C) 2013 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING.  If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
 package org.gnunet.consensus;
 
 import org.gnunet.construct.FillWith;

Modified: gnunet-java/src/main/java/org/gnunet/consensus/Consensus.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/Consensus.java       
2013-09-25 09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/consensus/Consensus.java       
2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,3 +1,23 @@
+/*
+ This file is part of GNUnet.
+ (C) 2013 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING.  If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
 package org.gnunet.consensus;
 
 import org.gnunet.mq.Envelope;

Modified: gnunet-java/src/main/java/org/gnunet/consensus/ConsensusElement.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/ConsensusElement.java        
2013-09-25 09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/consensus/ConsensusElement.java        
2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,3 +1,23 @@
+/*
+ This file is part of GNUnet.
+ (C) 2013 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING.  If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
 package org.gnunet.consensus;
 
 

Modified: gnunet-java/src/main/java/org/gnunet/consensus/InsertDoneCallback.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/InsertDoneCallback.java      
2013-09-25 09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/consensus/InsertDoneCallback.java      
2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,3 +1,23 @@
+/*
+ This file is part of GNUnet.
+ (C) 2013 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING.  If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
 package org.gnunet.consensus;
 
 public interface InsertDoneCallback {

Modified: 
gnunet-java/src/main/java/org/gnunet/consensus/InsertElementMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/InsertElementMessage.java    
2013-09-25 09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/consensus/InsertElementMessage.java    
2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,3 +1,23 @@
+/*
+ This file is part of GNUnet.
+ (C) 2013 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING.  If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
 package org.gnunet.consensus;
 
 import org.gnunet.construct.FillWith;

Modified: gnunet-java/src/main/java/org/gnunet/consensus/NewElementCallback.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/NewElementCallback.java      
2013-09-25 09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/consensus/NewElementCallback.java      
2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,3 +1,23 @@
+/*
+ This file is part of GNUnet.
+ (C) 2013 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING.  If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
 package org.gnunet.consensus;
 
 public interface NewElementCallback {

Modified: gnunet-java/src/main/java/org/gnunet/consensus/NewElementMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/NewElementMessage.java       
2013-09-25 09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/consensus/NewElementMessage.java       
2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,3 +1,23 @@
+/*
+ This file is part of GNUnet.
+ (C) 2013 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING.  If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
 package org.gnunet.consensus;
 
 import org.gnunet.construct.*;

Modified: gnunet-java/src/main/java/org/gnunet/mesh/Mesh.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/mesh/Mesh.java 2013-09-25 09:24:05 UTC 
(rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/mesh/Mesh.java 2013-09-25 11:32:19 UTC 
(rev 29526)
@@ -111,16 +111,17 @@
     public class Tunnel<T> extends MessageQueue {
         private T context;
         private final int opt;
-        public final PeerIdentity peer;
-        public final int port;
+        final PeerIdentity peer;
+        final int port;
         protected long tunnelId;
-        private boolean receive_done_expected = false;
-        int ack_count = 0;
+        private boolean receiveDoneExpected = false;
+        int ackCount = 0;
+        boolean destroyedByService;
 
         /**
          * Canceler for the currently submitted envelope.
          */
-        public Cancelable envelopeCanceler;
+        private Cancelable envelopeCanceler;
 
         /**
          * Create a new tunnel (we're initiator and will be allowed to 
add/remove peers
@@ -175,26 +176,28 @@
         }
 
         public void receiveDone() {
-            if (!receive_done_expected)
+            if (!receiveDoneExpected)
                 throw new AssertionError("unexpected call to receiveDone");
             LocalAckMessage am = new LocalAckMessage();
             am.tid = tunnelId;
             client.send(am);
-            receive_done_expected = false;
+            receiveDoneExpected = false;
         }
 
         public void destroy() {
-            TunnelDestroyMessage m = new TunnelDestroyMessage();
-            m.tunnel_id = tunnelId;
-            m.reserved = new byte[32];
-            client.send(m);
-            tunnelMap.remove(m.tunnel_id);
+            if (!destroyedByService) {
+                TunnelDestroyMessage m = new TunnelDestroyMessage();
+                m.tunnel_id = tunnelId;
+                m.reserved = new byte[32];
+                client.send(m);
+            }
+            tunnelMap.remove(tunnelId);
         }
 
         @Override
         protected void submit(Envelope ev) {
             logger.debug("submitting data message on tunnel {}", tunnelId);
-            if (ack_count <= 0)
+            if (ackCount <= 0)
                 throw new AssertionError();
             DataMessage m = new DataMessage();
             m.payload = Construct.toBinary(GnunetMessage.fromBody(ev.message));
@@ -208,7 +211,7 @@
             });
             client.send(mesh_ev);
             envelopeCanceler = mesh_ev;
-            ack_count -= 1;
+            ackCount -= 1;
         }
 
         @Override
@@ -227,10 +230,10 @@
             context = newContext;
         }
 
-        public void handleAck() {
-            ack_count++;
+        void handleAck() {
+            ackCount++;
             logger.debug("got ack for tunnel id " + tunnelId);
-            if (ack_count == 1) {
+            if (ackCount == 1) {
                 reportReadyForSubmit();
             }
         }
@@ -250,10 +253,12 @@
             Tunnel t = tunnelMap.get(m.tid);
             if (t != null)
             {
-                if (t.receive_done_expected)
+                if (t.receiveDoneExpected)
                     logger.warn("got unexpected message from service");
-                t.receive_done_expected = true;
+                t.receiveDoneExpected = true;
+                messageReceiver.setSender(t);
                 messageReceiver.visitAppropriate(Construct.parseAs(m.payload, 
GnunetMessage.class).body);
+                messageReceiver.setSender(null);
             }
         }
 
@@ -269,17 +274,21 @@
 
         public void visit(TunnelDestroyMessage m) {
             Tunnel t = tunnelMap.get(m.tunnel_id);
-            if (t == null) {
+            if (null == t) {
                 logger.warn("server got confused with tunnel IDs on destroy, 
ignoring message");
                 return;
             }
+            t.destroyedByService = true;
             t.destroy();
-            tunnelEndHandler.onTunnelEnd(t);
+            if (null != tunnelEndHandler) {
+                tunnelEndHandler.onTunnelEnd(t);
+            }
         }
 
         @Override
         public void handleError() {
-            if (tunnelEndHandler != null) {
+            logger.warn("lost connection to mesh service, reconnecting");
+            if (null != tunnelEndHandler) {
                 for (Tunnel t : tunnelMap.values()) {
                     tunnelEndHandler.onTunnelEnd(t);
                 }
@@ -293,13 +302,15 @@
     }
 
     /**
-     * Connect to the mesh service.
+     * Connect to the mesh service, listening to the given ports.
      *
      * @param cfg                  configuration to use
      * @param inboundTunnelHandler function called when an *inbound* tunnel is 
created
      * @param tunnelEndHandler     function called when an *inbound* tunnel is 
destroyed by the
      *                             remote peer, it is *not* called if 
Tunnel.destroy
      *                             is called on the tunnel
+     * @param messageReceiver runabout for messages we are interested in
+     * @param ports ports to listen on
      */
     public Mesh(Configuration cfg, InboundTunnelHandler inboundTunnelHandler,
                 TunnelEndHandler tunnelEndHandler, MeshRunabout 
messageReceiver, int... ports) {
@@ -317,6 +328,18 @@
         client.send(ccm);
     }
 
+    /**
+     * Connect to the mesh service.
+     *
+     * @param cfg                  configuration to use
+     * @param tunnelEndHandler     function called when an *inbound* tunnel is 
destroyed by the
+     *                             remote peer, it is *not* called if 
Tunnel.destroy
+     *                             is called on the tunnel
+     */
+    public Mesh(Configuration cfg, TunnelEndHandler tunnelEndHandler, 
MeshRunabout messageReceiver) {
+        this(cfg, null, tunnelEndHandler, messageReceiver);
+    }
+
     public <T> Tunnel<T> createTunnel(PeerIdentity peer, int port, boolean 
nobuffer, boolean reliable, T initialContext) {
         return new Tunnel<T>(peer, port, nobuffer, reliable, initialContext);
     }

Modified: gnunet-java/src/main/java/org/gnunet/mesh/MeshRunabout.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/mesh/MeshRunabout.java 2013-09-25 
09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/mesh/MeshRunabout.java 2013-09-25 
11:32:19 UTC (rev 29526)
@@ -9,11 +9,11 @@
  * @author Florian Dold
  */
 public class MeshRunabout extends Runabout {
-    private PeerIdentity sender;
-    /* package private */ void setSender(PeerIdentity sender) {
+    private Mesh.Tunnel sender;
+    /* package private */ void setSender(Mesh.Tunnel sender) {
         this.sender = sender;
     }
-    public PeerIdentity getSender() {
+    public Mesh.Tunnel getSender() {
         return sender;
     }
 }

Modified: gnunet-java/src/main/java/org/gnunet/mesh/TunnelEndHandler.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/mesh/TunnelEndHandler.java     
2013-09-25 09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/mesh/TunnelEndHandler.java     
2013-09-25 11:32:19 UTC (rev 29526)
@@ -6,5 +6,12 @@
  * @author Florian Dold
  */
 public interface TunnelEndHandler {
+    /**
+     * Called once a tunnel has been destroyed.
+     * The given tunnel can not be used anymore, and is only provided
+     * to identify the tunnel that has been destroyed.
+     *
+     * @param tunnel tunnel that has been destroyed
+     */
     void onTunnelEnd(Mesh.Tunnel tunnel);
 }

Modified: 
gnunet-java/src/main/java/org/gnunet/requests/MatchingRequestContainer.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/requests/MatchingRequestContainer.java 
2013-09-25 09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/requests/MatchingRequestContainer.java 
2013-09-25 11:32:19 UTC (rev 29526)
@@ -7,6 +7,9 @@
 import java.util.Map;
 
 
+/**
+ * Container for requests that are responded to with a matching request 
identification
+ */
 public class MatchingRequestContainer<K, T extends RequestContainer.Request> 
extends RequestContainer {
     private Map<K,T> requests = Maps.newHashMap();
     private final MessageQueue mq;
@@ -48,4 +51,15 @@
     public T getRequest(K key) {
         return requests.get(key);
     }
+
+    /**
+     * Retrieve the request matching the given key, and remove it.
+     * Return null if there is no matching request.
+     *
+     * @param key key to look for
+     * @return request, or null
+     */
+    public T pollRequest(K key) {
+        return requests.remove(key);
+    }
 }

Modified: gnunet-java/src/main/java/org/gnunet/testbed/CompressedConfig.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testbed/CompressedConfig.java  
2013-09-25 09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/testbed/CompressedConfig.java  
2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,3 +1,22 @@
+/*
+     This file is part of GNUnet.
+     (C) 2009,2013 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+ */
 package org.gnunet.testbed;
 
 import com.google.common.base.Charsets;

Modified: gnunet-java/src/main/java/org/gnunet/testbed/Controller.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testbed/Controller.java        
2013-09-25 09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/testbed/Controller.java        
2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,3 +1,22 @@
+/*
+     This file is part of GNUnet.
+     (C) 2009,2013 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+ */
 package org.gnunet.testbed;
 
 import org.gnunet.mq.Envelope;
@@ -3,9 +22,6 @@
 import org.gnunet.requests.MatchingRequestContainer;
 import org.gnunet.requests.RequestContainer;
-import org.gnunet.requests.SequentialRequestContainer;
 import org.gnunet.testbed.callbacks.*;
-import org.gnunet.testbed.messages.ControllerInitMessage;
-import org.gnunet.testbed.messages.PeerCreateMessage;
-import org.gnunet.testbed.messages.PeerCreateSuccessMessage;
+import org.gnunet.testbed.messages.*;
 import org.gnunet.util.Cancelable;
 import org.gnunet.util.Client;
@@ -25,16 +41,32 @@
  * links are established via TCP/IP on the controller's service port.
  */
 public class Controller {
-private static final Logger logger = LoggerFactory
+    private static final Logger logger = LoggerFactory
         .getLogger(Controller.class);
 
-    public static int ET_PEER_START = 0;
-    public static int ET_PEER_STOP = 1;
-    public static int ET_CONNECT = 2;
-    public static int ET_DISCONNECT = 3;
-    public static int ET_OPERATION_FINISHED = 4;
+    public static class EventTypes {
+       /**
+        * A peer has been started.
+        */
+        public static final int PEER_START = 0;
+        /**
+         * A peer has been stopped.
+         */
+        public static final int PEER_STOP = 1;
+        /**
+         * A connection between two peers has been established.
+         */
+        public static final int PEERS_CONNECT = 2;
+        /**
+         * A connection between two peers has been torn down.
+         */
+        public static final int PEERS_DISCONNECT = 3;
+        /**
+         * An operation has finished.
+         */
+        public static final int OPERATION_FINISHED = 4;
+    }
 
-
     /**
      * Client connecting to the testbed service.
      */
@@ -45,26 +77,42 @@
      */
     private Host host;
 
-    private int operationCounter;
+    private int operationCounter = 1;
     private int peerCounter;
 
     /**
-     * Request queue (called operation queue in the GNUnet C implementation)
+     * Request queue (akin to operation queue(s) in the GNUnet C 
implementation).
      */
-    private MatchingRequestContainer<Long,RequestContainer.Request> requests;
+    private MatchingRequestContainer<Long,OperationRequest> requests;
 
+    abstract class OperationRequest extends RequestContainer.Request {
+        protected final long operationId;
+        public OperationRequest() {
+            operationId = (((long) host.id) << 32) | (long) operationCounter++;
+        }
+    }
 
-    class PeerCreateRequest extends RequestContainer.Request {
-        private final Host host;
-        private final Configuration cfg;
-        private final PeerCreateCallback cb;
-        public long op_id;
+    abstract class GenericOperationRequest extends OperationRequest {
+        final protected OperationCompletionCallback cb;
+        public GenericOperationRequest(OperationCompletionCallback cb) {
+            this.cb = cb;
+        }
+        void onSuccess() {
+            cb.onCompletion();
+        }
+    }
 
+    class PeerCreateRequest extends OperationRequest {
+        final Host host;
+        final Configuration cfg;
+        final PeerCreateCallback cb;
+        final int peer_id;
+
         public PeerCreateRequest(Host host, Configuration cfg, 
PeerCreateCallback cb) {
             this.host = host;
             this.cfg = cfg;
             this.cb = cb;
-            this.op_id = getNextOpId();
+            this.peer_id = peerCounter++;
         }
 
         @Override
@@ -72,60 +120,230 @@
             CompressedConfig ccfg = new CompressedConfig(cfg);
             PeerCreateMessage m = new PeerCreateMessage();
             m.host_id = host.id;
-            m.operation_id = op_id;
-            m.peer_id = peerCounter++;
+            m.operation_id = operationId;
+            m.peer_id = peer_id;
             m.compressed_config = ccfg.compressed_data;
             m.config_size = ccfg.getUncompressedSize();
+            System.out.println("create request with opid " + m.operation_id);
             return new Envelope(m);
         }
     }
 
-    class PeerStartRequest extends RequestContainer.Request {
+    class PeerDestroyRequest extends GenericOperationRequest {
+        final int peerId;
+        public PeerDestroyRequest(Peer p, OperationCompletionCallback cb) {
+            super(cb);
+            peerId = p.peerId;
+        }
 
         @Override
         public Envelope assembleRequest() {
-            return null;  //To change body of implemented methods use File | 
Settings | File Templates.
+            PeerDestroyMessage m = new PeerDestroyMessage();
+            m.operation_id = operationId;
+            m.peer_id = peerId;
+
+            System.out.println("destroy request with opid " + m.operation_id);
+            return new Envelope(m);
         }
     }
 
-    class PeerStopRequest extends RequestContainer.Request {
+    class PeerInformationRequest extends OperationRequest {
+        final int peerId;
+        final PeerInformationCallback cb;
+        public PeerInformationRequest(Peer peer, PeerInformationCallback cb) {
+            peerId = peer.peerId;
+            this.cb = cb;
+        }
 
         @Override
         public Envelope assembleRequest() {
-            return null;  //To change body of implemented methods use File | 
Settings | File Templates.
+            PeerGetInformationMessage m = new PeerGetInformationMessage();
+            m.operationId = operationId;
+            m.peerId = peerId;
+            return new Envelope(m);
         }
     }
 
-    class ConnectOverlayRequest extends RequestContainer.Request {
+    class PeerUpdateConfigurationRequest extends GenericOperationRequest {
+        final int peerId;
+        final Configuration cfg;
+        public PeerUpdateConfigurationRequest(Peer peer, 
OperationCompletionCallback cb, Configuration cfg) {
+            super(cb);
+            peerId = peer.peerId;
+            this.cfg = cfg;
+        }
+
         @Override
         public Envelope assembleRequest() {
-            return null;  //To change body of implemented methods use File | 
Settings | File Templates.
+            PeerReconfigureMessage m = new PeerReconfigureMessage();
+            m.operationId = operationId;
+            m.peerId = peerId;
+            CompressedConfig ccfg = new CompressedConfig(cfg);
+            m.uncompressedConfigSize = ccfg.getUncompressedSize();
+            m.compressedConfig = ccfg.compressed_data;
+
+            System.out.println("compressed config size " 
+m.compressedConfig.length);
+            return new Envelope(m);
         }
     }
 
+    class PeerManageServiceRequest extends OperationRequest {
+        final Peer peer;
+        final boolean start;
+        final String serviceName;
+        public PeerManageServiceRequest(Peer peer, String serviceName, boolean 
start) {
+            this.peer = peer;
+            this.start = start;
+            this.serviceName = serviceName;
+        }
+        @Override
+        public Envelope assembleRequest() {
+            ManagePeerServiceMessage m = new ManagePeerServiceMessage();
+            m.operationId = operationId;
+            m.peerId = peer.peerId;
+            m.serviceName = serviceName;
+            m.start = start;
+            return new Envelope(m);
+        }
+    }
 
+    class PeerStartRequest extends OperationRequest {
+        final Peer peer;
+        final PeerChurnCallback peerChurnCallback;
+
+        public PeerStartRequest(Peer peer, PeerChurnCallback 
peerChurnCallback) {
+            this.peer = peer;
+            this.peerChurnCallback = peerChurnCallback;
+        }
+
+        @Override
+        public Envelope assembleRequest() {
+            PeerStartMessage m = new PeerStartMessage();
+            m.operation_id = operationId;
+            m.peer_id = peer.peerId;
+            return new Envelope(m);
+        }
+    }
+
+    class PeerStopRequest extends OperationRequest {
+        final Peer peer;
+        final PeerChurnCallback peerChurnCallback;
+
+        public PeerStopRequest(Peer peer, PeerChurnCallback peerChurnCallback) 
{
+            this.peer = peer;
+            this.peerChurnCallback = peerChurnCallback;
+        }
+
+        @Override
+        public Envelope assembleRequest() {
+            PeerStopMessage m = new PeerStopMessage();
+            m.operation_id = operationId;
+            m.peer_id = peer.peerId;
+            return new Envelope(m);
+        }
+    }
+
+    class PeerConnectOverlayRequest extends OperationRequest {
+        final Peer peer1;
+        final Peer peer2;
+        final OperationCompletionCallback cb;
+        public PeerConnectOverlayRequest(Peer peer1, Peer peer2, 
OperationCompletionCallback cb) {
+            this.peer1 = peer1;
+            this.peer2 = peer2;
+            this.cb = cb;
+        }
+        @Override
+        public Envelope assembleRequest() {
+            OverlayConnectMessage m = new OverlayConnectMessage();
+            m.operationId = operationId;
+            m.peer1 = peer1.peerId;
+            m.peer2 = peer2.peerId;
+            m.hostOfPeer2 = peer2.getHost().id;
+            return new Envelope(m);
+        }
+    }
+
+
     public class ControllerMessageReceiver extends RunaboutMessageReceiver {
+        public void visit(PeerEventMessage m) {
+            RequestContainer.Request r = requests.getRequest(m.operationId);
+            if (null == r) {
+                logger.error("no matching peer event request for op id %s", 
m.operationId);
+                return;
+            }
+            if (r instanceof PeerStartRequest && m.eventType == 
EventTypes.PEER_START) {
+                PeerStartRequest psr = (PeerStartRequest) r;
+                psr.peerChurnCallback.onChurnSuccess();
+            } else if (r instanceof PeerStopRequest && m.eventType == 
EventTypes.PEER_STOP) {
+                PeerStopRequest psr = (PeerStopRequest) r;
+                psr.peerChurnCallback.onChurnSuccess();
+            } else {
+                logger.error("unexpected peer event message, event type %s and 
request %s",
+                             m.eventType, r);
+            }
+        }
         public void visit(PeerCreateSuccessMessage m) {
-            RequestContainer.Request r = requests.getRequest(m.operation_id);
+            RequestContainer.Request r = requests.getRequest(m.operationId);
             if (!(r instanceof PeerCreateRequest)) {
                 logger.warn("response to peer create request does not match");
                 return;
             }
             PeerCreateRequest pcr = (PeerCreateRequest) r;
-            // FIXME: create peer
-            pcr.cb.onPeerCreated(null);
+            Peer p = new Peer(pcr.peer_id);
+            pcr.cb.onPeerCreated(p);
         }
+        public void visit(PeerInformationMessage m) {
+            RequestContainer.Request r = requests.getRequest(m.operationId);
+            if (null == r) {
+                logger.error("unexpected peer information message (opid={})", 
m.operationId);
+                return;
+            }
+            if (!(r instanceof PeerInformationRequest)) {
+                logger.warn("response to peer create request does not match");
+                return;
+            }
+            PeerInformationRequest pir = (PeerInformationRequest) r;
+            CompressedConfig ccfg = new CompressedConfig(m.compressedConfig);
+            pir.cb.onSuccess(m.peerIdentity, ccfg.decompress());
+        }
+        public void visit(GenericOperationSuccessMessage m) {
+            OperationRequest r = requests.getRequest(m.operationId);
+            if (null == r) {
+                logger.error("unexpected generic success message (opid={})", 
m.operationId);
+                return;
+            }
+            if (!(r instanceof GenericOperationRequest)) {
+                logger.error(String.format(
+                               "got GenericOperationSuccessMessage as response 
to request '%s', opid %s; event type %s",
+                               r.getClass(), m.operationId, m.eventType));
+                return;
+            }
+            GenericOperationRequest gr = (GenericOperationRequest) r;
+            gr.onSuccess();
+        }
+        public void visit(ConnectionEventMessage m) {
+            OperationRequest r = requests.getRequest(m.operationId);
+            if (null == r) {
+                logger.error("unexpected connection event message (opid={})", 
m.operationId);
+                return;
+            }
+            if (!(r instanceof PeerConnectOverlayRequest)) {
+                logger.error("unexpected connection event message for 
operation {}", r.getClass());
+                return;
+            }
+            PeerConnectOverlayRequest cr = (PeerConnectOverlayRequest) r;
+            cr.cb.onCompletion();
+
+        }
+        public void visit(OperationFailEventMessage m) {
+            logger.error("operation failed: " + m.errorMessage);
+        }
         @Override
         public void handleError() {
             throw new AssertionError();
         }
     }
 
-    private long getNextOpId() {
-        return (((long) host.id) << 32) | (long) operationCounter++;
-    }
-
-
     /**
      * Connect to a controller process.  The configuration to use for the 
connection
      * is retreived from the given host where a controller is started using
@@ -144,7 +362,7 @@
         this.host = host;
         client = new Client("testbed", host.cfg);
         client.installReceiver(new ControllerMessageReceiver());
-        requests = new MatchingRequestContainer<Long, 
RequestContainer.Request>(client);
+        requests = new MatchingRequestContainer<Long, 
OperationRequest>(client);
 
         ControllerInitMessage m = new ControllerInitMessage();
         m.event_mask = event_mask;
@@ -175,14 +393,13 @@
      * 'Peer.getInformation'.
      *
      * @param host host to run the peer on; cannot be NULL
-     * @param cfg Template configuration to use for the peer. Should exist 
until
-     *          operation is cancelled or GNUNET_TESTBED_operation_done() is 
called
+     * @param cfg Template configuration to use for the peer.
      * @param cb the callback to call when the peer has been created
      * @return the operation handle
      */
     public Cancelable createPeer(Host host, Configuration cfg, 
PeerCreateCallback cb) {
         PeerCreateRequest r = new PeerCreateRequest(host, cfg, cb);
-        return requests.addRequest(r.op_id, r);
+        return requests.addRequest(r.operationId, r);
     }
 
     /**
@@ -220,7 +437,7 @@
      *          controller via TCP/IP
      * @return the operation handle
      */
-    public Operation link(Host delegated_host, Host slave_host, boolean 
is_subordinate) {
+    public Cancelable link(Host delegated_host, Host slave_host, boolean 
is_subordinate) {
         // low priority
         throw new UnsupportedOperationException("not yet implemented");
     }
@@ -249,25 +466,54 @@
      * at a particular host.
      */
     public class Peer {
-        public Operation start(PeerChurnCallback peerChurnCallback) {
-            return null;
+        final private int peerId;
+
+        /**
+         * Private constructor for the peer, creates the peer with the given 
id,
+         * and, implicitly the containing controller.
+         *
+         * @param peerId id for the peer
+         */
+        private Peer(int peerId) {
+            this.peerId = peerId;
         }
 
-        public Operation stop(PeerChurnCallback peerChurnCallback) {
-            return null;
+        /**
+         * Start this peer
+         *
+         * @param peerChurnCallback completion callback
+         * @return handle to cancel the operation
+         */
+        public Cancelable start(PeerChurnCallback peerChurnCallback) {
+            PeerStartRequest r = new PeerStartRequest(this, peerChurnCallback);
+            return requests.addRequest(r.operationId, r);
         }
 
-        public Operation getInformation() {
-            return null;
+        /**
+         * Stop this peer
+         *
+         * @param peerChurnCallback completion callback
+         * @return handle to cancel the operation
+         */
+        public Cancelable stop(PeerChurnCallback peerChurnCallback) {
+            PeerStopRequest r = new PeerStopRequest(this, peerChurnCallback);
+            return requests.addRequest(r.operationId, r);
         }
 
+
+        public Cancelable requestInformation(PeerInformationCallback cb) {
+            PeerInformationRequest r = new PeerInformationRequest(this, cb);
+            return requests.addRequest(r.operationId, r);
+        }
+
         /*
          * Change peer configuration.  Must only be called while the
          * peer is stopped.  Ports and paths cannot be changed this
          * way.
          */
-        public Operation updateConfiguration(Configuration cfg) {
-            return null;
+        public Cancelable updateConfiguration(Configuration cfg, 
OperationCompletionCallback cb) {
+            PeerUpdateConfigurationRequest r = new 
PeerUpdateConfigurationRequest(this, cb, cfg);
+            return requests.addRequest(r.operationId, r);
         }
 
         /*
@@ -275,26 +521,58 @@
          * peer is stopped.  Ports and paths cannot be changed this
          * way.
          */
-        public Operation destroy() {
-            return null;
+        public Cancelable destroy(OperationCompletionCallback cb) {
+            PeerDestroyRequest r = new PeerDestroyRequest(this, cb);
+            return requests.addRequest(r.operationId, r);
         }
 
-        public Operation manageService(String serviceName, boolean start) {
-            return null;
+        public Cancelable manageService(String serviceName, boolean start, 
OperationCompletionCallback cb) {
+            PeerManageServiceRequest r = new PeerManageServiceRequest(this, 
serviceName, start);
+            return requests.addRequest(r.operationId, r);
         }
 
         /**
          * Both peers must have been started before calling this function.
          * This function then obtains a HELLO from this peer, gives it to 
'otherPeer'
-         * and asks 'otherPeer' to connect to this peer..
+         * and asks 'otherPeer' to connect to this peer.
+         *
+         * @param otherPeer peer to connect this peer to
+         * @param cb callback object to signal completion or failure
+         * @return token to cancel the request
+         *
          */
-        public Operation connectOverlay(OperationCompletionCallback cb, Peer 
otherPeer) {
-            return null;
+        public Cancelable connectOverlay(Peer otherPeer, 
OperationCompletionCallback cb) {
+            PeerConnectOverlayRequest r = new PeerConnectOverlayRequest(this, 
otherPeer, cb);
+            return requests.addRequest(r.operationId, r);
         }
 
-        public Operation getServiceConnection(String serviceName /*,... */) {
+
+        /**
+         * Connect to a service offered by the given peer.  Will ensure that
+         * the request is queued to not overwhelm our ability to create and
+         * maintain connections with other systems.  The actual service
+         * handle is then returned via the 'op_result' member in the event
+         * callback.  The 'ca' callback is used to create the connection
+         * when the time is right; the 'da' callback will be used to
+         * destroy the connection (upon 'GNUNET_TESTBED_operation_done').
+         * 'GNUNET_TESTBED_operation_done' can be used to abort this
+         * operation until the event callback has been called.
+         *
+         * @param serviceName name of the service to connect to
+         * @param serviceAdapter callback object for connection establishment 
and tear-down.
+         * @return handle for the operation
+         */
+        public Cancelable getServiceConnection(String serviceName, 
ServiceAdapter serviceAdapter) {
             return null;
         }
 
+        /**
+         * Get the host this peer is running on.
+         *
+         * @return the host this peer is running on
+         */
+        public Host getHost() {
+            return Controller.this.host;
+        }
     }
 }

Deleted: gnunet-java/src/main/java/org/gnunet/testbed/Operation.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testbed/Operation.java 2013-09-25 
09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/testbed/Operation.java 2013-09-25 
11:32:19 UTC (rev 29526)
@@ -1,11 +0,0 @@
-package org.gnunet.testbed;
-
-/**
- * Created with IntelliJ IDEA.
- * User: dold
- * Date: 8/25/13
- * Time: 1:26 PM
- * To change this template use File | Settings | File Templates.
- */
-public interface Operation {
-}

Modified: 
gnunet-java/src/main/java/org/gnunet/testbed/callbacks/OperationCompletionCallback.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/testbed/callbacks/OperationCompletionCallback.java
     2013-09-25 09:24:05 UTC (rev 29525)
+++ 
gnunet-java/src/main/java/org/gnunet/testbed/callbacks/OperationCompletionCallback.java
     2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,11 +1,7 @@
 package org.gnunet.testbed.callbacks;
 
-/**
- * Created with IntelliJ IDEA.
- * User: dold
- * Date: 8/25/13
- * Time: 1:35 PM
- * To change this template use File | Settings | File Templates.
- */
+
 public interface OperationCompletionCallback {
+    void onCompletion();
+    void onError(String emsg);
 }

Added: 
gnunet-java/src/main/java/org/gnunet/testbed/callbacks/PeerInformationCallback.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/testbed/callbacks/PeerInformationCallback.java
                         (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/testbed/callbacks/PeerInformationCallback.java
 2013-09-25 11:32:19 UTC (rev 29526)
@@ -0,0 +1,29 @@
+/*
+ This file is part of GNUnet.
+  (C) 2012, 2013 Christian Grothoff (and other contributing authors)
+
+  GNUnet is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  GNUnet is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with GNUnet; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+ */
+
+package org.gnunet.testbed.callbacks;
+
+
+import org.gnunet.util.Configuration;
+import org.gnunet.util.PeerIdentity;
+
+public interface PeerInformationCallback {
+    void onSuccess(PeerIdentity peerIdentity, Configuration configuration);
+}

Added: 
gnunet-java/src/main/java/org/gnunet/testbed/callbacks/ServiceAdapter.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testbed/callbacks/ServiceAdapter.java  
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/testbed/callbacks/ServiceAdapter.java  
2013-09-25 11:32:19 UTC (rev 29526)
@@ -0,0 +1,28 @@
+/*
+ This file is part of GNUnet.
+  (C) 2012, 2013 Christian Grothoff (and other contributing authors)
+
+  GNUnet is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  GNUnet is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with GNUnet; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+ */
+
+package org.gnunet.testbed.callbacks;
+
+import org.gnunet.util.Configuration;
+
+public interface ServiceAdapter {
+    void onConnect(Configuration cfg);
+    void onDisconnect();
+}

Modified: 
gnunet-java/src/main/java/org/gnunet/testbed/messages/ConnectionEventMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/testbed/messages/ConnectionEventMessage.java
   2013-09-25 09:24:05 UTC (rev 29525)
+++ 
gnunet-java/src/main/java/org/gnunet/testbed/messages/ConnectionEventMessage.java
   2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,11 +1,47 @@
+/*
+ This file is part of GNUnet.
+  (C) 2012, 2013 Christian Grothoff (and other contributing authors)
+
+  GNUnet is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  GNUnet is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with GNUnet; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+ */
+
 package org.gnunet.testbed.messages;
 
-/**
- * Created with IntelliJ IDEA.
- * User: dold
- * Date: 9/10/13
- * Time: 1:37 PM
- * To change this template use File | Settings | File Templates.
- */
-public class ConnectionEventMessage {
+
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UInt64;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
address@hidden(472)
+public class ConnectionEventMessage implements GnunetMessage.Body {
+    /**
+     * 'enum GNUNET_TESTBED_EventType' (in NBO);
+     * either GNUNET_TESTBED_ET_CONNECT or GNUNET_TESTBED_ET_DISCONNECT.
+     */
+    @UInt32
+    public int eventType;
+
+    @UInt32
+    public int peer1;
+
+    @UInt32
+    public int peer2;
+
+    @UInt64
+    public long operationId;
+
 }

Modified: 
gnunet-java/src/main/java/org/gnunet/testbed/messages/ControllerInitMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/testbed/messages/ControllerInitMessage.java
    2013-09-25 09:24:05 UTC (rev 29525)
+++ 
gnunet-java/src/main/java/org/gnunet/testbed/messages/ControllerInitMessage.java
    2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,3 +1,22 @@
+/*
+ This file is part of GNUnet.
+  (C) 2012, 2013 Christian Grothoff (and other contributing authors)
+
+  GNUnet is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  GNUnet is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with GNUnet; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+ */
 package org.gnunet.testbed.messages;
 
 
@@ -8,7 +27,7 @@
 import org.gnunet.util.GnunetMessage;
 
 /**
- * Initial message from a client to a testing control service.
+ * Initial message from a client to a testbed control service.
  */
 @UnionCase(460)
 public class ControllerInitMessage implements GnunetMessage.Body {

Deleted: 
gnunet-java/src/main/java/org/gnunet/testbed/messages/CreatePeerSuccessMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/testbed/messages/CreatePeerSuccessMessage.java
 2013-09-25 09:24:05 UTC (rev 29525)
+++ 
gnunet-java/src/main/java/org/gnunet/testbed/messages/CreatePeerSuccessMessage.java
 2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,5 +0,0 @@
-package org.gnunet.testbed.messages;
-
-
-public class CreatePeerSuccessMessage {
-}

Modified: 
gnunet-java/src/main/java/org/gnunet/testbed/messages/GenericOperationSuccessMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/testbed/messages/GenericOperationSuccessMessage.java
   2013-09-25 09:24:05 UTC (rev 29525)
+++ 
gnunet-java/src/main/java/org/gnunet/testbed/messages/GenericOperationSuccessMessage.java
   2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,11 +1,19 @@
 package org.gnunet.testbed.messages;
 
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UInt64;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
 /**
- * Created with IntelliJ IDEA.
- * User: dold
- * Date: 9/10/13
- * Time: 2:30 AM
- * To change this template use File | Settings | File Templates.
+ * Event notification from a controller to a client for
+ * a generic operational success where the operation does
+ * not return any data.
  */
-public class GenericOperationSuccessMessage {
address@hidden(475)
+public class GenericOperationSuccessMessage implements GnunetMessage.Body {
+    @UInt32
+    public int eventType;
+    @UInt64
+    public long operationId;
 }

Modified: 
gnunet-java/src/main/java/org/gnunet/testbed/messages/ManagePeerServiceMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/testbed/messages/ManagePeerServiceMessage.java
 2013-09-25 09:24:05 UTC (rev 29525)
+++ 
gnunet-java/src/main/java/org/gnunet/testbed/messages/ManagePeerServiceMessage.java
 2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,5 +1,29 @@
 package org.gnunet.testbed.messages;
 
 
-public class ManagePeerServiceMessage {
+import org.gnunet.construct.*;
+import org.gnunet.util.GnunetMessage;
+
address@hidden(483)
+public class ManagePeerServiceMessage implements GnunetMessage.Body {
+    /**
+     * Testbed-internal ID of the target peer.
+     */
+    @UInt32
+    public int peerId;
+
+    /**
+     * Operation ID.
+     */
+    @UInt64
+    public long operationId;
+
+    @UInt8
+    public boolean start;
+
+    /**
+     * Name of the service to start or stop.
+     */
+    @ZeroTerminatedString(optional = false)
+    public String serviceName;
 }

Modified: 
gnunet-java/src/main/java/org/gnunet/testbed/messages/OperationFailEventMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/testbed/messages/OperationFailEventMessage.java
        2013-09-25 09:24:05 UTC (rev 29525)
+++ 
gnunet-java/src/main/java/org/gnunet/testbed/messages/OperationFailEventMessage.java
        2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,11 +1,18 @@
 package org.gnunet.testbed.messages;
 
-/**
- * Created with IntelliJ IDEA.
- * User: dold
- * Date: 9/10/13
- * Time: 2:30 AM
- * To change this template use File | Settings | File Templates.
- */
-public class OperationFailEventMessage {
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UInt64;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.construct.ZeroTerminatedString;
+import org.gnunet.util.GnunetMessage;
+
+
address@hidden(473)
+public class OperationFailEventMessage implements GnunetMessage.Body {
+    @UInt32
+    public int eventType;
+    @UInt64
+    public long operationId;
+    @ZeroTerminatedString
+    public String errorMessage;
 }

Modified: 
gnunet-java/src/main/java/org/gnunet/testbed/messages/OverlayConnectMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/testbed/messages/OverlayConnectMessage.java
    2013-09-25 09:24:05 UTC (rev 29525)
+++ 
gnunet-java/src/main/java/org/gnunet/testbed/messages/OverlayConnectMessage.java
    2013-09-25 11:32:19 UTC (rev 29526)
@@ -21,7 +21,7 @@
      * Operation ID that is used to identify this operation.
      */
     @UInt64
-    public int operation_id;
+    public long operationId;
 
     /**
      * Unique ID for the second peer.
@@ -33,5 +33,5 @@
      * The ID of the host which runs peer2
      */
     @UInt32
-    public int peer2_host;
+    public int hostOfPeer2;
 }

Deleted: 
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerConfigurationInformationMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerConfigurationInformationMessage.java
      2013-09-25 09:24:05 UTC (rev 29525)
+++ 
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerConfigurationInformationMessage.java
      2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,8 +0,0 @@
-package org.gnunet.testbed.messages;
-
-/**
- * Peer configuration and identity reply from controller to a client.
- */
-public class PeerConfigurationInformationMessage {
-
-}

Modified: 
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerCreateSuccessMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerCreateSuccessMessage.java
 2013-09-25 09:24:05 UTC (rev 29525)
+++ 
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerCreateSuccessMessage.java
 2013-09-25 11:32:19 UTC (rev 29526)
@@ -13,5 +13,5 @@
     @UInt32
     public int peer_id;
     @UInt64
-    public long operation_id;
+    public long operationId;
 }

Modified: 
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerDestroyMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerDestroyMessage.java   
    2013-09-25 09:24:05 UTC (rev 29525)
+++ 
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerDestroyMessage.java   
    2013-09-25 11:32:19 UTC (rev 29526)
@@ -11,5 +11,5 @@
     @UInt32
     public int peer_id;
     @UInt64
-    public int operation_id;
+    public long operation_id;
 }

Modified: 
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerEventMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerEventMessage.java 
2013-09-25 09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerEventMessage.java 
2013-09-25 11:32:19 UTC (rev 29526)
@@ -16,11 +16,11 @@
      * either GNUNET_TESTBED_ET_PEER_START or GNUNET_TESTBED_ET_PEER_STOP.
      */
     @Int32
-    int event_type;
+    public int eventType;
     @Int32
-    int host_id;
+    public int host_id;
     @Int32
-    int peer_id;
+    public int peer_id;
     @Int64
-    int operation_id;
+    public long operationId;
 }

Added: 
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerGetInformationMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerGetInformationMessage.java
                                (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerGetInformationMessage.java
        2013-09-25 11:32:19 UTC (rev 29526)
@@ -0,0 +1,34 @@
+/*
+ This file is part of GNUnet.
+  (C) 2012, 2013 Christian Grothoff (and other contributing authors)
+
+  GNUnet is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  GNUnet is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with GNUnet; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+ */
+
+package org.gnunet.testbed.messages;
+
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UInt64;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
address@hidden(476)
+public class PeerGetInformationMessage implements GnunetMessage.Body {
+    @UInt32
+    public int peerId;
+    @UInt64
+    public long operationId;
+}

Added: 
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerInformationMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerInformationMessage.java
                           (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerInformationMessage.java
   2013-09-25 11:32:19 UTC (rev 29526)
@@ -0,0 +1,56 @@
+/*
+ This file is part of GNUnet.
+  (C) 2012, 2013 Christian Grothoff (and other contributing authors)
+
+  GNUnet is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  GNUnet is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with GNUnet; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+ */
+
+package org.gnunet.testbed.messages;
+
+import org.gnunet.construct.*;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.PeerIdentity;
+
address@hidden(477)
+public class PeerInformationMessage implements GnunetMessage.Body {
+    /**
+     * The testbed-internal id of the peer relevant to this
+     * information.
+     */
+    @UInt32
+    public int peerId;
+
+    /**
+     * Operation ID of the operation that created this event.
+     */
+    @UInt64
+    public long operationId;
+
+    /**
+     * Identity of the peer.
+     */
+    @NestedMessage
+    public PeerIdentity peerIdentity;
+
+    @UInt16
+    public int uncompressedConfigSize;
+
+    @UInt16
+    public int pading1;
+
+    @FillWith @UInt8
+    public byte[] compressedConfig;
+}

Modified: 
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerReconfigureMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerReconfigureMessage.java
   2013-09-25 09:24:05 UTC (rev 29525)
+++ 
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerReconfigureMessage.java
   2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,8 +1,21 @@
 package org.gnunet.testbed.messages;
 
+import org.gnunet.construct.*;
+import org.gnunet.util.GnunetMessage;
+
 /**
  * Message sent from client to testing service to
  * reconfigure a (stopped) a peer.
  */
-public class PeerReconfigureMessage {
address@hidden(465)
+public class PeerReconfigureMessage implements GnunetMessage.Body {
+    @UInt32
+    public int peerId;
+    @UInt64
+    public long operationId;
+    @UInt16
+    public int uncompressedConfigSize;
+    @FillWith
+    @UInt8
+    public byte[] compressedConfig;
 }

Modified: 
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerStartMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerStartMessage.java 
2013-09-25 09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerStartMessage.java 
2013-09-25 11:32:19 UTC (rev 29526)
@@ -11,5 +11,5 @@
     @UInt32
     public int peer_id;
     @UInt64
-    public int operation_id;
+    public long operation_id;
 }

Modified: 
gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerStopMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerStopMessage.java  
2013-09-25 09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/testbed/messages/PeerStopMessage.java  
2013-09-25 11:32:19 UTC (rev 29526)
@@ -11,5 +11,5 @@
     @UInt32
     public int peer_id;
     @UInt64
-    public int operation_id;
+    public long operation_id;
 }

Modified: gnunet-java/src/main/java/org/gnunet/util/AbsoluteTime.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/AbsoluteTime.java 2013-09-25 
09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/util/AbsoluteTime.java 2013-09-25 
11:32:19 UTC (rev 29526)
@@ -266,4 +266,8 @@
     public Date toDate() {
         return new Date(abs_value_us / 1000);
     }
+
+    public long getSeconds() {
+        return abs_value_us / (1000 * 1000);
+    }
 }

Modified: gnunet-java/src/main/java/org/gnunet/util/CryptoECC.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/CryptoECC.java    2013-09-25 
09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/util/CryptoECC.java    2013-09-25 
11:32:19 UTC (rev 29526)
@@ -24,13 +24,18 @@
 import org.gnunet.construct.Message;
 
 import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOError;
 import java.io.IOException;
 import java.math.BigInteger;
 import java.nio.ByteBuffer;
 import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.StandardOpenOption;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Random;
 
 /**
  * Implementation of the Ed25519 public-key signature system. See 
http://ed25519.cr.yp.to/.
@@ -68,6 +73,19 @@
             privateKey.d = data;
             return privateKey;
         }
+
+        public static PrivateKey createRandom() {
+            PrivateKey privateKey = new PrivateKey();
+            privateKey.d = new byte[32];
+            SecureRandom r = new SecureRandom();
+            r.nextBytes(privateKey.d);
+            return privateKey;
+        }
+
+        public void write(String privKeyFilename) throws IOException {
+            File f = new File(privKeyFilename);
+            Files.write(f.toPath(), d, StandardOpenOption.CREATE_NEW);
+        }
     }
 
     /**
@@ -97,6 +115,17 @@
         public BigInteger[] asPoint() {
             return new BigInteger[]{decodeint(x), decodeint(y)};
         }
+
+        public static PublicKey fromString(String s) {
+            PublicKey pk = new PublicKey();
+            byte[] data = Strings.stringToData(s, 32);
+            if (null == data)
+                return null;
+            BigInteger[] point = decodepoint(data);
+            pk.x = encodeint(point[0]);
+            pk.y = encodeint(point[1]);
+            return pk;
+        }
     }
 
     /**
@@ -124,6 +153,35 @@
             System.arraycopy(s, 0, data, r.length, s.length);
             return Strings.dataToString(data);
         }
+
+        public static Signature fromString(String s) {
+            Signature sig = new Signature();
+            sig.r = new byte[32];
+            sig.s = new byte[32];
+            byte[] data = Strings.stringToData(s, 64);
+            if (null == data) {
+                return null;
+            }
+            System.arraycopy(data, 0, sig.r, 0, 32);
+            System.arraycopy(data, 32, sig.s, 0, 32);
+            return sig;
+        }
+
+        /**
+         * Create a random signature that is invalid with
+         * very high probability.
+         *
+         * @return random signature, most probably invalid
+         */
+        public static Signature randomSignature() {
+            Random r = new Random();
+            Signature sig = new Signature();
+            sig.r = new byte[32];
+            sig.s = new byte[32];
+            r.nextBytes(sig.r);
+            r.nextBytes(sig.s);
+            return sig;
+        }
     }
 
     // curve parameter b

Modified: gnunet-java/src/main/java/org/gnunet/util/HashCode.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/HashCode.java     2013-09-25 
09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/util/HashCode.java     2013-09-25 
11:32:19 UTC (rev 29526)
@@ -91,4 +91,9 @@
     public int hashCode() {
         return Arrays.hashCode(this.data);
     }
+
+    @Override
+    public String toString() {
+        return Strings.dataToString(data);
+    }
 }
\ No newline at end of file

Modified: gnunet-java/src/main/java/org/gnunet/util/PeerIdentity.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/PeerIdentity.java 2013-09-25 
09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/util/PeerIdentity.java 2013-09-25 
11:32:19 UTC (rev 29526)
@@ -66,4 +66,10 @@
     public int hashCode() {
         return Arrays.hashCode(data);
     }
+
+    public static PeerIdentity fromString(String value) {
+        PeerIdentity p = new PeerIdentity();
+        p.data = Strings.stringToData(value, p.data.length);
+        return (p.data == null) ? null : p;
+    }
 }

Modified: gnunet-java/src/main/java/org/gnunet/util/Program.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/Program.java      2013-09-25 
09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/util/Program.java      2013-09-25 
11:32:19 UTC (rev 29526)
@@ -177,8 +177,10 @@
 
     /**
      * Start the Program as the initial task of the Scheduler.
+     *
+     * @return the exit value of the scheduler
      */
-    public final void start() {
+    public final int start() {
         Parser optParser = new Parser(this);
         unprocessedArgs = optParser.parse(args);
 
@@ -204,6 +206,8 @@
                 }
             });
         }
+
+        return returnValue;
     }
 
     /**
@@ -219,9 +223,9 @@
     /**
      * Override to implement the behavior of the Program.
      */
-    public abstract void run();
+    protected abstract void run();
 
-    public final Configuration getConfiguration() {
+    protected final Configuration getConfiguration() {
         return cfg;
     }
 }

Modified: gnunet-java/src/main/java/org/gnunet/util/RunaboutMessageReceiver.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/RunaboutMessageReceiver.java      
2013-09-25 09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/util/RunaboutMessageReceiver.java      
2013-09-25 11:32:19 UTC (rev 29526)
@@ -20,6 +20,7 @@
 
 package org.gnunet.util;
 
+import org.gnunet.construct.ProtocolViolationException;
 import org.grothoff.Runabout;
 
 /**
@@ -30,4 +31,12 @@
     public void process(GnunetMessage.Body msg) {
         this.visitAppropriate(msg);
     }
+    public void handleUnknownMessageType(UnknownMessageBody 
unknownMessageBody) {
+        throw new ProtocolViolationException(
+                String.format("unexpected message type: %s", 
unknownMessageBody.id));
+    }
+    public void visit(UnknownMessageBody unknownMessageBody) {
+        // FIXME: UnknownMessageBody is ugly and should be removed entirely
+        handleUnknownMessageType(unknownMessageBody);
+    }
 }

Modified: gnunet-java/src/main/java/org/gnunet/util/Strings.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/Strings.java      2013-09-25 
09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/util/Strings.java      2013-09-25 
11:32:19 UTC (rev 29526)
@@ -32,8 +32,6 @@
      * GNUnet specific.  It was chosen such that it only uses characters
      * in [0-9A-V], can be produced without complex arithmetics and uses a
      * small number of characters.
-     * Does not append 0-terminator, but returns a pointer to the place where
-     * it should be placed, if needed.
      *
      * returned string has length ((size*8) + (((size*8) % 5) > 0 ? 5 - 
((size*8) % 5) : 0)) / 5 bytes
      *
@@ -75,7 +73,7 @@
      *
      * @param string the string to decode
      * @param outSize size of the output buffer
-     * @return GNUNET_OK on success, GNUNET_SYSERR if result has the wrong 
encoding
+     * @return the decoded data on success, null if result has the wrong 
encoding
      */
 
     public static byte[] stringToData(String string, int outSize) {
@@ -97,20 +95,20 @@
             shift = 0;
         }
         if ((encoded_len + shift) / 5 != enclen) {
-            throw new AssertionError();
+            return null;
         }
 
         wpos = outSize;
         rpos = enclen;
         bits = (ret = getValue__(string.charAt((int) (--rpos)))) >> (5 - 
encoded_len % 5);
         if (-1 == ret) {
-            throw new AssertionError();
+            return null;
         }
         while (wpos > 0) {
             assert rpos > 0;
             bits = ((ret = getValue__(string.charAt((int) (--rpos)))) << vbit) 
| bits;
             if (-1 == ret) {
-                throw new AssertionError();
+                return null;
             }
             vbit += 5;
             if (vbit >= 8) {

Added: gnunet-java/src/main/java/org/gnunet/voting/Ballot.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/Ballot.java                     
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/voting/Ballot.java     2013-09-25 
11:32:19 UTC (rev 29526)
@@ -0,0 +1,289 @@
+/*
+ This file is part of GNUnet.
+  (C) 2012, 2013 Christian Grothoff (and other contributing authors)
+
+  GNUnet is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  GNUnet is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with GNUnet; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+ */
+
+package org.gnunet.voting;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.Maps;
+import org.gnunet.util.*;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.*;
+import java.util.regex.Pattern;
+
+/**
+ * Public information about an election.
+ */
+public class Ballot {
+
+    String topic;
+    List<String> choices;
+    AbsoluteTime electionStartTime;
+    AbsoluteTime electionEndTime;
+    BiMap<String,PeerIdentity> authorities;
+    SortedMap<String,CryptoECC.Signature> registrationSigs;
+    CryptoECC.PublicKey caPub;
+    CryptoECC.PublicKey issuerPub;
+    CryptoECC.Signature issuerSig;
+    CryptoECC.PublicKey voter_pub;
+    CryptoECC.Signature permission;
+    CryptoECC.PublicKey voter_sig;
+    SortedMap<String,CryptoECC.Signature> vouchers;
+
+    /**
+     * Choice in plaintext.
+     * This field would be encrypted in a secure version.
+     */
+    int choiceId = -1;
+
+    /**
+     * Load a ballot from file.
+     *
+     * @throws InvalidBallotException
+     * @param filename
+     */
+    public Ballot(String filename) {
+        Configuration cfg = new Configuration();
+        cfg.parse(filename);
+        fillBallot(cfg);
+    }
+
+    public Ballot(Configuration cfg) {
+        fillBallot(cfg);
+    }
+
+    private void fillBallot(Configuration cfg) {
+        Optional<String> optTopic = cfg.getValueString("election", "TOPIC");
+        if (!optTopic.isPresent()) {
+            throw new InvalidBallotException("ballot has no topic");
+        }
+        topic = optTopic.get();
+        Optional<String> optChoices = cfg.getValueString("election", 
"CHOICES");
+        if (!optChoices.isPresent()) {
+            throw new InvalidBallotException("ballot has no choices");
+        }
+        choices = Arrays.asList(optChoices.get().split(Pattern.quote("//")));
+        if (choices.size() < 2) {
+            throw new InvalidBallotException("less than two choices present");
+        }
+        authorities = HashBiMap.create();
+        for (Map.Entry<String,String> e : 
cfg.getSection("authorities").entrySet()) {
+            String alias = e.getKey();
+            PeerIdentity peerIdentity = PeerIdentity.fromString(e.getValue());
+            if (peerIdentity == null) {
+                throw new InvalidBallotException(
+                        String.format("authority %s has invalid peer 
identity", alias));
+            }
+            authorities.put(alias, peerIdentity);
+        }
+        if (authorities.size() < 1) {
+            throw new InvalidBallotException("ballot must specify at least one 
authority");
+        }
+        Optional<String> optCaPub = cfg.getValueString("election", "CA_PUB");
+        if (!optCaPub.isPresent()) {
+            throw new InvalidBallotException("no CA pub key given");
+        }
+        caPub = CryptoECC.PublicKey.fromString(optCaPub.get());
+        if (null == caPub) {
+            throw new InvalidBallotException("CA pub key invalid");
+        }
+        Optional<String> optIssuerPub = cfg.getValueString("election", 
"ISSUER_PUB");
+        if (optIssuerPub.isPresent()) {
+            issuerPub = CryptoECC.PublicKey.fromString(optIssuerPub.get());
+            Optional<String> optIssuerSig = cfg.getValueString("election", 
"ISSUER_SIG");
+            if (!optIssuerSig.isPresent()) {
+                throw new InvalidBallotException("issuer public key present, 
but no signature");
+            }
+            issuerSig = CryptoECC.Signature.fromString(optIssuerSig.get());
+        }
+        registrationSigs = new TreeMap<String, CryptoECC.Signature>();
+        for (Map.Entry<String,String> e : 
cfg.getSection("registration-signatures").entrySet()) {
+            CryptoECC.Signature sig = 
CryptoECC.Signature.fromString(e.getValue());
+            if (null == sig) {
+                throw new InvalidBallotException("registration signature has 
invalid format");
+            }
+            if (!authorities.containsKey(e.getKey())) {
+                throw new InvalidBallotException("ballot contains superfluous 
registration signature");
+            }
+            registrationSigs.put(e.getKey(), sig);
+        }
+        Optional<String> optChoiceId = cfg.getValueString("vote", "CHOICE_ID");
+        if (optChoiceId.isPresent()) {
+            choiceId = Integer.parseInt(optChoiceId.get());
+            if (choiceId < 0 || choiceId >= choices.size()) {
+                throw new InvalidBallotException("invalid choice in vote");
+            }
+        } else {
+            choiceId = -1;
+        }
+    }
+
+    public HashCode getBallotGuid() {
+        if (issuerSig == null) {
+            throw new InvalidBallotException("can't compute GUID of non-issued 
ballot");
+        }
+        MessageDigest digest;
+        try {
+            digest = MessageDigest.getInstance("SHA-512");
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("crypto algorithm required but not 
provided");
+        }
+        digest.update(topic.getBytes());
+        //digest.update(Longs.toByteArray(electionStartTime.getSeconds()));
+        //digest.update(Longs.toByteArray(electionEndTime.getSeconds()));
+        for (String choice : choices) {
+            digest.update(choice.getBytes());
+        }
+        for (SortedMap.Entry<String,PeerIdentity> x : authorities.entrySet()) {
+            digest.update(x.getKey().getBytes());
+            digest.update(x.getValue().data);
+        }
+        digest.update(issuerPub.x);
+        digest.update(issuerPub.y);
+        digest.update(caPub.x);
+        digest.update(caPub.y);
+        return new HashCode(digest.digest());
+    }
+
+    /**
+     * Encode the given choice permanently in the ballot.
+     *
+     * @param choice the choice to encode the ballot
+     * @param privateKey the private key to use for encoding
+     */
+    public void encodeChoice(String choice, CryptoECC.PrivateKey privateKey) {
+        choiceId = -1;
+        int i = 0;
+        for (String possibleChoice : choices) {
+            if (choice.equals(possibleChoice)) {
+                choiceId = i;
+            }
+            i++;
+        }
+        if (choiceId == -1) {
+            throw new InvalidBallotException(String.format("choice '%s' not 
valid", choice));
+        }
+    }
+
+    public Configuration toConfiguration() {
+        Configuration cfg = new Configuration();
+        cfg.setValueString("election", "TOPIC", topic);
+        cfg.setValueString("election", "CHOICES", 
Joiner.on("//").join(choices));
+        cfg.setValueString("election", "CA_PUB", caPub.toString());
+        for (Map.Entry<String, PeerIdentity> e : authorities.entrySet()) {
+            cfg.setValueString("authorities", e.getKey(), 
e.getValue().toString());
+        }
+        for (Map.Entry<String, CryptoECC.Signature> e : 
registrationSigs.entrySet()) {
+            cfg.setValueString("registration-signatures", e.getKey(), 
e.getValue().toString());
+        }
+        if (null != issuerPub) {
+            cfg.setValueString("election", "ISSUER_PUB", issuerPub.toString());
+            cfg.setValueString("election", "ISSUER_SIG", issuerSig.toString());
+        }
+        if (-1 != choiceId) {
+            cfg.setValueNumber("vote", "CHOICE_ID", choiceId);
+        }
+        return cfg;
+    }
+
+    public String serialize() {
+        return toConfiguration().serialize();
+    }
+
+    public String describe() {
+        StringBuilder buf = new StringBuilder();
+        buf.append("Topic: ");
+        buf.append("'");
+        buf.append(topic);
+        buf.append("'\n");
+        buf.append("Choices:\n");
+        for (int i = 0; i < choices.size(); i++) {
+            buf.append(""+(i+1));
+            buf.append(". '");
+            buf.append(choices.get(i));
+            buf.append("'\n");
+        }
+        buf.append("Authorities:\n");
+        for (Map.Entry<String, PeerIdentity> e : authorities.entrySet()) {
+            buf.append("'");
+            buf.append(e.getKey());
+            buf.append("', ");
+            buf.append(e.getValue().toString());
+            buf.append("\n");
+        }
+        if (issuerPub == null) {
+            buf.append("issue status: not issued\n");
+        } else {
+            buf.append("issue status: issued, GUID ");
+            buf.append(getBallotGuid().toString());
+            buf.append("\n");
+        }
+        if (!registrationSigs.isEmpty()) {
+            buf.append("ballot is registered with the following 
authorities:\n");
+            for (Map.Entry<String, CryptoECC.Signature> e : 
registrationSigs.entrySet()) {
+                buf.append(e.getKey());
+                buf.append(" ");
+            }
+            buf.append("\n");
+        }
+        else {
+            buf.append("ballot not registered\n");
+        }
+        if (choiceId != -1) {
+            buf.append("choice selected\n");
+        } else {
+            buf.append("no choice selected\n");
+        }
+        return buf.toString();
+    }
+
+    public List<PeerIdentity> getRemainingSubmitAuthorities() {
+        // FIXME: only return remaining authorities
+        return new ArrayList<PeerIdentity>(authorities.values());
+    }
+
+
+    public List<PeerIdentity> getRemainingRegisterAuthorities() {
+        LinkedList<PeerIdentity> remaining = new LinkedList<PeerIdentity>();
+        for (SortedMap.Entry<String,PeerIdentity> x : authorities.entrySet()) {
+            if (!registrationSigs.containsKey(x.getKey()))
+                remaining.add(x.getValue());
+        }
+        return remaining;
+    }
+
+    public void issue(CryptoECC.PrivateKey privateKey) {
+        issuerPub = CryptoECC.computePublicKey(privateKey);
+        issuerSig = CryptoECC.sign(getBallotGuid().data, privateKey, 
issuerPub);
+    }
+
+    public void addRegistrationSignature(PeerIdentity currentAuthority, 
CryptoECC.Signature registrationSignature) {
+        String alias = authorities.inverse().get(currentAuthority);
+        registrationSigs.put(alias, registrationSignature);
+    }
+
+    public List<PeerIdentity> getAuthorities() {
+        return new ArrayList<PeerIdentity>(authorities.values());
+    }
+}

Modified: gnunet-java/src/main/java/org/gnunet/voting/BallotTool.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/BallotTool.java 2013-09-25 
09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/voting/BallotTool.java 2013-09-25 
11:32:19 UTC (rev 29526)
@@ -27,8 +27,10 @@
 import com.google.common.io.OutputSupplier;
 import org.gnunet.mesh.Mesh;
 import org.gnunet.mesh.MeshRunabout;
+import org.gnunet.mesh.TunnelEndHandler;
 import org.gnunet.util.Configuration;
 import org.gnunet.util.CryptoECC;
+import org.gnunet.util.PeerIdentity;
 import org.gnunet.util.Program;
 import org.gnunet.util.getopt.Argument;
 import org.gnunet.util.getopt.ArgumentAction;
@@ -41,6 +43,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.charset.Charset;
+import java.util.List;
 import java.util.regex.Pattern;
 
 /**
@@ -71,6 +74,13 @@
             boolean register = false;
 
             @Argument(
+                    shortname = "i",
+                    longname = "issue",
+                    action = ArgumentAction.SET,
+                    description = "sign the ballot as issuer")
+            boolean issue = false;
+
+            @Argument(
                     shortname = "x",
                     longname = "select",
                     action = ArgumentAction.STORE_STRING,
@@ -99,6 +109,21 @@
                     description = "write a template ballot to the give ballot 
file")
             boolean template = false;
 
+            @Argument(
+                    shortname = "g",
+                    longname = "gen-key",
+                    action = ArgumentAction.SET,
+                    description = "generate a private key")
+            boolean genKey = false;
+
+            @Argument(
+                    shortname = "P",
+                    longname = "print-key",
+                    action = ArgumentAction.SET,
+                    description = "print the public key for the private key")
+            boolean printKey = false;
+
+
             @Override
             protected String makeHelpText() {
                 return "gnunet-ballot [OPTIONS]... BALLOT [PRIVKEYFILE]\n" +
@@ -120,7 +145,7 @@
                 }
             }
 
-            private void runRegister(String ballotFilename, String 
privKeyFilename) {
+            private void runIssue(String ballotFilename, String 
privKeyFilename) {
                 File bf = new File(ballotFilename);
                 if (!bf.exists()) {
                     System.err.println("ballot file does not exist");
@@ -131,26 +156,18 @@
                     System.err.println("private-key file does not exist");
                     return;
                 }
-                Configuration ballot = new Configuration();
-                ballot.parse(ballotFilename);
+                Ballot b = new Ballot(ballotFilename);
                 CryptoECC.PrivateKey privateKey = 
CryptoECC.PrivateKey.fromFile(privKeyFilename);
                 if (privateKey == null) {
                     System.err.println("keyfile invalid");
                     return;
                 }
-                CryptoECC.PublicKey publicKey = 
CryptoECC.computePublicKey(privateKey);
-                ballot.setValueString("election", "ISSUER_PUB", 
publicKey.toString());
-                String sigData = ballot.serialize("election", "authorities");
-                CryptoECC.Signature sig = 
CryptoECC.sign(sigData.getBytes(Charsets.UTF_8), privateKey, publicKey);
-                ballot.setValueString("registration-issuer", "SIGNATURE", 
sig.toString());
-                String b = ballot.serialize();
+                b.issue(privateKey);
                 try {
-                    Files.write(b, bf, Charsets.UTF_8);
+                    Files.write(b.serialize(), bf, Charsets.UTF_8);
                 } catch (IOException e) {
-                    System.err.println("can not write to ballot file");
-                    return;
+                    System.err.println("could not write ballot file: " + 
e.getMessage());
                 }
-                System.out.println("ballot registered");
             }
 
             public void runSelect(String ballotFilename, String 
privKeyFilename, String choice) {
@@ -164,30 +181,13 @@
                     System.err.println("private-key file does not exist");
                     return;
                 }
-                Configuration ballot = new Configuration();
-                ballot.parse(ballotFilename);
                 CryptoECC.PrivateKey privateKey = 
CryptoECC.PrivateKey.fromFile(privKeyFilename);
                 if (privateKey == null) {
                     System.err.println("keyfile invalid");
                     return;
                 }
-                CryptoECC.PublicKey publicKey = 
CryptoECC.computePublicKey(privateKey);
-                String choicesStr = ballot.getValueString("election", 
"CHOICES").get();
-                String[] choices = 
choicesStr.trim().split(Pattern.quote("//"));
-                choice = choice.trim();
-                boolean found = false;
-                for (String possibleChoice : choices) {
-                    if (possibleChoice.trim().equals(choice))
-                        found = true;
-                }
-                if (!found) {
-                    System.err.println("choice invalid");
-                    return;
-                }
-                CryptoECC.Signature sig = 
CryptoECC.sign(choice.getBytes(Charsets.UTF_8), privateKey, publicKey);
-                ballot.setValueString("vote", "CHOICE_PLAIN", choice);
-                ballot.setValueString("vote", "PUB", publicKey.toString());
-                ballot.setValueString("vote", "SIG", sig.toString());
+                Ballot ballot = new Ballot(ballotFilename);
+                ballot.encodeChoice(choice, privateKey);
                 try {
                     Files.write(ballot.serialize(), bf, Charsets.UTF_8);
                 } catch (IOException e) {
@@ -197,42 +197,49 @@
                 System.out.println("vote written to ballot file");
             }
 
-            public void runSubmit(String ballotFilename) {
-                class SubmitMessageReceiver extends MeshRunabout {
-                    public void visit(SubmitResponseMessage m) {
-                        // FIXME ...
-                    }
-                }
-                File bf = new File(ballotFilename);
-                if (!bf.exists()) {
-                    System.err.println("ballot file does not exist");
+            public void runPermission(String ballotFilename, String 
privKeyFilename) {
+
+            }
+
+            public void runGenKey(String privKeyFilename) {
+                File kf = new File(privKeyFilename);
+                if (kf.exists()) {
+                    System.err.println("private key file already exists, not 
overwriting");
                     return;
                 }
-                Configuration ballot = new Configuration();
-                ballot.parse(ballotFilename);
-                Mesh mesh = new Mesh(getConfiguration(), null, null, new 
SubmitMessageReceiver());
-                Mesh.Tunnel<Void> t = mesh.createTunnel(null, 
CertificateAuthorityService.MESH_PORT, true, true, null);
-                SubmitMessage m = new SubmitMessage();
-                m.ballot = ballot.toString();
-                t.send(m);
-                // FIXME ...
+                CryptoECC.PrivateKey privateKey = 
CryptoECC.PrivateKey.createRandom();
+                try {
+                    privateKey.write(privKeyFilename);
+                } catch (IOException e) {
+                    System.err.println("can't write to key file: " + 
e.getMessage());
+                }
             }
 
-            public void runQuery(String ballotFilename) {
-
+            public void runPrintKey(String privKeyFilename) {
+                File kf = new File(privKeyFilename);
+                if (!kf.exists()) {
+                    System.err.println("key file does not exist");
+                    return;
+                }
+                CryptoECC.PrivateKey privateKey = 
CryptoECC.PrivateKey.fromFile(privKeyFilename);
+                if (privateKey == null) {
+                    System.err.println("key invalid");
+                    return;
+                }
+                CryptoECC.PublicKey publicKey = 
CryptoECC.computePublicKey(privateKey);
+                System.out.println(publicKey.toString());
             }
 
-
-            public void runPermission(String ballotFilename, String 
privKeyFilename) {
-
-            }
-
-
             public void runVerify(String ballotFilename) {
-                System.err.print("not implemented");
+                File bf = new File(ballotFilename);
+                if (!bf.exists()) {
+                    System.err.println("ballot file does not exist");
+                    return;
+                }
+                Ballot ballot = new Ballot(ballotFilename);
+                System.out.print(ballot.describe());
             }
 
-
             @Override
             public void run() {
                 if (template) {
@@ -245,11 +252,20 @@
                 }
 
                 if (register) {
+                    if (this.unprocessedArgs.length != 1) {
+                        System.err.println("-r/--register requires exactly one 
positional argument");
+                        return;
+                    }
+                    RegisterCommand c = new 
RegisterCommand(getConfiguration(), unprocessedArgs[0]);
+                    c.run();
+                    return;
+                }
+                if (issue) {
                     if (this.unprocessedArgs.length != 2) {
-                        System.err.println("-r/--register requires exactly two 
positional arguments");
+                        System.err.println("-i/--issue requires exactly two 
positional arguments");
                         return;
                     }
-                    runRegister(unprocessedArgs[0], unprocessedArgs[1]);
+                    runIssue(unprocessedArgs[0], unprocessedArgs[1]);
                     return;
                 }
                 if (select != null) {
@@ -265,7 +281,8 @@
                         System.err.println("-s/--submit requires exactly one 
positional argument");
                         return;
                     }
-                    runSubmit(unprocessedArgs[0]);
+                    SubmitCommand c = new SubmitCommand(getConfiguration(), 
unprocessedArgs[0]);
+                    c.run();
                     return;
                 }
                 if (verify) {
@@ -281,7 +298,8 @@
                         System.err.println("-q/--query requires exactly one 
positional argument");
                         return;
                     }
-                    runQuery(unprocessedArgs[0]);
+                    QueryCommand c = new QueryCommand(getConfiguration(), 
unprocessedArgs[0]);
+                    c.run();
                     return;
                 }
                 if (permission) {
@@ -289,9 +307,25 @@
                         System.err.println("-p/--permission requires exactly 
two positional arguments");
                         return;
                     }
-                    runPermission(unprocessedArgs[0], unprocessedArgs[1]);
+                    PermissionCommand c = new 
PermissionCommand(getConfiguration(),
+                            unprocessedArgs[0], unprocessedArgs[1]);
+                    c.run();
                     return;
                 }
+                if (genKey) {
+                    if (this.unprocessedArgs.length != 1) {
+                        System.err.println("-g/--gen-key requires exactly one 
positional argument");
+                        return;
+                    }
+                    runGenKey(unprocessedArgs[0]);
+                }
+                if (printKey) {
+                    if (this.unprocessedArgs.length != 1) {
+                        System.err.println("-P/--print-key requires exactly 
one positional argument");
+                        return;
+                    }
+                    runPrintKey(unprocessedArgs[0]);
+                }
             }
         }.start();
 

Added: gnunet-java/src/main/java/org/gnunet/voting/InvalidBallotException.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/InvalidBallotException.java     
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/voting/InvalidBallotException.java     
2013-09-25 11:32:19 UTC (rev 29526)
@@ -0,0 +1,30 @@
+/*
+ This file is part of GNUnet.
+  (C) 2012, 2013 Christian Grothoff (and other contributing authors)
+
+  GNUnet is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  GNUnet is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with GNUnet; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+ */
+
+package org.gnunet.voting;
+
+/**
+ * Exception thrown when a ballot contains insufficient or invalid data.
+ */
+public class InvalidBallotException extends RuntimeException {
+    public InvalidBallotException(String msg) {
+        super(msg);
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/voting/PermissionCommand.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/PermissionCommand.java          
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/voting/PermissionCommand.java  
2013-09-25 11:32:19 UTC (rev 29526)
@@ -0,0 +1,132 @@
+/*
+ This file is part of GNUnet.
+  (C) 2012, 2013 Christian Grothoff (and other contributing authors)
+
+  GNUnet is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  GNUnet is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with GNUnet; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+ */
+
+/*
+ This file is part of GNUnet.
+  (C) 2012, 2013 Christian Grothoff (and other contributing authors)
+
+  GNUnet is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  GNUnet is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with GNUnet; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+ */
+
+/*
+ This file is part of GNUnet.
+  (C) 2012, 2013 Christian Grothoff (and other contributing authors)
+
+  GNUnet is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  GNUnet is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with GNUnet; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+ */
+
+package org.gnunet.voting;
+
+
+import org.gnunet.mesh.Mesh;
+import org.gnunet.mesh.MeshRunabout;
+import org.gnunet.mesh.TunnelEndHandler;
+import org.gnunet.util.Configuration;
+import org.gnunet.util.PeerIdentity;
+import org.gnunet.voting.messages.QueryFailureMessage;
+import org.gnunet.voting.messages.QueryMessage;
+import org.gnunet.voting.messages.QueryResponseMessage;
+
+import java.io.File;
+import java.util.List;
+import java.util.Random;
+
+public class PermissionCommand extends MeshRunabout implements 
TunnelEndHandler {
+    private final String ballotFilename;
+    private Ballot ballot;
+    private final Configuration cfg;
+    private Mesh mesh;
+    private Mesh.Tunnel<Void> tunnel;
+    private boolean submitted = false;
+
+    @Override
+    public void onTunnelEnd(Mesh.Tunnel tunnel) {
+        if (!submitted)
+            throw new AssertionError();
+    }
+
+    public void visit(QueryResponseMessage m) {
+        submitted = true;
+        if (m.results.length != ballot.choices.size()) {
+            System.out.println("failure to query result: malformed response");
+        } else {
+            System.out.println("got results:");
+            for (int i = 0; i < m.results.length; i++) {
+                System.out.println("'" + ballot.choices.get(i) + "': " + 
m.results[i]);
+            }
+        }
+
+        tunnel.destroy();
+        mesh.destroy();
+    }
+
+
+    public void visit(QueryFailureMessage m) {
+        submitted = true;
+        System.out.println("failure to query result: authority refused");
+        tunnel.destroy();
+        mesh.destroy();
+    }
+
+    public PermissionCommand(Configuration cfg, String ballotFilename, String 
pubKeyString) {
+        this.cfg = cfg;
+        this.ballotFilename = ballotFilename;
+    }
+
+    public void run() {
+        File bf = new File(ballotFilename);
+        if (!bf.exists()) {
+            System.err.println("ballot file does not exist");
+            return;
+        }
+        ballot = new Ballot(ballotFilename);
+
+        Random r = new Random();
+        mesh = new Mesh(cfg, this, this);
+        tunnel = mesh.createTunnel(null /* FIXME */, 
CertificateAuthorityService.MESH_PORT, true, true, null);
+        //tunnel.send(m);
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/voting/QueryCommand.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/QueryCommand.java               
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/voting/QueryCommand.java       
2013-09-25 11:32:19 UTC (rev 29526)
@@ -0,0 +1,120 @@
+/*
+ This file is part of GNUnet.
+  (C) 2012, 2013 Christian Grothoff (and other contributing authors)
+
+  GNUnet is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  GNUnet is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with GNUnet; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+ */
+
+/*
+ This file is part of GNUnet.
+  (C) 2012, 2013 Christian Grothoff (and other contributing authors)
+
+  GNUnet is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  GNUnet is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with GNUnet; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+ */
+
+package org.gnunet.voting;
+
+
+import org.gnunet.mesh.Mesh;
+import org.gnunet.mesh.MeshRunabout;
+import org.gnunet.mesh.TunnelEndHandler;
+import org.gnunet.util.Configuration;
+import org.gnunet.util.PeerIdentity;
+import org.gnunet.voting.messages.QueryFailureMessage;
+import org.gnunet.voting.messages.QueryMessage;
+import org.gnunet.voting.messages.QueryResponseMessage;
+
+import java.io.File;
+import java.util.List;
+import java.util.Random;
+
+public class QueryCommand extends MeshRunabout implements TunnelEndHandler {
+    private final String ballotFilename;
+    private Ballot ballot;
+    private final Configuration cfg;
+    private Mesh mesh;
+    private Mesh.Tunnel<Void> tunnel;
+    private boolean submitted = false;
+
+    @Override
+    public void onTunnelEnd(Mesh.Tunnel tunnel) {
+        if (!submitted)
+            throw new AssertionError();
+    }
+
+    public void visit(QueryResponseMessage m) {
+        submitted = true;
+        if (m.results.length != ballot.choices.size()) {
+            System.out.println("failure to query result: malformed response");
+        } else {
+            System.out.println("got results:");
+            for (int i = 0; i < m.results.length; i++) {
+                System.out.println("'" + ballot.choices.get(i) + "': " + 
m.results[i]);
+            }
+        }
+
+        tunnel.destroy();
+        mesh.destroy();
+    }
+
+
+    public void visit(QueryFailureMessage m) {
+        submitted = true;
+        System.out.println("failure to query result: authority refused");
+        tunnel.destroy();
+        mesh.destroy();
+    }
+
+    public QueryCommand(Configuration cfg, String ballotFilename) {
+        this.cfg = cfg;
+        this.ballotFilename = ballotFilename;
+    }
+
+    public void run() {
+        File bf = new File(ballotFilename);
+        if (!bf.exists()) {
+            System.err.println("ballot file does not exist");
+            return;
+        }
+        ballot = new Ballot(ballotFilename);
+        List<PeerIdentity> remainingAuthorities = ballot.getAuthorities();
+        if (remainingAuthorities.isEmpty()) {
+            System.err.println("no authorities available");
+            return;
+        }
+        Random r = new Random();
+        PeerIdentity authority = 
remainingAuthorities.get(r.nextInt(remainingAuthorities.size()));
+        System.out.println("querying authority" + authority.toString());
+        mesh = new Mesh(cfg, this, this);
+        tunnel = mesh.createTunnel(authority, TallyAuthorityDaemon.MESH_PORT, 
true, true, null);
+        QueryMessage m = new QueryMessage();
+        m.ballotGUID = ballot.getBallotGuid();
+        tunnel.send(m);
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/voting/RegisterCommand.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/RegisterCommand.java            
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/voting/RegisterCommand.java    
2013-09-25 11:32:19 UTC (rev 29526)
@@ -0,0 +1,119 @@
+/*
+ This file is part of GNUnet.
+  (C) 2012, 2013 Christian Grothoff (and other contributing authors)
+
+  GNUnet is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  GNUnet is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with GNUnet; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+ */
+
+/*
+ This file is part of GNUnet.
+  (C) 2012, 2013 Christian Grothoff (and other contributing authors)
+
+  GNUnet is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  GNUnet is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with GNUnet; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+ */
+
+package org.gnunet.voting;
+
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+import org.gnunet.mesh.Mesh;
+import org.gnunet.mesh.MeshRunabout;
+import org.gnunet.mesh.TunnelEndHandler;
+import org.gnunet.testbed.CompressedConfig;
+import org.gnunet.util.Configuration;
+import org.gnunet.util.PeerIdentity;
+import org.gnunet.voting.messages.BallotRegisterRequestMessage;
+import org.gnunet.voting.messages.BallotRegisterRespondMessage;
+import org.gnunet.voting.messages.SubmitMessage;
+import org.gnunet.voting.messages.SubmitResponseMessage;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Random;
+
+public class RegisterCommand extends MeshRunabout implements TunnelEndHandler {
+    private final String ballotFilename;
+    private Ballot ballot;
+    private final Configuration cfg;
+    private Mesh mesh;
+    private Mesh.Tunnel<Void> tunnel;
+    private boolean submitted = false;
+    private PeerIdentity currentAuthority;
+
+    @Override
+    public void onTunnelEnd(Mesh.Tunnel tunnel) {
+        if (!submitted)
+            throw new AssertionError();
+    }
+
+    public void visit(BallotRegisterRespondMessage m) {
+        submitted = true;
+        System.out.println("vote successfully registered");
+        ballot.addRegistrationSignature(currentAuthority, 
m.registrationSignature);
+        try {
+            Files.write(ballot.serialize(), new File(ballotFilename), 
Charsets.UTF_8);
+        } catch (IOException e) {
+            System.out.println("could not write ballot file");
+            return;
+        }
+        tunnel.destroy();
+        mesh.destroy();
+    }
+
+    public RegisterCommand(Configuration cfg, String ballotFilename) {
+        this.cfg = cfg;
+        this.ballotFilename = ballotFilename;
+    }
+
+    public void run() {
+        File bf = new File(ballotFilename);
+        if (!bf.exists()) {
+            System.err.println("ballot file does not exist");
+            return;
+        }
+        ballot = new Ballot(ballotFilename);
+
+        List<PeerIdentity> remainingAuthorities = 
ballot.getRemainingRegisterAuthorities();
+        if (remainingAuthorities.isEmpty()) {
+            System.err.println("all authorities already received the ballot");
+            return;
+        }
+        Random r = new Random();
+        currentAuthority = 
remainingAuthorities.get(r.nextInt(remainingAuthorities.size()));
+        System.out.println("registering ballot with authority " + 
currentAuthority.toString());
+        mesh = new Mesh(cfg, this, this);
+        tunnel = mesh.createTunnel(currentAuthority, 
TallyAuthorityDaemon.MESH_PORT, true, true, null);
+        BallotRegisterRequestMessage m = new BallotRegisterRequestMessage();
+        CompressedConfig ccfg = new CompressedConfig(ballot.toConfiguration());
+        m.compressedBallotConfig = ccfg.compressed_data;
+        tunnel.send(m);
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/voting/SubmitCommand.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/SubmitCommand.java              
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/voting/SubmitCommand.java      
2013-09-25 11:32:19 UTC (rev 29526)
@@ -0,0 +1,84 @@
+/*
+ This file is part of GNUnet.
+  (C) 2012, 2013 Christian Grothoff (and other contributing authors)
+
+  GNUnet is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  GNUnet is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with GNUnet; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+ */
+
+package org.gnunet.voting;
+
+
+import org.gnunet.mesh.Mesh;
+import org.gnunet.mesh.MeshRunabout;
+import org.gnunet.mesh.TunnelEndHandler;
+import org.gnunet.util.Configuration;
+import org.gnunet.util.PeerIdentity;
+import org.gnunet.voting.messages.SubmitMessage;
+import org.gnunet.voting.messages.SubmitResponseMessage;
+
+import java.io.File;
+import java.util.List;
+import java.util.Random;
+
+public class SubmitCommand extends MeshRunabout implements TunnelEndHandler {
+    private final String ballotFilename;
+    private Ballot ballot;
+    private final Configuration cfg;
+    private Mesh mesh;
+    private Mesh.Tunnel<Void> tunnel;
+    private boolean submitted = false;
+
+    @Override
+    public void onTunnelEnd(Mesh.Tunnel tunnel) {
+        if (!submitted)
+            throw new AssertionError();
+    }
+
+    public void visit(SubmitResponseMessage m) {
+        submitted = true;
+        System.out.println("vote successfully submitted");
+        tunnel.destroy();
+        mesh.destroy();
+    }
+
+    public SubmitCommand(Configuration cfg, String ballotFilename) {
+        this.cfg = cfg;
+        this.ballotFilename = ballotFilename;
+    }
+
+    public void run() {
+        File bf = new File(ballotFilename);
+        if (!bf.exists()) {
+            System.err.println("ballot file does not exist");
+            return;
+        }
+        ballot = new Ballot(ballotFilename);
+        List<PeerIdentity> remainingAuthorities = 
ballot.getRemainingSubmitAuthorities();
+        if (remainingAuthorities.isEmpty()) {
+            System.err.println("all authorities already received the ballot");
+            return;
+        }
+        Random r = new Random();
+        PeerIdentity authority = 
remainingAuthorities.get(r.nextInt(remainingAuthorities.size()));
+        System.out.println("submitting to authority" + authority.toString());
+        mesh = new Mesh(cfg, null, this);
+        tunnel = mesh.createTunnel(authority, TallyAuthorityDaemon.MESH_PORT, 
true, true, null);
+        SubmitMessage m = new SubmitMessage();
+        m.ballotGuid = ballot.getBallotGuid();
+        m.choiceId = ballot.choiceId;
+        tunnel.send(m);
+    }
+}

Copied: gnunet-java/src/main/java/org/gnunet/voting/TallyAuthorityDaemon.java 
(from rev 29503, 
gnunet-java/src/main/java/org/gnunet/voting/TallyAuthorityService.java)
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/TallyAuthorityDaemon.java       
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/voting/TallyAuthorityDaemon.java       
2013-09-25 11:32:19 UTC (rev 29526)
@@ -0,0 +1,116 @@
+/*
+ This file is part of GNUnet.
+  (C) 2012, 2013 Christian Grothoff (and other contributing authors)
+
+  GNUnet is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  GNUnet is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with GNUnet; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+ */
+package org.gnunet.voting;
+
+
+import com.google.common.collect.Maps;
+import org.gnunet.mesh.Mesh;
+import org.gnunet.mesh.MeshRunabout;
+import org.gnunet.testbed.CompressedConfig;
+import org.gnunet.util.*;
+import org.gnunet.voting.messages.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+
+public class TallyAuthorityDaemon extends Program {
+    private static final Logger logger = LoggerFactory
+            .getLogger(TallyAuthorityDaemon.class);
+
+    public static final int MESH_PORT = 1002;
+    private Mesh mesh;
+
+    private HashMap<HashCode, ElectionState> elections = Maps.newHashMap();
+
+    class ElectionState {
+        Ballot ballot;
+        /**
+         * Set of voters that have submitted their ballot.
+         */
+        Set<CryptoECC.PublicKey> voters = new HashSet<CryptoECC.PublicKey>();
+
+        /**
+         * Maping from choice to number of votes for that choice.
+         */
+        int[] tally;
+    }
+
+
+    private class TallyMeshReceiver extends MeshRunabout {
+        public void visit(SubmitMessage m) {
+            ElectionState electionState = elections.get(m.ballotGuid);
+            if (null == electionState) {
+                return;
+            }
+            if (m.choiceId < 0 || m.choiceId >= electionState.tally.length) {
+                return;
+            }
+            electionState.tally[m.choiceId] += 1;
+            SubmitResponseMessage rm = new SubmitResponseMessage();
+            getSender().send(rm);
+            getSender().receiveDone();
+        }
+
+        public void visit(BallotRegisterRequestMessage m) {
+            logger.info("ballot register requested");
+
+            CompressedConfig ccfg = new 
CompressedConfig(m.compressedBallotConfig);
+            Ballot b = new Ballot(ccfg.decompress());
+            ElectionState electionState = new ElectionState();
+            electionState.tally = new int[b.choices.size()];
+            electionState.ballot = b;
+            elections.put(b.getBallotGuid(), electionState);
+            BallotRegisterRespondMessage rm = new 
BallotRegisterRespondMessage();
+            rm.registrationSignature = CryptoECC.Signature.randomSignature();
+            getSender().send(rm);
+            getSender().receiveDone();
+        }
+
+        public void visit(QueryMessage m) {
+            ElectionState electionState = elections.get(m.ballotGUID);
+            if (null == electionState) {
+                QueryFailureMessage rm = new QueryFailureMessage();
+                getSender().send(rm);
+
+            } else {
+                QueryResponseMessage rm = new QueryResponseMessage();
+                rm.results = electionState.tally;
+                getSender().send(rm);
+            }
+            getSender().receiveDone();
+        }
+    }
+
+    public TallyAuthorityDaemon(String[] args) {
+        super(args);
+    }
+
+    public static void main(String[] args) {
+        TallyAuthorityDaemon daemon = new TallyAuthorityDaemon(args);
+        daemon.start();
+    }
+
+    @Override
+    public void run() {
+        logger.info("running tally daemon");
+        mesh = new Mesh(getConfiguration(), null, null, new 
TallyMeshReceiver(), MESH_PORT);
+    }
+}

Deleted: gnunet-java/src/main/java/org/gnunet/voting/TallyAuthorityService.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/TallyAuthorityService.java      
2013-09-25 09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/voting/TallyAuthorityService.java      
2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,81 +0,0 @@
-/*
- This file is part of GNUnet.
-  (C) 2012, 2013 Christian Grothoff (and other contributing authors)
-
-  GNUnet is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published
-  by the Free Software Foundation; either version 3, or (at your
-  option) any later version.
-
-  GNUnet is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with GNUnet; see the file COPYING.  If not, write to the
-  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-  Boston, MA 02111-1307, USA.
- */
-package org.gnunet.voting;
-
-
-import com.google.common.collect.Maps;
-import org.gnunet.mesh.Mesh;
-import org.gnunet.mesh.MeshRunabout;
-import org.gnunet.util.CryptoECC;
-import org.gnunet.util.RelativeTime;
-import org.gnunet.util.Service;
-import org.gnunet.voting.messages.BallotRegisterRequestMessage;
-import org.gnunet.voting.messages.CertificateRequestMessage;
-import org.gnunet.voting.messages.QueryResultMessage;
-import org.gnunet.voting.messages.SubmitMessage;
-
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-public class TallyAuthorityService extends Service {
-    public static final int MESH_PORT = 1002;
-    private Mesh mesh;
-
-
-    private class ElectionState {
-        /**
-         * Set of voters that have submitted their ballot.
-         */
-        private Set<CryptoECC.PublicKey> voters = new 
HashSet<CryptoECC.PublicKey>();
-
-        /**
-         * Maping from choice to number of votes for that choice.
-         */
-        private Map<String,Integer> tally = Maps.newHashMap();
-    }
-
-
-    private class TallyMeshReceiver extends MeshRunabout {
-        public void visit(SubmitMessage m) {
-            // TODO: count!
-        }
-        public void visit(BallotRegisterRequestMessage m) {
-            // TODO ...
-        }
-        public void visit(QueryResultMessage m) {
-
-        }
-    }
-
-    public TallyAuthorityService(String[] args) {
-        super("voting-tally", RelativeTime.FOREVER, true, args);
-    }
-
-    public static void main(String[] args) {
-        CertificateAuthorityService service = new 
CertificateAuthorityService(args);
-        service.start();
-    }
-
-    @Override
-    public void run() {
-        mesh = new Mesh(getConfiguration(), null, null, new 
TallyMeshReceiver(), MESH_PORT);
-    }
-}

Modified: 
gnunet-java/src/main/java/org/gnunet/voting/messages/BallotRegisterRequestMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/voting/messages/BallotRegisterRequestMessage.java
      2013-09-25 09:24:05 UTC (rev 29525)
+++ 
gnunet-java/src/main/java/org/gnunet/voting/messages/BallotRegisterRequestMessage.java
      2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,6 +1,11 @@
 package org.gnunet.voting.messages;
 
+import org.gnunet.construct.IntegerFill;
+import org.gnunet.construct.UnionCase;
 import org.gnunet.util.GnunetMessage;
 
address@hidden(42001)
 public class BallotRegisterRequestMessage implements GnunetMessage.Body {
+    @IntegerFill(signed = false, bitSize = 8)
+    public byte[] compressedBallotConfig;
 }

Modified: 
gnunet-java/src/main/java/org/gnunet/voting/messages/BallotRegisterRespondMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/voting/messages/BallotRegisterRespondMessage.java
      2013-09-25 09:24:05 UTC (rev 29525)
+++ 
gnunet-java/src/main/java/org/gnunet/voting/messages/BallotRegisterRespondMessage.java
      2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,6 +1,12 @@
 package org.gnunet.voting.messages;
 
+import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.CryptoECC;
 import org.gnunet.util.GnunetMessage;
 
address@hidden(42002)
 public class BallotRegisterRespondMessage implements GnunetMessage.Body {
+    @NestedMessage
+    public CryptoECC.Signature registrationSignature;
 }

Modified: 
gnunet-java/src/main/java/org/gnunet/voting/messages/CertificateRequestMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/voting/messages/CertificateRequestMessage.java
 2013-09-25 09:24:05 UTC (rev 29525)
+++ 
gnunet-java/src/main/java/org/gnunet/voting/messages/CertificateRequestMessage.java
 2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,8 +1,12 @@
 package org.gnunet.voting.messages;
 
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
 /**
  * Request a certificate that allows a voter to submit a ballot.
  * Sent by the voter to the certificate authority.
  */
-public class CertificateRequestMessage {
address@hidden(42003)
+public class CertificateRequestMessage implements GnunetMessage.Body {
 }

Modified: 
gnunet-java/src/main/java/org/gnunet/voting/messages/CertificateResponseMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/voting/messages/CertificateResponseMessage.java
        2013-09-25 09:24:05 UTC (rev 29525)
+++ 
gnunet-java/src/main/java/org/gnunet/voting/messages/CertificateResponseMessage.java
        2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,5 +1,9 @@
 package org.gnunet.voting.messages;
 
 
-public class CertificateResponseMessage {
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
address@hidden(42004)
+public class CertificateResponseMessage implements GnunetMessage.Body {
 }

Added: 
gnunet-java/src/main/java/org/gnunet/voting/messages/QueryFailureMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/voting/messages/QueryFailureMessage.java   
                            (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/voting/messages/QueryFailureMessage.java   
    2013-09-25 11:32:19 UTC (rev 29526)
@@ -0,0 +1,28 @@
+/*
+ This file is part of GNUnet.
+  (C) 2012, 2013 Christian Grothoff (and other contributing authors)
+
+  GNUnet is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  GNUnet is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with GNUnet; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+ */
+
+package org.gnunet.voting.messages;
+
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
address@hidden(42009)
+public class QueryFailureMessage implements GnunetMessage.Body {
+}

Copied: gnunet-java/src/main/java/org/gnunet/voting/messages/QueryMessage.java 
(from rev 29503, 
gnunet-java/src/main/java/org/gnunet/voting/messages/QueryResultMessage.java)
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/messages/QueryMessage.java      
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/voting/messages/QueryMessage.java      
2013-09-25 11:32:19 UTC (rev 29526)
@@ -0,0 +1,12 @@
+package org.gnunet.voting.messages;
+
+import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.HashCode;
+
address@hidden(42005)
+public class QueryMessage implements GnunetMessage.Body {
+    @NestedMessage
+    public HashCode ballotGUID;
+}

Copied: 
gnunet-java/src/main/java/org/gnunet/voting/messages/QueryResponseMessage.java 
(from rev 29503, 
gnunet-java/src/main/java/org/gnunet/voting/messages/QueryResultResponseMessage.java)
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/voting/messages/QueryResponseMessage.java  
                            (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/voting/messages/QueryResponseMessage.java  
    2013-09-25 11:32:19 UTC (rev 29526)
@@ -0,0 +1,12 @@
+package org.gnunet.voting.messages;
+
+
+import org.gnunet.construct.*;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.HashCode;
+
address@hidden(42006)
+public class QueryResponseMessage implements GnunetMessage.Body {
+    @IntegerFill(signed = false, bitSize = 32)
+    public int[] results;
+}

Deleted: 
gnunet-java/src/main/java/org/gnunet/voting/messages/QueryResultMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/voting/messages/QueryResultMessage.java    
    2013-09-25 09:24:05 UTC (rev 29525)
+++ 
gnunet-java/src/main/java/org/gnunet/voting/messages/QueryResultMessage.java    
    2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,5 +0,0 @@
-package org.gnunet.voting.messages;
-
-public class QueryResultMessage {
-
-}

Deleted: 
gnunet-java/src/main/java/org/gnunet/voting/messages/QueryResultResponseMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/voting/messages/QueryResultResponseMessage.java
        2013-09-25 09:24:05 UTC (rev 29525)
+++ 
gnunet-java/src/main/java/org/gnunet/voting/messages/QueryResultResponseMessage.java
        2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,5 +0,0 @@
-package org.gnunet.voting.messages;
-
-
-public class QueryResultResponseMessage {
-}

Modified: 
gnunet-java/src/main/java/org/gnunet/voting/messages/SubmitMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/messages/SubmitMessage.java     
2013-09-25 09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/java/org/gnunet/voting/messages/SubmitMessage.java     
2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,12 +1,19 @@
 package org.gnunet.voting.messages;
 
+import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UnionCase;
 import org.gnunet.construct.ZeroTerminatedString;
 import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.HashCode;
 
 /**
  * Message send by the voter to the election authority to submit a vote.
  */
address@hidden(42007)
 public class SubmitMessage implements GnunetMessage.Body {
-    @ZeroTerminatedString
-    public String ballot;
+    @NestedMessage
+    public HashCode ballotGuid;
+    @UInt32
+    public int choiceId;
 }

Modified: 
gnunet-java/src/main/java/org/gnunet/voting/messages/SubmitResponseMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/voting/messages/SubmitResponseMessage.java 
    2013-09-25 09:24:05 UTC (rev 29525)
+++ 
gnunet-java/src/main/java/org/gnunet/voting/messages/SubmitResponseMessage.java 
    2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,5 +1,9 @@
 package org.gnunet.voting.messages;
 
 
-public class SubmitResponseMessage {
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
address@hidden(42008)
+public class SubmitResponseMessage implements GnunetMessage.Body {
 }

Modified: gnunet-java/src/main/resources/org/gnunet/construct/MsgMap.txt
===================================================================
--- gnunet-java/src/main/resources/org/gnunet/construct/MsgMap.txt      
2013-09-25 09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/resources/org/gnunet/construct/MsgMap.txt      
2013-09-25 11:32:19 UTC (rev 29526)
@@ -10,10 +10,19 @@
 org.gnunet.util.GnunetMessage$Body|142=org.gnunet.dht.ClientPutMessage
 org.gnunet.util.GnunetMessage$Body|286=org.gnunet.mesh.LocalAckMessage
 org.gnunet.util.GnunetMessage$Body|285=org.gnunet.mesh.DataMessage
+org.gnunet.util.GnunetMessage$Body|42003=org.gnunet.voting.messages.CertificateRequestMessage
 org.gnunet.util.GnunetMessage$Body|153=org.gnunet.dht.MonitorStartStop
+org.gnunet.util.GnunetMessage$Body|42002=org.gnunet.voting.messages.BallotRegisterRespondMessage
+org.gnunet.util.GnunetMessage$Body|42001=org.gnunet.voting.messages.BallotRegisterRequestMessage
 
org.gnunet.util.GnunetMessage$Body|155=org.gnunet.dht.ClientPutConfirmationMessage
+org.gnunet.util.GnunetMessage$Body|42007=org.gnunet.voting.messages.SubmitMessage
+org.gnunet.util.GnunetMessage$Body|42006=org.gnunet.voting.messages.QueryResponseMessage
+org.gnunet.util.GnunetMessage$Body|42005=org.gnunet.voting.messages.QueryMessage
+org.gnunet.util.GnunetMessage$Body|42004=org.gnunet.voting.messages.CertificateResponseMessage
 org.gnunet.util.GnunetMessage$Body|144=org.gnunet.dht.ClientGetStopMessage
 org.gnunet.util.GnunetMessage$Body|145=org.gnunet.dht.ClientResultMessage
+org.gnunet.util.GnunetMessage$Body|42009=org.gnunet.voting.messages.QueryFailureMessage
+org.gnunet.util.GnunetMessage$Body|42008=org.gnunet.voting.messages.SubmitResponseMessage
 org.gnunet.util.GnunetMessage$Body|149=org.gnunet.dht.MonitorGetMessage
 org.gnunet.util.GnunetMessage$Body|150=org.gnunet.dht.MonitorGetRespMessage
 org.gnunet.util.GnunetMessage$Body|151=org.gnunet.dht.MonitorPutMessage
@@ -28,20 +37,26 @@
 org.gnunet.util.GnunetMessage$Body|523=org.gnunet.consensus.NewElementMessage
 org.gnunet.util.GnunetMessage$Body|68=org.gnunet.core.DisconnectNotifyMessage
 
org.gnunet.util.GnunetMessage$Body|70=org.gnunet.core.NotifyInboundTrafficMessage
+org.gnunet.util.GnunetMessage$Body|476=org.gnunet.testbed.messages.PeerGetInformationMessage
 
org.gnunet.util.GnunetMessage$Body|71=org.gnunet.core.NotifyOutboundTrafficMessage
+org.gnunet.util.GnunetMessage$Body|477=org.gnunet.testbed.messages.PeerInformationMessage
 org.gnunet.util.GnunetMessage$Body|64=org.gnunet.core.InitMessage
 
org.gnunet.util.GnunetMessage$Body|474=org.gnunet.testbed.messages.PeerCreateSuccessMessage
 org.gnunet.util.GnunetMessage$Body|65=org.gnunet.core.InitReplyMessage
+org.gnunet.util.GnunetMessage$Body|475=org.gnunet.testbed.messages.GenericOperationSuccessMessage
+org.gnunet.util.GnunetMessage$Body|472=org.gnunet.testbed.messages.ConnectionEventMessage
 org.gnunet.util.GnunetMessage$Body|67=org.gnunet.core.ConnectNotifyMessage
+org.gnunet.util.GnunetMessage$Body|473=org.gnunet.testbed.messages.OperationFailEventMessage
 org.gnunet.util.GnunetMessage$Body|76=org.gnunet.core.SendMessage
 
org.gnunet.util.GnunetMessage$Body|470=org.gnunet.testbed.messages.OverlayConnectMessage
 
org.gnunet.util.GnunetMessage$Body|471=org.gnunet.testbed.messages.PeerEventMessage
 
org.gnunet.util.GnunetMessage$Body|468=org.gnunet.testbed.messages.PeerDestroyMessage
 
org.gnunet.util.GnunetMessage$Body|466=org.gnunet.testbed.messages.PeerStartMessage
 
org.gnunet.util.GnunetMessage$Body|467=org.gnunet.testbed.messages.PeerStopMessage
+org.gnunet.util.GnunetMessage$Body|74=org.gnunet.core.SendMessageRequest
 
org.gnunet.util.GnunetMessage$Body|464=org.gnunet.testbed.messages.PeerCreateMessage
-org.gnunet.util.GnunetMessage$Body|74=org.gnunet.core.SendMessageRequest
 org.gnunet.util.GnunetMessage$Body|75=org.gnunet.core.SendMessageReady
+org.gnunet.util.GnunetMessage$Body|465=org.gnunet.testbed.messages.PeerReconfigureMessage
 
org.gnunet.util.GnunetMessage$Body|460=org.gnunet.testbed.messages.ControllerInitMessage
 org.gnunet.util.GnunetMessage$Body|323=org.gnunet.nse.UpdateMessage
 org.gnunet.util.GnunetMessage$Body|321=org.gnunet.nse.StartMessage
@@ -56,5 +71,6 @@
 
org.gnunet.util.GnunetMessage$Body|496=org.gnunet.testbed.messages.HelperReplyMessage
 
org.gnunet.util.GnunetMessage$Body|495=org.gnunet.testbed.messages.HelperInitMessage
 org.gnunet.util.GnunetMessage$Body|360=org.gnunet.transport.StartMessage
+org.gnunet.util.GnunetMessage$Body|483=org.gnunet.testbed.messages.ManagePeerServiceMessage
 org.gnunet.construct.MessageUnion|525=org.gnunet.consensus.ConcludeDoneMessage
-# generated 2013/09/11 02:53:45
+# generated 2013/09/25 01:34:20

Modified: gnunet-java/src/main/resources/org/gnunet/voting/template.espec
===================================================================
--- gnunet-java/src/main/resources/org/gnunet/voting/template.espec     
2013-09-25 09:24:05 UTC (rev 29525)
+++ gnunet-java/src/main/resources/org/gnunet/voting/template.espec     
2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,3 +1,6 @@
+# Ballot template.
+# A Ballot contains public information at various stages of an election.
+
 [election]
 
 # human-readable topic description of the election
@@ -6,11 +9,14 @@
 # choices for the voter, separated by a double slash
 CHOICES = yes//no
 
-# starting time of the election, yy/mm/dd hh:mm:ss UTC+x
+# starting time of the election, in the local time of the election issuer
 # authorities affirm that they are available for the election from this point 
in time
 ELECTION_START =
 
-# deadline for vote submission, yy/mm/dd hh:mm:ss UTC+x
+# election start time, as posix time stamp
+# ELECTION_START_POSIX
+
+# deadline for vote submission, in the local time of the election issuer
 # must be later than ELECTION_START
 ELECTION_END =
 
@@ -18,10 +24,6 @@
 # (fixme: need to define exactly which rounds there will be...)
 AUTHORITY_ROUND_X_TIME =
 
-# peer identity of the certificate authority for voting eligibility
-# currently the CA is a single peer, this may change in the future
-CA_PEER =
-
 # public key of the certificate authority
 CA_PUB =
 
@@ -34,7 +36,7 @@
 # awesome_authority_one = 123abc
 
 
-[registration-authorities]
+[registration-signatures]
 # will be filled in by gnunet-vote-call once
 # authorities agreed to participate, with one entry per authority
 # <authority-alias> = <signature>

Modified: gnunet-java/src/test/java/org/gnunet/testbed/TestbedTest.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/testbed/TestbedTest.java       
2013-09-25 09:24:05 UTC (rev 29525)
+++ gnunet-java/src/test/java/org/gnunet/testbed/TestbedTest.java       
2013-09-25 11:32:19 UTC (rev 29526)
@@ -1,11 +1,10 @@
 package org.gnunet.testbed;
 
 
-import org.gnunet.testbed.callbacks.ControllerEventCallback;
-import org.gnunet.testbed.callbacks.ControllerStatusCallback;
-import org.gnunet.testbed.callbacks.PeerCreateCallback;
+import org.gnunet.testbed.callbacks.*;
 import org.gnunet.testing.TestingFixture;
 import org.gnunet.util.Configuration;
+import org.gnunet.util.PeerIdentity;
 import org.gnunet.util.Program;
 import org.gnunet.util.Wrapper;
 import org.junit.Assert;
@@ -13,7 +12,7 @@
 
 public class TestbedTest extends TestingFixture {
 
-    @Test
+    @Test(timeout = 1000)
     public void test_controller_proc() {
         final Wrapper<Boolean> success = new Wrapper<Boolean>(false);
         new Program("-LDEBUG") {
@@ -39,9 +38,7 @@
     }
 
 
-
-
-    @Test
+    @Test(timeout = 1000)
     public void test_peer_create() {
         new Program("-LDEBUG") {
             ControllerProc cp;
@@ -86,4 +83,556 @@
             }
         }.start();
     }
+
+    @Test(timeout = 1000)
+    public void test_peer_destroy() {
+        new Program("-LDEBUG") {
+            ControllerProc cp;
+            Host h;
+            Controller c;
+
+            class CEC extends ControllerEventCallback {
+            }
+
+            class PCB implements PeerCreateCallback {
+                @Override
+                public void onPeerCreated(final Controller.Peer peer) {
+                    System.out.println("peer created!");
+                    peer.destroy(new OperationCompletionCallback() {
+                        @Override
+                        public void onCompletion() {
+                            System.out.println("destroy completed");
+                            c.disconnect();
+                            cp.stop();
+                        }
+
+                        @Override
+                        public void onError(String emsg) {
+                            Assert.fail();
+                        }
+                    });
+                }
+
+                @Override
+                public void onError(String errorMessage) {
+                    Assert.fail();
+                }
+            }
+
+            @Override
+            public void run() {
+                // use local peer's config, does that really make sense?
+                h = new Host(null, null, getConfiguration(), 0);
+                cp = new ControllerProc();
+                cp.start("127.0.0.1", h, new ControllerStatusCallback() {
+                    @Override
+                    public void onStartupSuccess(Configuration cfg) {
+                        System.out.println("startup success");
+                        c = new Controller(h, 1 | 2 | 4 | 8 | 32, new CEC());
+                        // FIXME: use config from resource
+                        c.createPeer(h, getConfiguration(), new PCB());
+                    }
+                    @Override
+                    public void onStartupFailure() {
+                        Assert.fail();
+                    }
+                });
+            }
+        }.start();
+    }
+
+    @Test(timeout = 5000)
+    public void test_peer_start() {
+        final Wrapper<Boolean> startSuccessful = new Wrapper<Boolean>(false);
+        int ret = new Program("-LDEBUG") {
+            ControllerProc cp;
+            Host h;
+            Controller c;
+
+            class CEC extends ControllerEventCallback {
+            }
+
+            class MyPeerChurnCallback implements PeerChurnCallback {
+
+                @Override
+                public void onChurnSuccess() {
+                    System.out.println("peer started");
+                    startSuccessful.set(true);
+                    c.disconnect();
+                    cp.stop();
+                }
+
+                @Override
+                public void onChurnError(String emsg) {
+                    Assert.fail("churn failed: " + emsg);
+                }
+            }
+
+            class MyPeerCreateCallback implements PeerCreateCallback {
+                @Override
+                public void onPeerCreated(Controller.Peer peer) {
+                    System.out.println("peer created");
+                    peer.start(new MyPeerChurnCallback());
+                }
+
+                @Override
+                public void onError(String errorMessage) {
+                    Assert.fail();
+                }
+            }
+
+            @Override
+            public void run() {
+                // use local peer's config, does that really make sense?
+                h = new Host(null, null, getConfiguration(), 0);
+                cp = new ControllerProc();
+                cp.start("127.0.0.1", h, new ControllerStatusCallback() {
+                    @Override
+                    public void onStartupSuccess(Configuration cfg) {
+                        System.out.println("controller started");
+                        c = new Controller(h, 1 | 2 | 4 | 8 | 32, new CEC());
+                        // FIXME: use config from resource
+                        c.createPeer(h, getConfiguration(), new 
MyPeerCreateCallback());
+                    }
+                    @Override
+                    public void onStartupFailure() {
+                        Assert.fail();
+                    }
+                });
+            }
+        }.start();
+        Assert.assertTrue(startSuccessful.get());
+        Assert.assertEquals(0, ret);
+    }
+
+    @Test(timeout = 5000)
+    public void test_peer_stop() {
+        final Wrapper<Boolean> startSuccessful = new Wrapper<Boolean>(false);
+        final Wrapper<Boolean> stopSuccessful = new Wrapper<Boolean>(false);
+        int ret = new Program("-LDEBUG") {
+            ControllerProc cp;
+            Host h;
+            Controller c;
+            Controller.Peer p;
+
+            class CEC extends ControllerEventCallback {
+            }
+
+            class MyPeerStopCallback implements PeerChurnCallback {
+
+                @Override
+                public void onChurnSuccess() {
+                    stopSuccessful.set(true);
+                    c.disconnect();
+                    cp.stop();
+                }
+
+                @Override
+                public void onChurnError(String emsg) {
+                    Assert.fail();
+                }
+            }
+
+            class MyPeerStartCallback implements PeerChurnCallback {
+
+                @Override
+                public void onChurnSuccess() {
+                    System.out.println("peer started");
+                    startSuccessful.set(true);
+                    p.stop(new MyPeerStopCallback());
+                }
+
+                @Override
+                public void onChurnError(String emsg) {
+                    Assert.fail("churn failed: " + emsg);
+                }
+            }
+
+            class MyPeerCreateCallback implements PeerCreateCallback {
+                @Override
+                public void onPeerCreated(Controller.Peer peer) {
+                    p = peer;
+                    System.out.println("peer created");
+                    peer.start(new MyPeerStartCallback());
+                }
+
+                @Override
+                public void onError(String errorMessage) {
+                    Assert.fail();
+                }
+            }
+
+            @Override
+            public void run() {
+                // use local peer's config, does that really make sense?
+                h = new Host(null, null, getConfiguration(), 0);
+                cp = new ControllerProc();
+                cp.start("127.0.0.1", h, new ControllerStatusCallback() {
+                    @Override
+                    public void onStartupSuccess(Configuration cfg) {
+                        System.out.println("controller started");
+                        c = new Controller(h, 1 | 2 | 4 | 8 | 32, new CEC());
+                        // FIXME: use config from resource
+                        c.createPeer(h, getConfiguration(), new 
MyPeerCreateCallback());
+                    }
+                    @Override
+                    public void onStartupFailure() {
+                        Assert.fail();
+                    }
+                });
+            }
+        }.start();
+        Assert.assertTrue(startSuccessful.get());
+        Assert.assertTrue(stopSuccessful.get());
+        Assert.assertEquals(0, ret);
+    }
+
+    @Test(timeout = 5000)
+    public void test_peer_stop_destroy() {
+        final Wrapper<Boolean> startSuccessful = new Wrapper<Boolean>(false);
+        final Wrapper<Boolean> stopSuccessful = new Wrapper<Boolean>(false);
+        final Wrapper<Boolean> destroySuccessful = new Wrapper<Boolean>(false);
+        int ret = new Program("-LDEBUG") {
+            ControllerProc cp;
+            Host h;
+            Controller c;
+            Controller.Peer p;
+
+            class CEC extends ControllerEventCallback {
+            }
+
+            class MyPeerDestroyCallback implements OperationCompletionCallback 
{
+
+                @Override
+                public void onCompletion() {
+                    destroySuccessful.set(true);
+                    c.disconnect();
+                    cp.stop();
+                }
+
+                @Override
+                public void onError(String emsg) {
+                    Assert.fail();
+                }
+            }
+
+            class MyPeerStopCallback implements PeerChurnCallback {
+                @Override
+                public void onChurnSuccess() {
+                    stopSuccessful.set(true);
+                    p.destroy(new MyPeerDestroyCallback());
+                }
+
+                @Override
+                public void onChurnError(String emsg) {
+                    Assert.fail();
+                }
+            }
+
+            class MyPeerStartCallback implements PeerChurnCallback {
+
+                @Override
+                public void onChurnSuccess() {
+                    System.out.println("peer started");
+                    startSuccessful.set(true);
+                    p.stop(new MyPeerStopCallback());
+                }
+
+                @Override
+                public void onChurnError(String emsg) {
+                    Assert.fail("churn failed: " + emsg);
+                }
+            }
+
+            class MyPeerCreateCallback implements PeerCreateCallback {
+                @Override
+                public void onPeerCreated(Controller.Peer peer) {
+                    p = peer;
+                    System.out.println("peer created");
+                    peer.start(new MyPeerStartCallback());
+                }
+
+                @Override
+                public void onError(String errorMessage) {
+                    Assert.fail();
+                }
+            }
+
+            @Override
+            public void run() {
+                // use local peer's config, does that really make sense?
+                h = new Host(null, null, getConfiguration(), 0);
+                cp = new ControllerProc();
+                cp.start("127.0.0.1", h, new ControllerStatusCallback() {
+                    @Override
+                    public void onStartupSuccess(Configuration cfg) {
+                        System.out.println("controller started");
+                        c = new Controller(h, 1 | 2 | 4 | 8 | 32, new CEC());
+                        // FIXME: use config from resource
+                        c.createPeer(h, getConfiguration(), new 
MyPeerCreateCallback());
+                    }
+                    @Override
+                    public void onStartupFailure() {
+                        Assert.fail();
+                    }
+                });
+            }
+        }.start();
+        Assert.assertTrue(startSuccessful.get());
+        Assert.assertTrue(stopSuccessful.get());
+        Assert.assertTrue(destroySuccessful.get());
+        Assert.assertEquals(0, ret);
+    }
+
+    @Test(timeout = 5000)
+    public void test_peer_get_config() {
+        final Wrapper<Boolean> startSuccessful = new Wrapper<Boolean>(false);
+        final Wrapper<Boolean> infoSuccessful = new Wrapper<Boolean>(false);
+        int ret = new Program("-LDEBUG") {
+            ControllerProc cp;
+            Host h;
+            Controller c;
+            Controller.Peer p;
+
+            class CEC extends ControllerEventCallback {
+            }
+
+            class MyPeerInformationCallback implements PeerInformationCallback 
{
+                @Override
+                public void onSuccess(PeerIdentity peerIdentity, Configuration 
configuration) {
+                    Assert.assertNotNull(peerIdentity);
+                    Assert.assertNotNull(configuration);
+                    Assert.assertTrue(configuration.getSections().size() > 0);
+                    infoSuccessful.set(true);
+                    c.disconnect();
+                    cp.stop();
+                }
+            }
+
+            class MyPeerCreateCallback implements PeerCreateCallback {
+                @Override
+                public void onPeerCreated(Controller.Peer peer) {
+                    p = peer;
+                    System.out.println("peer created");
+                    startSuccessful.set(true);
+                    peer.requestInformation(new MyPeerInformationCallback());
+                }
+
+                @Override
+                public void onError(String errorMessage) {
+                    Assert.fail();
+                }
+            }
+
+            @Override
+            public void run() {
+                // use local peer's config, does that really make sense?
+                h = new Host(null, null, getConfiguration(), 0);
+                cp = new ControllerProc();
+                cp.start("127.0.0.1", h, new ControllerStatusCallback() {
+                    @Override
+                    public void onStartupSuccess(Configuration cfg) {
+                        System.out.println("controller started");
+                        c = new Controller(h, 1 | 2 | 4 | 8 | 32, new CEC());
+                        // FIXME: use config from resource
+                        c.createPeer(h, getConfiguration(), new 
MyPeerCreateCallback());
+                    }
+                    @Override
+                    public void onStartupFailure() {
+                        Assert.fail();
+                    }
+                });
+            }
+        }.start();
+        Assert.assertTrue(startSuccessful.get());
+        Assert.assertTrue(infoSuccessful.get());
+        Assert.assertEquals(0, ret);
+    }
+
+    @Test(timeout = 5000)
+    public void test_peer_reconfigure() {
+        final Wrapper<Boolean> startSuccessful = new Wrapper<Boolean>(false);
+        final Wrapper<Boolean> infoSuccessful = new Wrapper<Boolean>(false);
+        final Wrapper<Boolean> reconfigureSuccessful = new 
Wrapper<Boolean>(false);
+        int ret = new Program("-LDEBUG") {
+            ControllerProc cp;
+            Host h;
+            Controller c;
+            Controller.Peer p;
+
+            class CEC extends ControllerEventCallback {
+            }
+
+            class MyUpdateConfigDoneCallback implements 
OperationCompletionCallback {
+                @Override
+                public void onCompletion() {
+                    System.out.println("config update complete");
+                    reconfigureSuccessful.set(true);
+                    c.disconnect();
+                    cp.stop();
+                }
+
+                @Override
+                public void onError(String emsg) {
+                    Assert.fail();
+                }
+            }
+
+            /*
+            class MyPeerInformationCallback implements PeerInformationCallback 
{
+                @Override
+                public void onSuccess(PeerIdentity peerIdentity, Configuration 
configuration) {
+                    Assert.assertNotNull(peerIdentity);
+                    Assert.assertNotNull(configuration);
+                    Assert.assertTrue(configuration.getSections().size() > 0);
+                    infoSuccessful.set(true);
+                    System.out.println("updating configuration");
+                    p.updateConfiguration(cfg, new 
MyUpdateConfigDoneCallback());
+                }
+            }
+
+            */
+
+            class MyPeerCreateCallback implements PeerCreateCallback {
+                @Override
+                public void onPeerCreated(Controller.Peer peer) {
+                    p = peer;
+                    System.out.println("peer created");
+                    startSuccessful.set(true);
+                    //peer.requestInformation(new MyPeerInformationCallback());
+
+                    Configuration cfg = new Configuration();
+                    cfg.setValueString("my-test-section", "my-test-option", 
"my-test-value");
+                    peer.updateConfiguration(cfg, new 
MyUpdateConfigDoneCallback());
+                }
+
+                @Override
+                public void onError(String errorMessage) {
+                    Assert.fail();
+                }
+            }
+
+            @Override
+            public void run() {
+                // use local peer's config, does that really make sense?
+                final Configuration configuration = getConfiguration();
+                h = new Host(null, null, configuration, 0);
+                cp = new ControllerProc();
+                cp.start("127.0.0.1", h, new ControllerStatusCallback() {
+                    @Override
+                    public void onStartupSuccess(Configuration cfg) {
+                        System.out.println("controller started");
+                        c = new Controller(h, 1 | 2 | 4 | 8 | 32, new CEC());
+                        // FIXME: use config from resource
+                        c.createPeer(h, configuration, new 
MyPeerCreateCallback());
+                    }
+
+                    @Override
+                    public void onStartupFailure() {
+                        Assert.fail();
+                    }
+                });
+            }
+        }.start();
+        Assert.assertTrue(reconfigureSuccessful.get());
+        Assert.assertEquals(0, ret);
+    }
+
+    @Test(timeout = 5000)
+    public void test_peer_get_connect_overlay() {
+        final Wrapper<Boolean> connectSuccessful = new Wrapper<Boolean>(false);
+        int ret = new Program("-LDEBUG") {
+            ControllerProc cp;
+            Host h;
+            Controller c;
+            Controller.Peer p0;
+            Controller.Peer p1;
+            int peersStarted = 0;
+
+            class CEC extends ControllerEventCallback {
+            }
+
+            class MyConnectCompleteCallback implements 
OperationCompletionCallback {
+
+                @Override
+                public void onCompletion() {
+                    connectSuccessful.set(true);
+                    c.disconnect();
+                    cp.stop();
+                }
+
+                @Override
+                public void onError(String emsg) {
+                    Assert.fail();
+                }
+            }
+
+            class MyPeerChurnCallback implements PeerChurnCallback {
+                int pid;
+                MyPeerChurnCallback(int pid) {
+                    this.pid = pid;
+                }
+                @Override
+                public void onChurnSuccess() {
+                    peersStarted += 1;
+                    if (peersStarted == 2) {
+                        p0.connectOverlay(p1, new MyConnectCompleteCallback());
+                    }
+
+                }
+
+                @Override
+                public void onChurnError(String emsg) {
+                    Assert.fail();
+                }
+            }
+
+            class MyPeerCreateCallback implements PeerCreateCallback {
+                int pid;
+                MyPeerCreateCallback(int pid) {
+                    this.pid = pid;
+                }
+                @Override
+                public void onPeerCreated(Controller.Peer peer) {
+                    if (pid == 0) {
+                        p0 = peer;
+                    } else if (pid == 1) {
+                        p1 = peer;
+                    } else {
+                        Assert.fail();
+                    }
+                    System.out.println("peer created");
+                    peer.start(new MyPeerChurnCallback(pid));
+                }
+
+                @Override
+                public void onError(String errorMessage) {
+                    Assert.fail();
+                }
+            }
+
+            @Override
+            public void run() {
+                // use local peer's config, does that really make sense?
+                h = new Host(null, null, getConfiguration(), 0);
+                cp = new ControllerProc();
+                cp.start("127.0.0.1", h, new ControllerStatusCallback() {
+                    @Override
+                    public void onStartupSuccess(Configuration cfg) {
+                        System.out.println("controller started");
+                        c = new Controller(h, 1 | 2 | 4 | 8 | 32, new CEC());
+                        // FIXME: use config from resource
+                        c.createPeer(h, getConfiguration(), new 
MyPeerCreateCallback(0));
+                        c.createPeer(h, getConfiguration(), new 
MyPeerCreateCallback(1));
+                    }
+                    @Override
+                    public void onStartupFailure() {
+                        Assert.fail();
+                    }
+                });
+            }
+        }.start();
+        Assert.assertTrue(connectSuccessful.get());
+        Assert.assertEquals(0, ret);
+    }
 }




reply via email to

[Prev in Thread] Current Thread [Next in Thread]