gnunet-svn
[Top][All Lists]
Advanced

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

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


From: gnunet
Subject: [GNUnet-SVN] r30387 - in gnunet-java: . src/main/java/org/gnunet/consensus src/main/java/org/gnunet/consensus/messages src/main/java/org/gnunet/identity src/main/java/org/gnunet/mesh src/main/java/org/gnunet/testbed src/main/java/org/gnunet/testing src/main/java/org/gnunet/transport src/main/java/org/gnunet/transport/callbacks src/main/java/org/gnunet/transport/messages src/main/java/org/gnunet/util src/main/java/org/gnunet/util/crypto src/main/java/org/gnunet/util/getopt 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 src/test/java/org/gnunet/consensus
Date: Mon, 21 Oct 2013 20:48:51 +0200

Author: dold
Date: 2013-10-21 20:48:51 +0200 (Mon, 21 Oct 2013)
New Revision: 30387

Added:
   gnunet-java/src/main/java/org/gnunet/consensus/ConsensusCallback.java
   gnunet-java/src/main/java/org/gnunet/consensus/messages/
   
gnunet-java/src/main/java/org/gnunet/consensus/messages/ConcludeDoneMessage.java
   gnunet-java/src/main/java/org/gnunet/consensus/messages/ConcludeMessage.java
   
gnunet-java/src/main/java/org/gnunet/consensus/messages/InsertElementMessage.java
   gnunet-java/src/main/java/org/gnunet/consensus/messages/JoinMessage.java
   
gnunet-java/src/main/java/org/gnunet/consensus/messages/NewElementMessage.java
   gnunet-java/src/main/java/org/gnunet/testbed/SimpleTestbed.java
   gnunet-java/src/main/java/org/gnunet/transport/AddressMonitor.java
   gnunet-java/src/main/java/org/gnunet/transport/HelloAddress.java
   gnunet-java/src/main/java/org/gnunet/transport/callbacks/
   
gnunet-java/src/main/java/org/gnunet/transport/callbacks/BlacklistCallback.java
   
gnunet-java/src/main/java/org/gnunet/transport/callbacks/HelloUpdateCallback.java
   
gnunet-java/src/main/java/org/gnunet/transport/callbacks/PeerAddressListCallback.java
   
gnunet-java/src/main/java/org/gnunet/transport/callbacks/PeerAddressMonitorCallback.java
   
gnunet-java/src/main/java/org/gnunet/transport/callbacks/PeerIterateCallback.java
   
gnunet-java/src/main/java/org/gnunet/transport/callbacks/TryConnectCallback.java
   gnunet-java/src/main/java/org/gnunet/transport/messages/
   
gnunet-java/src/main/java/org/gnunet/transport/messages/AddressIterateMessage.java
   
gnunet-java/src/main/java/org/gnunet/transport/messages/AddressIterateResponseMessage.java
   
gnunet-java/src/main/java/org/gnunet/transport/messages/AddressIterateResponseMessageContent.java
   
gnunet-java/src/main/java/org/gnunet/transport/messages/BlacklistInitMessage.java
   
gnunet-java/src/main/java/org/gnunet/transport/messages/BlacklistQueryMessage.java
   
gnunet-java/src/main/java/org/gnunet/transport/messages/BlacklistReplyMessage.java
   
gnunet-java/src/main/java/org/gnunet/transport/messages/RequestConnectMessage.java
   gnunet-java/src/main/java/org/gnunet/transport/messages/StartMessage.java
   gnunet-java/src/main/java/org/gnunet/util/CancellationToken.java
   gnunet-java/src/main/java/org/gnunet/voting/GroupCert.java
   gnunet-java/src/main/java/org/gnunet/voting/InvalidGroupCertException.java
   gnunet-java/src/main/resources/org/gnunet/voting/ballot-template.espec
   gnunet-java/src/test/java/org/gnunet/consensus/
   gnunet-java/src/test/java/org/gnunet/consensus/ConsensusSingleTest.java
   gnunet-java/src/test/java/org/gnunet/consensus/ConsensusTestbedTest.java
Removed:
   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/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/transport/AddressIterateMessage.java
   gnunet-java/src/main/java/org/gnunet/transport/BlacklistInitMessage.java
   gnunet-java/src/main/java/org/gnunet/transport/BlacklistQueryMessage.java
   gnunet-java/src/main/java/org/gnunet/transport/BlacklistReplyMessage.java
   gnunet-java/src/main/java/org/gnunet/transport/HelloUpdateCallback.java
   gnunet-java/src/main/java/org/gnunet/transport/PeerIterateCallback.java
   gnunet-java/src/main/java/org/gnunet/transport/RequestConnectMessage.java
   gnunet-java/src/main/java/org/gnunet/transport/StartMessage.java
   gnunet-java/src/main/java/org/gnunet/transport/TryConnectCallback.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/resources/org/gnunet/voting/template.espec
Modified:
   gnunet-java/
   gnunet-java/ISSUES
   gnunet-java/build.gradle
   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/identity/Identity.java
   gnunet-java/src/main/java/org/gnunet/mesh/Mesh.java
   gnunet-java/src/main/java/org/gnunet/mesh/TunnelDestroyMessage.java
   gnunet-java/src/main/java/org/gnunet/testbed/Controller.java
   gnunet-java/src/main/java/org/gnunet/testbed/ControllerProc.java
   gnunet-java/src/main/java/org/gnunet/testing/TestingSubsystem.java
   gnunet-java/src/main/java/org/gnunet/transport/Blacklist.java
   gnunet-java/src/main/java/org/gnunet/transport/Transport.java
   gnunet-java/src/main/java/org/gnunet/util/Configuration.java
   gnunet-java/src/main/java/org/gnunet/util/HashCode.java
   gnunet-java/src/main/java/org/gnunet/util/Helper.java
   gnunet-java/src/main/java/org/gnunet/util/MessageStreamTokenizer.java
   gnunet-java/src/main/java/org/gnunet/util/Program.java
   gnunet-java/src/main/java/org/gnunet/util/Scheduler.java
   gnunet-java/src/main/java/org/gnunet/util/Strings.java
   gnunet-java/src/main/java/org/gnunet/util/crypto/EcdsaPrivateKey.java
   gnunet-java/src/main/java/org/gnunet/util/crypto/EcdsaPublicKey.java
   gnunet-java/src/main/java/org/gnunet/util/crypto/EcdsaSignature.java
   gnunet-java/src/main/java/org/gnunet/util/crypto/EddsaSignature.java
   gnunet-java/src/main/java/org/gnunet/util/getopt/Parser.java
   gnunet-java/src/main/java/org/gnunet/voting/Ballot.java
   gnunet-java/src/main/java/org/gnunet/voting/BallotTool.java
   gnunet-java/src/main/java/org/gnunet/voting/CertifyGroupTool.java
   gnunet-java/src/main/java/org/gnunet/voting/TallyAuthorityDaemon.java
   gnunet-java/src/main/java/org/gnunet/voting/messages/SubmitMessage.java
   gnunet-java/src/main/resources/org/gnunet/construct/MsgMap.txt
Log:
- use identity in voting
- group certificate tool instead of ca daemon
- consensus api fully implemented, consensus+testbed test
- authorities use consensus
- some progress on transport api


Index: gnunet-java
===================================================================
--- gnunet-java 2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java 2013-10-21 18:48:51 UTC (rev 30387)

Property changes on: gnunet-java
___________________________________________________________________
Modified: svn:ignore
## -1,2 +1,3 ##
 build
 bin
+.swp
Modified: gnunet-java/ISSUES
===================================================================
--- gnunet-java/ISSUES  2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/ISSUES  2013-10-21 18:48:51 UTC (rev 30387)
@@ -1,35 +1,77 @@
-gnunet-java, user services and ports
+What now works:
+ * consensus+testbed testcase in pure Java
+ * group certificates and corresponding tool implemented
+ * authorities now use consensus (though only tested with one authority)
+ * ballot tool is now drastically simplified (one <400LOC file)
 
-$-expansion and XDG_CONFIG_DIR implemented
 
-crypto: please check that I didn't confuse anything:
+gnunet-ballot-group-certify [OPTIONS]...
+Create a certificate attesting group membership for a given key.
+The resulting certificate file written to standard output.
+  -c --config=FILENAME         path of the configuration file
+  -h --help                    print this help message
+  -v --version                 print version
+  -L --log=LOGLEVEL            configure logging to use LOGLEVEL
+  -l --logfile=LOGFILE         configure logging to write logs to LOGFILE
+  -e --ego=EGONAME             name of the identity ego to use for signing
+  -m --member=KEY              key to certify membership for
+  -g --group=NAME              group to certify membership of key for
+  -x --expire=DATE             expiration date, in local time
 
-For EdDSA:
-- This is quite clear: Use a curve equivalent to Curve25519, namely Ed25519 
(i.e. Curve25519 converted to a twisted edwards curve), where I already have 
the implementation in Java. Points will eventually be stored in compressed form 
where the x-coordinate is restored from the y-coordinate.
 
-For ECDHE:
-- This uses the "actual" Curve25519, which is a curve in Montgomery form. 
Thus, gnunet-java will have to implement the group law for Montgomery Curves, 
right? BUT: Are points in Montgomery form or in affine form?
+gnunet-ballot [OPTIONS]... BALLOT
+Create, modify and execute operation on ballots.
+  -c --config=FILENAME         path of the configuration file
+  -h --help                    print this help message
+  -v --version                 print version
+  -L --log=LOGLEVEL            configure logging to use LOGLEVEL
+  -l --logfile=LOGFILE         configure logging to write logs to LOGFILE
+  -e --ego                     ego to use for the operation
+  -q --query                   query election result
+  -s --submit                  submit the vote to the authorities
+  -r --register                register an election with the authorities (use 
with -e)
+  -i --issue                   sign the ballot as issuer (use with -e)
+  -x --select=CHOICE           select and encrypt a vote option (use with -e)
+  -V --verify                  verify signatures in the ballot and show 
information
+  -g --group                   incorporate the group cert into the ballot
+  -t --template                write a template ballot to the give ballot file
 
-- According to a mail you forwarded me by Tanja Lange (and the paper 
"Curve25519: new Diffie-Hellman speed records") points on a Montgomery Curve 
can also be represented by Montgomery Coordinates, which allows to use fast 
Montgomery Arithmetic with only the x-coordinate (losing the y-coordinate's 
sign, which does not matter for ECDHE). But, for ECDSA the y-coordinate matters 
...
 
-For deterministic ECDSA/RFC 6979:
-- I'm quite sure this also uses the Montgomery Curve25519, so I need to 
implement the full group law anyway ..
+ * as the ballot tool is not an API, doesn't it make most sense to have
+   voting tests as shellscripts?
 
-there's a non-free C implementation
-http://code.google.com/p/curve25519-donna/
+ * how should FOREVER be handled in config files?
 
-RFC 6979 has an example implementation ... in java!
- * I implemented my own, very short, but likely slower version
+Multiple authorities:
+ * what should happen when duplicate vote is detected? where do we "complain"?
+   (can't always detect this on submission, as two auth. might get different 
votes)
 
 
-voting:
- * how do you suggest testing voting when the crypto (GNUnet/gnunet-java) does 
not work
-   together yet?
-  * authorities should only store their public key in the ballot information, 
but then
-    mesh can't connect as the peer identity can't be derived.
+ * When is the threshold crypto set up? Don't we need another
+   time for when authorities start to set up the shared secret?
 
- * we agreed that authorities are peers, right? Thus they use EdDSA.
- * voters should use ECDSA (you vote with an ego)
- * CAs are also ECDSA (gns!)
- * what about the issuer?
 
+exceptions and static initializers: when moving a class and not running 
'gradle msgtypes',
+this nice message pops up:
+
+java.lang.NoClassDefFoundError: Could not initialize class 
org.gnunet.construct.MessageLoader
+       at 
org.gnunet.construct.OptionalUnionTest.setupMessageMap(OptionalUnionTest.java:36)
+       at 
org.gnunet.construct.OptionalUnionTest.test_optional_union1(OptionalUnionTest.java:42)
+       at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+       at 
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
+       at 
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
+       at java.lang.reflect.Method.invoke(Method.java:597)
+       at 
org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
+
+Any idea on how to improve the error reporting?
+
+question on testbed service disconnect adapter:
+ * why is this needed? I get the idea, namely that the operation of service 
connect
+   is "canceled" (by calling done) and then the code for disconnecting is 
called, but why wouldn't
+   I want to do this myself?
+
+
+* crypto mismatch is a bit annoying, when will libgcrypt be done?
+
+consensus+testbed test looks *horrible*, do you have any suggestions on how to 
improve?
+

Modified: gnunet-java/build.gradle
===================================================================
--- gnunet-java/build.gradle    2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/build.gradle    2013-10-21 18:48:51 UTC (rev 30387)
@@ -6,6 +6,9 @@
 
 buildDir = "$projectDir/build-gradle"
 
+sourceCompatibility = "1.6"
+targetCompatibility = "1.6"
+
 // specify where to get jars
 repositories {
   flatDir {

Deleted: gnunet-java/src/main/java/org/gnunet/consensus/ConcludeCallback.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/ConcludeCallback.java        
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/consensus/ConcludeCallback.java        
2013-10-21 18:48:51 UTC (rev 30387)
@@ -1,25 +0,0 @@
-/*
- 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 {
-    void onConcludeDone();
-}

Deleted: gnunet-java/src/main/java/org/gnunet/consensus/ConcludeDoneMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/ConcludeDoneMessage.java     
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/consensus/ConcludeDoneMessage.java     
2013-10-21 18:48:51 UTC (rev 30387)
@@ -1,33 +0,0 @@
-/*
- 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.MessageUnion;
-import org.gnunet.construct.UnionCase;
-
-/**
- * Notify the client that conclude has finished.
- * Direction: service -> client
- */
address@hidden(525)
-public class ConcludeDoneMessage implements MessageUnion {
-}

Deleted: gnunet-java/src/main/java/org/gnunet/consensus/ConcludeMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/ConcludeMessage.java 
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/consensus/ConcludeMessage.java 
2013-10-21 18:48:51 UTC (rev 30387)
@@ -1,39 +0,0 @@
-/*
- 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;
-import org.gnunet.construct.UInt16;
-import org.gnunet.construct.UInt8;
-import org.gnunet.construct.UnionCase;
-import org.gnunet.util.GnunetMessage;
-
-/**
- * Notify the client of a new element.
- *
- * Direction: service -> client
- *
- * @author Florian Dold
- */
address@hidden(524)
-public class ConcludeMessage implements GnunetMessage.Body {
-    /* empty body */
-}
\ No newline at end of file

Modified: gnunet-java/src/main/java/org/gnunet/consensus/Consensus.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/Consensus.java       
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/consensus/Consensus.java       
2013-10-21 18:48:51 UTC (rev 30387)
@@ -20,8 +20,9 @@
 
 package org.gnunet.consensus;
 
+import com.google.common.base.Preconditions;
+import org.gnunet.consensus.messages.*;
 import org.gnunet.mq.Envelope;
-import org.gnunet.mq.MessageQueue;
 import org.gnunet.mq.NotifySentHandler;
 import org.gnunet.util.*;
 import org.slf4j.Logger;
@@ -38,12 +39,6 @@
             .getLogger(Consensus.class);
 
     /**
-     * Callback for new elements arriving from the service.
-     * Also used to notify of consensus failure.
-     */
-    private final NewElementCallback newElementCallback;
-
-    /**
      * Client connected to the consensus service.
      */
     private Client client;
@@ -51,71 +46,82 @@
     /**
      * Called when conclude has finished.
      */
-    private ConcludeCallback concludeCallback;
+    private ConsensusCallback consensusCallback;
 
     /**
      * Message dispatch for messages from the consensus service.
      */
     private class ConsensusMessageReceiver extends RunaboutMessageReceiver {
         public void visit(ConcludeDoneMessage m) {
-            if (null == concludeCallback)
-            {
+            if (null == consensusCallback) {
                 logger.error("unexpected conclude done message");
                 return;
             }
-            concludeCallback.onConcludeDone();
+            consensusCallback.onDone();
         }
 
         public void visit(NewElementMessage m) {
-            ConsensusElement element = new ConsensusElement();
-            element.element_type = m.element_type;
-            element.data = m.element_data;
-            newElementCallback.onNewElement(element);
+            ConsensusElement element = new ConsensusElement(m.elementData, 
m.elementType);
+            element.elementType = m.elementType;
+            element.data = m.elementData;
+            consensusCallback.onElement(element);
         }
 
         @Override
         public void handleError() {
-            newElementCallback.onNewElement(null);
+            consensusCallback.onElement(null);
         }
     }
 
     /**
      * Create a consensus session.  The set being reconciled is initially
-     * empty.  Only reconcile with other peers after
-     * GNUNET_CONSENSUS_reconcile has been called.
+     * empty.
      *
-     * @param num_peers number of peers in the session
      * @param peers array of peers participating in this consensus session
      *              Inclusion of the local peer is optional.
      * @param sessionId session identifier
      *                   Allows a group of peers to have more than consensus 
session.
-     * @param newElementCallback callback, called when a new element is added 
to the set by
-     *                    another peer
      */
-    public Consensus(Configuration cfg, int num_peers, PeerIdentity[] peers, 
HashCode sessionId,
-                     NewElementCallback newElementCallback) {
+    public Consensus(Configuration cfg, PeerIdentity[] peers, HashCode 
sessionId) {
         client = new Client("consensus", cfg);
         client.installReceiver(new ConsensusMessageReceiver());
-        this.newElementCallback = newElementCallback;
+
+        JoinMessage m = new JoinMessage();
+        m.numPeers = peers.length;
+        m.peers = peers;
+        m.sessionId = sessionId;
+        client.send(m);
     }
 
+
     /**
      * Insert an element into the consensus set.
      *
-     * @param element element to insert in the consnesus
+     * @param element element to insert in the consensus
+     */
+    public void insertElement(ConsensusElement element) {
+        insertElement(element, null);
+    }
+
+    /**
+     * Insert an element into the consensus set.
+     *
+     * @param element element to insert in the consensus
      * @param idc called when the element has been sent to the service
      */
-    public void insertElement (ConsensusElement element, final 
InsertDoneCallback idc) {
+    public void insertElement(ConsensusElement element, final 
InsertDoneCallback idc) {
         InsertElementMessage m = new InsertElementMessage();
         m.element_data = element.data;
-        m.element_type = element.element_type;
+        m.element_type = element.elementType;
         Envelope ev = new Envelope(m);
-        ev.notifySent(new NotifySentHandler() {
+        if (null != idc) {
+            ev.notifySent(new NotifySentHandler() {
             @Override
             public void onSent() {
                 idc.onInsertDone();
             }
         });
+        }
         client.send(ev);
     }
 
@@ -124,15 +130,17 @@
      * try to conclude the consensus within a given time window.
      * After conclude has been called, no further elements may be
      * inserted by the client.
+     *
+     * @param concludeTimeout timeout for conclude, may never be forever
      * @param concludeCallback called when the consensus has concluded
      */
-    public void conclude(ConcludeCallback concludeCallback) {
-        if (null == concludeCallback)
-            throw new AssertionError("conclude with empty callback");
-        if (null != this.concludeCallback)
-            throw new AssertionError("called conclude twice");
-        this.concludeCallback = concludeCallback;
+    public void conclude(RelativeTime concludeTimeout, ConsensusCallback 
concludeCallback) {
+        Preconditions.checkNotNull(concludeCallback, "conclude with null 
callback");
+        Preconditions.checkState(null == this.consensusCallback, "called 
conclude twice");
+        Preconditions.checkArgument(!concludeTimeout.isForever(), "conclude 
timeout may not be forever");
+        this.consensusCallback = concludeCallback;
         ConcludeMessage m = new ConcludeMessage();
+        m.concludeTimeout = concludeTimeout.toNetwork();
         client.send(m);
     }
 

Added: gnunet-java/src/main/java/org/gnunet/consensus/ConsensusCallback.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/ConsensusCallback.java       
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/consensus/ConsensusCallback.java       
2013-10-21 18:48:51 UTC (rev 30387)
@@ -0,0 +1,26 @@
+/*
+ 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.consensus;
+
+public interface ConsensusCallback {
+    void onElement(ConsensusElement element);
+    void onDone();
+}

Modified: gnunet-java/src/main/java/org/gnunet/consensus/ConsensusElement.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/ConsensusElement.java        
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/consensus/ConsensusElement.java        
2013-10-21 18:48:51 UTC (rev 30387)
@@ -24,13 +24,18 @@
 public class ConsensusElement {
     /**
      * Type of the element.
-     * 0 <= element_type <= 2^16
+     * 0 <= elementType <= 2^16
      */
-    int element_type;
+    int elementType;
 
     /**
      * Data for the element.
      * 0 <= data.length <= 2^16
      */
-    byte[] data;
+    public byte[] data;
+
+    public ConsensusElement(byte[] data, int elementType) {
+        this.data = data;
+        this.elementType = elementType;
+    }
 }

Deleted: 
gnunet-java/src/main/java/org/gnunet/consensus/InsertElementMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/InsertElementMessage.java    
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/consensus/InsertElementMessage.java    
2013-10-21 18:48:51 UTC (rev 30387)
@@ -1,43 +0,0 @@
-/*
- 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;
-import org.gnunet.construct.UInt16;
-import org.gnunet.construct.UInt8;
-import org.gnunet.construct.UnionCase;
-import org.gnunet.util.GnunetMessage;
-
-/**
- * Send an element to the service, insert it into the consensus set.
- *
- * Direction: client -> service
- *
- * @author Florian Dold
- */
address@hidden(521)
-public class InsertElementMessage implements GnunetMessage.Body {
-    @UInt16
-    public int element_type;
-    @FillWith
-    @UInt8
-    public byte[] element_data;
-}
\ No newline at end of file

Deleted: gnunet-java/src/main/java/org/gnunet/consensus/NewElementCallback.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/NewElementCallback.java      
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/consensus/NewElementCallback.java      
2013-10-21 18:48:51 UTC (rev 30387)
@@ -1,25 +0,0 @@
-/*
- 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 {
-    void onNewElement(ConsensusElement element);
-}

Deleted: gnunet-java/src/main/java/org/gnunet/consensus/NewElementMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/NewElementMessage.java       
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/consensus/NewElementMessage.java       
2013-10-21 18:48:51 UTC (rev 30387)
@@ -1,40 +0,0 @@
-/*
- 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.*;
-import org.gnunet.util.GnunetMessage;
-
-/**
- * Notify the client of a new element.
- *
- * Direction: service -> client
- *
- * @author Florian Dold
- */
address@hidden(523)
-public class NewElementMessage implements GnunetMessage.Body {
-    @UInt16
-    public int element_type;
-    @FillWith
-    @UInt8
-    public byte[] element_data;
-}
\ No newline at end of file

Copied: 
gnunet-java/src/main/java/org/gnunet/consensus/messages/ConcludeDoneMessage.java
 (from rev 30348, 
gnunet-java/src/main/java/org/gnunet/consensus/ConcludeDoneMessage.java)
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/consensus/messages/ConcludeDoneMessage.java
                            (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/consensus/messages/ConcludeDoneMessage.java
    2013-10-21 18:48:51 UTC (rev 30387)
@@ -0,0 +1,34 @@
+/*
+ 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.messages;
+
+
+import org.gnunet.construct.MessageUnion;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
+/**
+ * Notify the client that conclude has finished.
+ * Direction: service -> client
+ */
address@hidden(525)
+public class ConcludeDoneMessage implements GnunetMessage.Body {
+}

Copied: 
gnunet-java/src/main/java/org/gnunet/consensus/messages/ConcludeMessage.java 
(from rev 30348, 
gnunet-java/src/main/java/org/gnunet/consensus/ConcludeMessage.java)
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/consensus/messages/ConcludeMessage.java    
                            (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/consensus/messages/ConcludeMessage.java    
    2013-10-21 18:48:51 UTC (rev 30387)
@@ -0,0 +1,40 @@
+/*
+ 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.messages;
+
+import org.gnunet.construct.*;
+import org.gnunet.util.AbsoluteTime;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.RelativeTime;
+import org.gnunet.util.RelativeTimeMessage;
+
+/**
+ * Notify the client of a new element.
+ *
+ * Direction: service -> client
+ *
+ * @author Florian Dold
+ */
address@hidden(524)
+public class ConcludeMessage implements GnunetMessage.Body {
+    @NestedMessage
+    public RelativeTimeMessage concludeTimeout;
+}
\ No newline at end of file

Copied: 
gnunet-java/src/main/java/org/gnunet/consensus/messages/InsertElementMessage.java
 (from rev 30348, 
gnunet-java/src/main/java/org/gnunet/consensus/InsertElementMessage.java)
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/consensus/messages/InsertElementMessage.java
                           (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/consensus/messages/InsertElementMessage.java
   2013-10-21 18:48:51 UTC (rev 30387)
@@ -0,0 +1,43 @@
+/*
+ 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.messages;
+
+import org.gnunet.construct.FillWith;
+import org.gnunet.construct.UInt16;
+import org.gnunet.construct.UInt8;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
+/**
+ * Send an element to the service, insert it into the consensus set.
+ *
+ * Direction: client -> service
+ *
+ * @author Florian Dold
+ */
address@hidden(521)
+public class InsertElementMessage implements GnunetMessage.Body {
+    @UInt16
+    public int element_type;
+    @FillWith
+    @UInt8
+    public byte[] element_data;
+}
\ No newline at end of file

Added: gnunet-java/src/main/java/org/gnunet/consensus/messages/JoinMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/messages/JoinMessage.java    
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/consensus/messages/JoinMessage.java    
2013-10-21 18:48:51 UTC (rev 30387)
@@ -0,0 +1,39 @@
+/*
+ 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.consensus.messages;
+
+import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.construct.VariableSizeArray;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.HashCode;
+import org.gnunet.util.PeerIdentity;
+
address@hidden(520)
+public class JoinMessage implements GnunetMessage.Body {
+    @UInt32
+    public int numPeers;
+    @NestedMessage
+    public HashCode sessionId;
+    @VariableSizeArray(lengthField = "numPeers")
+    public PeerIdentity[] peers;
+}

Copied: 
gnunet-java/src/main/java/org/gnunet/consensus/messages/NewElementMessage.java 
(from rev 30348, 
gnunet-java/src/main/java/org/gnunet/consensus/NewElementMessage.java)
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/consensus/messages/NewElementMessage.java  
                            (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/consensus/messages/NewElementMessage.java  
    2013-10-21 18:48:51 UTC (rev 30387)
@@ -0,0 +1,40 @@
+/*
+ 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.messages;
+
+import org.gnunet.construct.*;
+import org.gnunet.util.GnunetMessage;
+
+/**
+ * Notify the client of a new element.
+ *
+ * Direction: service -> client
+ *
+ * @author Florian Dold
+ */
address@hidden(523)
+public class NewElementMessage implements GnunetMessage.Body {
+    @UInt16
+    public int elementType;
+    @FillWith
+    @UInt8
+    public byte[] elementData;
+}
\ No newline at end of file

Modified: gnunet-java/src/main/java/org/gnunet/identity/Identity.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/identity/Identity.java 2013-10-21 
17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/identity/Identity.java 2013-10-21 
18:48:51 UTC (rev 30387)
@@ -45,6 +45,66 @@
 
     private static Ego anonymousEgo;
 
+    public static Cancelable lookup(Configuration configuration,
+                                    final String egoName, final 
IdentityCallback identityCallback) {
+        final Identity myIdentity = new Identity();
+        final CancellationToken cancellationToken = new CancellationToken();
+        myIdentity.connect(configuration, new IdentityListCallback() {
+            boolean done = false;
+            @Override
+            public void onEgoAdd(Ego ego) {
+                if (done) {
+                    throw new AssertionError();
+                }
+                if (ego.name.equals(egoName)) {
+                    done = true;
+                    cancellationToken.cancel();
+                    identityCallback.onEgo(ego);
+                }
+            }
+
+            @Override
+            public void onEgoDelete(Ego ego) {
+                if (done) {
+                    throw new AssertionError();
+                }
+                logger.error("unsolicited ego delete on lookup");
+                done = true;
+                cancellationToken.cancel();
+                identityCallback.onError("identity service misbehaved");
+            }
+
+            @Override
+            public void onEgoRename(String oldName, Ego ego) {
+                if (done) {
+                    throw new AssertionError();
+                }
+                logger.error("unsolicited ego rename on lookup");
+                done = true;
+                cancellationToken.cancel();
+                identityCallback.onError("identity service misbehaved");
+            }
+
+            @Override
+            public void onListEnd() {
+                if (done) {
+                    throw new AssertionError();
+                }
+                done = true;
+                cancellationToken.cancel();
+                identityCallback.onError("identity not found");
+
+            }
+        });
+        cancellationToken.add(new Cancelable() {
+            @Override
+            public void cancel() {
+                myIdentity.disconnect();
+            }
+        });
+        return cancellationToken;
+    }
+
     public static class Ego {
         private String name;
         private EcdsaPrivateKey privateKey;

Modified: gnunet-java/src/main/java/org/gnunet/mesh/Mesh.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/mesh/Mesh.java 2013-10-21 17:20:42 UTC 
(rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/mesh/Mesh.java 2013-10-21 18:48:51 UTC 
(rev 30387)
@@ -188,7 +188,7 @@
             if (!destroyedByService) {
                 TunnelDestroyMessage m = new TunnelDestroyMessage();
                 m.tunnel_id = tunnelId;
-                m.reserved = new byte[32];
+                m.reserved = new byte[64];
                 client.send(m);
             }
             tunnelMap.remove(tunnelId);

Modified: gnunet-java/src/main/java/org/gnunet/mesh/TunnelDestroyMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/mesh/TunnelDestroyMessage.java 
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/mesh/TunnelDestroyMessage.java 
2013-10-21 18:48:51 UTC (rev 30387)
@@ -17,7 +17,7 @@
     @UInt32
     public long tunnel_id;
 
-    @FixedSizeIntegerArray(bitSize = 8, signed = false, length = 32)
+    @FixedSizeIntegerArray(bitSize = 8, signed = false, length = 64)
     public byte[] reserved;
     @UInt32
     public int port;

Modified: gnunet-java/src/main/java/org/gnunet/testbed/Controller.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testbed/Controller.java        
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/testbed/Controller.java        
2013-10-21 18:48:51 UTC (rev 30387)
@@ -24,10 +24,7 @@
 import org.gnunet.requests.RequestContainer;
 import org.gnunet.testbed.callbacks.*;
 import org.gnunet.testbed.messages.*;
-import org.gnunet.util.Cancelable;
-import org.gnunet.util.Client;
-import org.gnunet.util.Configuration;
-import org.gnunet.util.RunaboutMessageReceiver;
+import org.gnunet.util.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -557,8 +554,13 @@
          * @param serviceAdapter callback object for connection establishment 
and tear-down.
          * @return handle for the operation
          */
-        public Cancelable getServiceConnection(String serviceName, 
ServiceAdapter serviceAdapter) {
-            return null;
+        public Cancelable getServiceConnection(String serviceName, final 
ServiceAdapter serviceAdapter) {
+            return requestInformation(new PeerInformationCallback() {
+                @Override
+                public void onSuccess(PeerIdentity peerIdentity, Configuration 
configuration) {
+                    serviceAdapter.onConnect(configuration);
+                }
+            });
         }
 
         /**

Modified: gnunet-java/src/main/java/org/gnunet/testbed/ControllerProc.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testbed/ControllerProc.java    
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/testbed/ControllerProc.java    
2013-10-21 18:48:51 UTC (rev 30387)
@@ -43,7 +43,7 @@
     /**
      * Create a controller proc. Nothing will hapen until ControllerProc.start 
is called.
      */
-    public void ControllerProc() {
+    public ControllerProc() {
         // empty
     }
 
@@ -73,8 +73,6 @@
         this.host = host;
         if (host.isLocal()) {
             List<String> args = new ArrayList<String>();
-            args.add("-llog-testbed-helper");
-            args.add("-LDEBUG");
             helper = new Helper(false, "gnunet-helper-testbed", args, new 
ControllerProcReceiver());
         } else {
             throw new AssertionError("not implemented yet");

Added: gnunet-java/src/main/java/org/gnunet/testbed/SimpleTestbed.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testbed/SimpleTestbed.java             
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/testbed/SimpleTestbed.java     
2013-10-21 18:48:51 UTC (rev 30387)
@@ -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;
+
+/**
+ * Helper class for running a "simple" testbed.
+ *
+ */
+public abstract class SimpleTestbed {
+    public SimpleTestbed(String configFilename, int numPeers) {
+
+    }
+
+    protected abstract void onRun(Controller.Peer[] peers);
+
+}

Modified: gnunet-java/src/main/java/org/gnunet/testing/TestingSubsystem.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testing/TestingSubsystem.java  
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/testing/TestingSubsystem.java  
2013-10-21 18:48:51 UTC (rev 30387)
@@ -50,8 +50,18 @@
     }
 
     public TestingSubsystem(String service) {
+        this(service, null);
+    }
+
+    public TestingSubsystem(String service, String configFilename) {
+        final String[] args;
+        if (configFilename == null) {
+            args = new String[]{"gnunet-testing", "-r", service};
+        } else {
+            args = new String[]{"gnunet-testing", "-r", service, "-c", 
configFilename};
+        }
         try {
-            p = Runtime.getRuntime().exec(new String[]{"gnunet-testing", "-r", 
service});
+            p = Runtime.getRuntime().exec(args);
         } catch (IOException e) {
             throw new TestingSetup.SetupException(e);
         }
@@ -71,7 +81,6 @@
             throw new TestingSetup.SetupException("could not start service ('" 
+ started + "')");
         }
 
-
         String cfgFileName;
         try {
             cfgFileName = reader.readLine();

Deleted: 
gnunet-java/src/main/java/org/gnunet/transport/AddressIterateMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/transport/AddressIterateMessage.java   
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/transport/AddressIterateMessage.java   
2013-10-21 18:48:51 UTC (rev 30387)
@@ -1,35 +0,0 @@
-package org.gnunet.transport;
-
-
-import org.gnunet.construct.NestedMessage;
-import org.gnunet.construct.UInt32;
-import org.gnunet.construct.UnionCase;
-import org.gnunet.util.AbsoluteTime;
-import org.gnunet.util.GnunetMessage;
-import org.gnunet.util.PeerIdentity;
-
-
-/**
- * Message from the client to the transport service
- * asking for binary addresses known for a peer.
- */
address@hidden(380)
-public class AddressIterateMessage implements GnunetMessage.Body {
-    /**
-     * One shot call or continous replies?
-     */
-    @UInt32
-    public int one_shot;
-
-    /**
-     * FIXME: This field seems to be deprecated in the C API?
-     */
-    @NestedMessage
-    public AbsoluteTime timeout;
-
-    /**
-     * The identity of the peer to look up.
-     */
-    @NestedMessage
-    public PeerIdentity peer;
-}

Added: gnunet-java/src/main/java/org/gnunet/transport/AddressMonitor.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/transport/AddressMonitor.java          
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/transport/AddressMonitor.java  
2013-10-21 18:48:51 UTC (rev 30387)
@@ -0,0 +1,153 @@
+/*
+ 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.transport;
+
+import org.gnunet.transport.callbacks.PeerAddressListCallback;
+import org.gnunet.transport.callbacks.PeerAddressMonitorCallback;
+import org.gnunet.transport.messages.AddressIterateMessage;
+import org.gnunet.transport.messages.AddressIterateResponseMessage;
+import org.gnunet.transport.messages.BlacklistInitMessage;
+import org.gnunet.util.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Get active addresses of peers.
+ */
+public class AddressMonitor {
+    private static final Logger logger = LoggerFactory
+            .getLogger(AddressMonitor.class);
+    private final boolean oneShot;
+    private final PeerIdentity peerIdentity;
+    private final PeerAddressMonitorCallback monitorCallback;
+    private final PeerAddressListCallback listCallback;
+    /**
+     * Client connecting to the transport service.
+     */
+    private Client client;
+
+
+    private final class AddressMonitorReceiver extends RunaboutMessageReceiver 
{
+        public void visit(AddressIterateResponseMessage m) {
+            if (m.content == null) {
+                // uglyness in the api, when using one-shot the service
+                // may send a second message indicating end of list.
+                if (listCallback != null) {
+                    listCallback.onDone();
+                } else {
+                    client.reconnect();
+                    sendInitMessage();
+                }
+                return;
+            }
+            if (listCallback != null) {
+                if (m.content.addrlen == 0 && m.content.pluginlen == 0) {
+                    logger.warn("empty peer address list item");
+                } else {
+                    HelloAddress helloAddress = new HelloAddress();
+                    helloAddress.peer = m.content.peerIdentity;
+                    // FIXME: address and plugin
+                    listCallback.onPeerAddress(helloAddress);
+                }
+            } else {
+                if (m.content.addrlen == 0 && m.content.pluginlen == 0) {
+                    monitorCallback.onPeerDisconnect(m.content.peerIdentity);
+                } else {
+                    HelloAddress helloAddress = new HelloAddress();
+                    helloAddress.peer = m.content.peerIdentity;
+                    // FIXME: address and plugin
+                    monitorCallback.onPeerAddress(helloAddress);
+                }
+            }
+        }
+        @Override
+        public void handleError() {
+            client.reconnect();
+            client.send(new BlacklistInitMessage());
+        }
+    }
+
+    /**
+     *
+     * @param configuration configuration to use for connecting to
+     *                      the transport service
+     * @param peerIdentity peer identity to monitor addresses of, null
+     *                     to monitor addresses of all connected peers
+     * @param peerAddressCallback callback to call when receiving an address
+     *                            for the specified peer
+     */
+    public AddressMonitor(Configuration configuration,
+                          PeerIdentity peerIdentity,
+                          PeerAddressMonitorCallback peerAddressCallback) {
+        this.oneShot = false;
+        this.peerIdentity = peerIdentity;
+        this.monitorCallback = peerAddressCallback;
+        this.listCallback = null;
+
+        client = new Client("transport", configuration);
+        client.send(new BlacklistInitMessage());
+        client.installReceiver(new AddressMonitorReceiver());
+
+        createAndInitClient(configuration);
+    }
+
+    /**
+     *
+     * @param configuration configuration to use for connecting to
+     *                      the transport service
+     * @param peerIdentity peer identity to monitor addresses of, null
+     *                     to monitor addresses of all connected peers
+     * @param listCallback callback to call when receiving an address
+     *                            for the specified peer
+     */
+    public AddressMonitor(Configuration configuration,
+                          PeerIdentity peerIdentity,
+                          PeerAddressListCallback listCallback) {
+        this.oneShot = true;
+        this.peerIdentity = peerIdentity;
+        this.listCallback = listCallback;
+        this.monitorCallback = null;
+
+        createAndInitClient(configuration);
+
+    }
+
+    private void createAndInitClient(Configuration configuration) {
+        client = new Client("transport", configuration);
+        client.send(new BlacklistInitMessage());
+        client.installReceiver(new AddressMonitorReceiver());
+        sendInitMessage();
+    }
+
+    private void sendInitMessage() {
+        AddressIterateMessage m = new AddressIterateMessage();
+        m.oneShot = oneShot;
+        if (peerIdentity == null) {
+            m.peer = new PeerIdentity();
+        } else {
+            m.peer = peerIdentity;
+        }
+        // value seems to be deprecated in the C api, we're not using
+        // service-managed timeouts anyway, so send FOREVER
+        m.timeout = AbsoluteTime.FOREVER.asMessage();
+        client.send(m);
+    }
+}

Modified: gnunet-java/src/main/java/org/gnunet/transport/Blacklist.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/transport/Blacklist.java       
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/transport/Blacklist.java       
2013-10-21 18:48:51 UTC (rev 30387)
@@ -1,22 +1,29 @@
 package org.gnunet.transport;
 
+import org.gnunet.transport.callbacks.BlacklistCallback;
+import org.gnunet.transport.messages.BlacklistInitMessage;
+import org.gnunet.transport.messages.BlacklistQueryMessage;
+import org.gnunet.transport.messages.BlacklistReplyMessage;
 import org.gnunet.util.Client;
 import org.gnunet.util.Configuration;
-import org.gnunet.util.PeerIdentity;
 import org.gnunet.util.RunaboutMessageReceiver;
 
 /**
  * Transport blacklist.
  */
-public abstract class Blacklist {
+public class Blacklist {
     /**
+     * Callback that decided whether to accept or reject peers.
+     */
+    private final BlacklistCallback blacklistCallback;
+    /**
      * Client connecting to the transport service.
      */
     private Client client;
 
-    private final class TransportReceiver extends RunaboutMessageReceiver {
+    private final class TransportBlacklistReceiver extends 
RunaboutMessageReceiver {
         void visit(BlacklistQueryMessage m) {
-            boolean allowed = isAllowed(m.peer);
+            boolean allowed = blacklistCallback.checkAllowed(m.peer);
             BlacklistReplyMessage mr = new BlacklistReplyMessage();
             mr.is_allowed = allowed;
             mr.peer = m.peer;
@@ -40,16 +47,15 @@
      * whitelisted again.  This is the only way to re-enable
      * connections from peers that were previously blacklisted.
      */
-    public Blacklist(Configuration cfg) {
-        client = new Client("transport", cfg);
+    public Blacklist(Configuration configuration, BlacklistCallback 
blacklistCallback) {
+        this.blacklistCallback = blacklistCallback;
+        client = new Client("transport", configuration);
         client.send(new BlacklistInitMessage());
-        client.installReceiver(new TransportReceiver());
+        client.installReceiver(new TransportBlacklistReceiver());
     }
 
     public void destroy() {
         client.disconnect();
         client = null;
     }
-
-    protected abstract boolean isAllowed(PeerIdentity peer);
 }

Deleted: 
gnunet-java/src/main/java/org/gnunet/transport/BlacklistInitMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/transport/BlacklistInitMessage.java    
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/transport/BlacklistInitMessage.java    
2013-10-21 18:48:51 UTC (rev 30387)
@@ -1,9 +0,0 @@
-package org.gnunet.transport;
-
-import org.gnunet.construct.UnionCase;
-import org.gnunet.util.GnunetMessage;
-
address@hidden(369)
-public class BlacklistInitMessage implements GnunetMessage.Body {
-    // message body is empty
-}

Deleted: 
gnunet-java/src/main/java/org/gnunet/transport/BlacklistQueryMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/transport/BlacklistQueryMessage.java   
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/transport/BlacklistQueryMessage.java   
2013-10-21 18:48:51 UTC (rev 30387)
@@ -1,16 +0,0 @@
-package org.gnunet.transport;
-
-import org.gnunet.construct.NestedMessage;
-import org.gnunet.construct.UInt32;
-import org.gnunet.construct.UnionCase;
-import org.gnunet.util.GnunetMessage;
-import org.gnunet.util.PeerIdentity;
-
address@hidden(370)
-public class BlacklistQueryMessage implements GnunetMessage.Body {
-    @UInt32
-    public byte reserved;
-
-    @NestedMessage
-    public PeerIdentity peer;
-}

Deleted: 
gnunet-java/src/main/java/org/gnunet/transport/BlacklistReplyMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/transport/BlacklistReplyMessage.java   
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/transport/BlacklistReplyMessage.java   
2013-10-21 18:48:51 UTC (rev 30387)
@@ -1,16 +0,0 @@
-package org.gnunet.transport;
-
-import org.gnunet.construct.NestedMessage;
-import org.gnunet.construct.UInt32;
-import org.gnunet.construct.UnionCase;
-import org.gnunet.util.GnunetMessage;
-import org.gnunet.util.PeerIdentity;
-
address@hidden(371)
-public class BlacklistReplyMessage implements GnunetMessage.Body {
-    @UInt32
-    public boolean is_allowed;
-
-    @NestedMessage
-    public PeerIdentity peer;
-}

Added: gnunet-java/src/main/java/org/gnunet/transport/HelloAddress.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/transport/HelloAddress.java            
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/transport/HelloAddress.java    
2013-10-21 18:48:51 UTC (rev 30387)
@@ -0,0 +1,27 @@
+/*
+ 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.transport;
+
+import org.gnunet.util.PeerIdentity;
+
+public class HelloAddress {
+    public PeerIdentity peer;
+}

Deleted: gnunet-java/src/main/java/org/gnunet/transport/HelloUpdateCallback.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/transport/HelloUpdateCallback.java     
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/transport/HelloUpdateCallback.java     
2013-10-21 18:48:51 UTC (rev 30387)
@@ -1,9 +0,0 @@
-package org.gnunet.transport;
-
-/**
- * ...
- *
- * @author Florian Dold
- */
-public class HelloUpdateCallback {
-}

Deleted: gnunet-java/src/main/java/org/gnunet/transport/PeerIterateCallback.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/transport/PeerIterateCallback.java     
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/transport/PeerIterateCallback.java     
2013-10-21 18:48:51 UTC (rev 30387)
@@ -1,12 +0,0 @@
-package org.gnunet.transport;
-
-import org.gnunet.util.PeerIdentity;
-
-/**
- * ...
- *
- * @author Florian Dold
- */
-public interface PeerIterateCallback {
-    void processPeerAddress(PeerIdentity peer, Object hello);
-}

Deleted: 
gnunet-java/src/main/java/org/gnunet/transport/RequestConnectMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/transport/RequestConnectMessage.java   
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/transport/RequestConnectMessage.java   
2013-10-21 18:48:51 UTC (rev 30387)
@@ -1,24 +0,0 @@
-package org.gnunet.transport;
-
-import org.gnunet.construct.NestedMessage;
-import org.gnunet.construct.UInt32;
-import org.gnunet.construct.UnionCase;
-import org.gnunet.util.GnunetMessage;
-import org.gnunet.util.PeerIdentity;
-
-/**
- * ...
- *
- * @author Florian Dold
- */
address@hidden(374)
-public class RequestConnectMessage implements GnunetMessage.Body {
-    @UInt32
-    public int reserved;
-
-    /**
-     * Identity of the peer we would like to connect to.
-     */
-    @NestedMessage
-    public PeerIdentity peer;
-}

Deleted: gnunet-java/src/main/java/org/gnunet/transport/StartMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/transport/StartMessage.java    
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/transport/StartMessage.java    
2013-10-21 18:48:51 UTC (rev 30387)
@@ -1,32 +0,0 @@
-package org.gnunet.transport;
-
-import org.gnunet.construct.NestedMessage;
-import org.gnunet.construct.UInt32;
-import org.gnunet.construct.UnionCase;
-import org.gnunet.util.GnunetMessage;
-import org.gnunet.util.PeerIdentity;
-
-/**
- * ...
- *
- * @author Florian Dold
- */
address@hidden(360)
-public class StartMessage implements GnunetMessage.Body {
-    /**
-     * 0: no options
-     * 1: The 'self' field should be checked
-     * 2: this client is interested in payload traffic
-     */
-    @UInt32
-    public int options;
-
-    /**
-     * Identity we think we have.  If it does not match, the
-     * receiver should print out an error message and disconnect.
-     */
-    @NestedMessage
-    public PeerIdentity self;
-
-
-}

Modified: gnunet-java/src/main/java/org/gnunet/transport/Transport.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/transport/Transport.java       
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/transport/Transport.java       
2013-10-21 18:48:51 UTC (rev 30387)
@@ -3,6 +3,10 @@
 import org.gnunet.hello.HelloMessage;
 import org.gnunet.mq.Envelope;
 import org.gnunet.mq.NotifySentHandler;
+import org.gnunet.transport.callbacks.HelloUpdateCallback;
+import org.gnunet.transport.callbacks.PeerIterateCallback;
+import org.gnunet.transport.callbacks.TryConnectCallback;
+import org.gnunet.transport.messages.RequestConnectMessage;
 import org.gnunet.util.*;
 
 /**
@@ -17,7 +21,7 @@
      */
     private final Client client;
 
-    boolean init_requested;
+    boolean initRequested;
 
     private final class TransportReceiver extends RunaboutMessageReceiver {
         @Override
@@ -108,7 +112,7 @@
 
     /**
      * Return all the known addresses for a specific peer or all peers.
-     * Returns continuously all address if one_shot is set to false
+     * Returns continuously all address if oneShot is set to false
      * <p/>
      * Returns the address(es) that we are currently using for this
      * peer.  Upon completion, the 'AddressLookUpCallback' is called one more
@@ -118,7 +122,7 @@
      * @param peer                      peer identity to look up the addresses 
of, CHANGE: allow NULL for all (connected) peers
      * @param one_shot                  GNUNET_YES to return the current state 
and then end (with NULL+NULL),
      *                                  GNUNET_NO to monitor the set of 
addresses used (continuously, must be explicitly canceled)
-     * @param timeout                   how long is the lookup allowed to take 
at most (irrelevant if one_shot is set to GNUNET_NO)
+     * @param timeout                   how long is the lookup allowed to take 
at most (irrelevant if oneShot is set to GNUNET_NO)
      * @param peer_address_callback     function to call with the results
      */
     Cancelable getActiveAddresses(PeerIdentity peer, int one_shot,

Deleted: gnunet-java/src/main/java/org/gnunet/transport/TryConnectCallback.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/transport/TryConnectCallback.java      
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/transport/TryConnectCallback.java      
2013-10-21 18:48:51 UTC (rev 30387)
@@ -1,10 +0,0 @@
-package org.gnunet.transport;
-
-/**
- * ...
- *
- * @author Florian Dold
- */
-public interface TryConnectCallback {
-    void onDone();
-}

Added: 
gnunet-java/src/main/java/org/gnunet/transport/callbacks/BlacklistCallback.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/transport/callbacks/BlacklistCallback.java 
                            (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/transport/callbacks/BlacklistCallback.java 
    2013-10-21 18:48:51 UTC (rev 30387)
@@ -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.transport.callbacks;
+
+
+import org.gnunet.util.PeerIdentity;
+
+public interface BlacklistCallback {
+    boolean checkAllowed(PeerIdentity peer);
+}

Copied: 
gnunet-java/src/main/java/org/gnunet/transport/callbacks/HelloUpdateCallback.java
 (from rev 30348, 
gnunet-java/src/main/java/org/gnunet/transport/HelloUpdateCallback.java)
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/transport/callbacks/HelloUpdateCallback.java
                           (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/transport/callbacks/HelloUpdateCallback.java
   2013-10-21 18:48:51 UTC (rev 30387)
@@ -0,0 +1,9 @@
+package org.gnunet.transport.callbacks;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public interface HelloUpdateCallback {
+}

Added: 
gnunet-java/src/main/java/org/gnunet/transport/callbacks/PeerAddressListCallback.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/transport/callbacks/PeerAddressListCallback.java
                               (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/transport/callbacks/PeerAddressListCallback.java
       2013-10-21 18:48:51 UTC (rev 30387)
@@ -0,0 +1,50 @@
+/*
+ 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.transport.callbacks;
+
+
+import org.gnunet.transport.HelloAddress;
+import org.gnunet.util.PeerIdentity;
+
+public interface PeerAddressListCallback {
+    void onPeerAddress(HelloAddress helloAddress);
+    void onDone();
+}

Added: 
gnunet-java/src/main/java/org/gnunet/transport/callbacks/PeerAddressMonitorCallback.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/transport/callbacks/PeerAddressMonitorCallback.java
                            (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/transport/callbacks/PeerAddressMonitorCallback.java
    2013-10-21 18:48:51 UTC (rev 30387)
@@ -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.transport.callbacks;
+
+
+import org.gnunet.transport.HelloAddress;
+import org.gnunet.util.PeerIdentity;
+
+public interface PeerAddressMonitorCallback {
+    void onPeerAddress(HelloAddress helloAddress);
+    void onPeerDisconnect(PeerIdentity peerIdentity);
+}

Copied: 
gnunet-java/src/main/java/org/gnunet/transport/callbacks/PeerIterateCallback.java
 (from rev 30348, 
gnunet-java/src/main/java/org/gnunet/transport/PeerIterateCallback.java)
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/transport/callbacks/PeerIterateCallback.java
                           (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/transport/callbacks/PeerIterateCallback.java
   2013-10-21 18:48:51 UTC (rev 30387)
@@ -0,0 +1,12 @@
+package org.gnunet.transport.callbacks;
+
+import org.gnunet.util.PeerIdentity;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public interface PeerIterateCallback {
+    void processPeerAddress(PeerIdentity peer, Object hello);
+}

Copied: 
gnunet-java/src/main/java/org/gnunet/transport/callbacks/TryConnectCallback.java
 (from rev 30348, 
gnunet-java/src/main/java/org/gnunet/transport/TryConnectCallback.java)
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/transport/callbacks/TryConnectCallback.java
                            (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/transport/callbacks/TryConnectCallback.java
    2013-10-21 18:48:51 UTC (rev 30387)
@@ -0,0 +1,10 @@
+package org.gnunet.transport.callbacks;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public interface TryConnectCallback {
+    void onDone();
+}

Copied: 
gnunet-java/src/main/java/org/gnunet/transport/messages/AddressIterateMessage.java
 (from rev 30348, 
gnunet-java/src/main/java/org/gnunet/transport/AddressIterateMessage.java)
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/transport/messages/AddressIterateMessage.java
                          (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/transport/messages/AddressIterateMessage.java
  2013-10-21 18:48:51 UTC (rev 30387)
@@ -0,0 +1,36 @@
+package org.gnunet.transport.messages;
+
+
+import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.AbsoluteTime;
+import org.gnunet.util.AbsoluteTimeMessage;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.PeerIdentity;
+
+
+/**
+ * Message from the client to the transport service
+ * asking for binary addresses known for a peer.
+ */
address@hidden(380)
+public class AddressIterateMessage implements GnunetMessage.Body {
+    /**
+     * One shot call or continous replies?
+     */
+    @UInt32
+    public boolean oneShot;
+
+    /**
+     * FIXME: This field seems to be deprecated in the C API?
+     */
+    @NestedMessage
+    public AbsoluteTimeMessage timeout;
+
+    /**
+     * The identity of the peer to look up.
+     */
+    @NestedMessage
+    public PeerIdentity peer;
+}

Added: 
gnunet-java/src/main/java/org/gnunet/transport/messages/AddressIterateResponseMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/transport/messages/AddressIterateResponseMessage.java
                          (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/transport/messages/AddressIterateResponseMessage.java
  2013-10-21 18:48:51 UTC (rev 30387)
@@ -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.transport.messages;
+
+import org.gnunet.construct.IntegerFill;
+import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.PeerIdentity;
+
address@hidden(383)
+public class AddressIterateResponseMessage implements GnunetMessage.Body {
+    @NestedMessage(optional = true)
+    public AddressIterateResponseMessageContent content;
+}

Added: 
gnunet-java/src/main/java/org/gnunet/transport/messages/AddressIterateResponseMessageContent.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/transport/messages/AddressIterateResponseMessageContent.java
                           (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/transport/messages/AddressIterateResponseMessageContent.java
   2013-10-21 18:48:51 UTC (rev 30387)
@@ -0,0 +1,57 @@
+/*
+ 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.transport.messages;
+
+import org.gnunet.construct.*;
+import org.gnunet.util.PeerIdentity;
+
+public class AddressIterateResponseMessageContent implements Message {
+    @UInt32
+    public int reserved;
+    @NestedMessage
+    public PeerIdentity peerIdentity;
+    @UInt32
+    public int addrlen;
+    @UInt32
+    public int pluginlen;
+    @IntegerFill(bitSize = 8, signed = true)
+    public byte[] rest;
+}

Copied: 
gnunet-java/src/main/java/org/gnunet/transport/messages/BlacklistInitMessage.java
 (from rev 30348, 
gnunet-java/src/main/java/org/gnunet/transport/BlacklistInitMessage.java)
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/transport/messages/BlacklistInitMessage.java
                           (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/transport/messages/BlacklistInitMessage.java
   2013-10-21 18:48:51 UTC (rev 30387)
@@ -0,0 +1,9 @@
+package org.gnunet.transport.messages;
+
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
address@hidden(369)
+public class BlacklistInitMessage implements GnunetMessage.Body {
+    // message body is empty
+}

Copied: 
gnunet-java/src/main/java/org/gnunet/transport/messages/BlacklistQueryMessage.java
 (from rev 30348, 
gnunet-java/src/main/java/org/gnunet/transport/BlacklistQueryMessage.java)
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/transport/messages/BlacklistQueryMessage.java
                          (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/transport/messages/BlacklistQueryMessage.java
  2013-10-21 18:48:51 UTC (rev 30387)
@@ -0,0 +1,16 @@
+package org.gnunet.transport.messages;
+
+import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.PeerIdentity;
+
address@hidden(370)
+public class BlacklistQueryMessage implements GnunetMessage.Body {
+    @UInt32
+    public byte reserved;
+
+    @NestedMessage
+    public PeerIdentity peer;
+}

Copied: 
gnunet-java/src/main/java/org/gnunet/transport/messages/BlacklistReplyMessage.java
 (from rev 30348, 
gnunet-java/src/main/java/org/gnunet/transport/BlacklistReplyMessage.java)
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/transport/messages/BlacklistReplyMessage.java
                          (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/transport/messages/BlacklistReplyMessage.java
  2013-10-21 18:48:51 UTC (rev 30387)
@@ -0,0 +1,16 @@
+package org.gnunet.transport.messages;
+
+import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.PeerIdentity;
+
address@hidden(371)
+public class BlacklistReplyMessage implements GnunetMessage.Body {
+    @UInt32
+    public boolean is_allowed;
+
+    @NestedMessage
+    public PeerIdentity peer;
+}

Copied: 
gnunet-java/src/main/java/org/gnunet/transport/messages/RequestConnectMessage.java
 (from rev 30348, 
gnunet-java/src/main/java/org/gnunet/transport/RequestConnectMessage.java)
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/transport/messages/RequestConnectMessage.java
                          (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/transport/messages/RequestConnectMessage.java
  2013-10-21 18:48:51 UTC (rev 30387)
@@ -0,0 +1,24 @@
+package org.gnunet.transport.messages;
+
+import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.PeerIdentity;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
address@hidden(374)
+public class RequestConnectMessage implements GnunetMessage.Body {
+    @UInt32
+    public int reserved;
+
+    /**
+     * Identity of the peer we would like to connect to.
+     */
+    @NestedMessage
+    public PeerIdentity peer;
+}

Copied: 
gnunet-java/src/main/java/org/gnunet/transport/messages/StartMessage.java (from 
rev 30348, gnunet-java/src/main/java/org/gnunet/transport/StartMessage.java)
===================================================================
--- gnunet-java/src/main/java/org/gnunet/transport/messages/StartMessage.java   
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/transport/messages/StartMessage.java   
2013-10-21 18:48:51 UTC (rev 30387)
@@ -0,0 +1,32 @@
+package org.gnunet.transport.messages;
+
+import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.PeerIdentity;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
address@hidden(360)
+public class StartMessage implements GnunetMessage.Body {
+    /**
+     * 0: no options
+     * 1: The 'self' field should be checked
+     * 2: this client is interested in payload traffic
+     */
+    @UInt32
+    public int options;
+
+    /**
+     * Identity we think we have.  If it does not match, the
+     * receiver should print out an error message and disconnect.
+     */
+    @NestedMessage
+    public PeerIdentity self;
+
+
+}

Added: gnunet-java/src/main/java/org/gnunet/util/CancellationToken.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/CancellationToken.java            
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/CancellationToken.java    
2013-10-21 18:48:51 UTC (rev 30387)
@@ -0,0 +1,49 @@
+/*
+ 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.util;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Token that calls a method or other cancelables on cancellation,
+ * and stores its cancellation state.
+ */
+public class CancellationToken implements Cancelable {
+    private boolean isCanceled;
+    private List<Cancelable> cancelables = new LinkedList<Cancelable>();
+    @Override
+    public void cancel() {
+        if (isCanceled) {
+            throw new AssertionError();
+        }
+        for (Cancelable c : cancelables) {
+            c.cancel();
+        }
+    }
+
+    public void add(Cancelable cancelable) {
+        if (isCanceled) {
+            throw new AssertionError();
+        }
+        cancelables.add(cancelable);
+    }
+}

Modified: gnunet-java/src/main/java/org/gnunet/util/Configuration.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/Configuration.java        
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/util/Configuration.java        
2013-10-21 18:48:51 UTC (rev 30387)
@@ -21,7 +21,7 @@
 package org.gnunet.util;
 
 import com.google.common.base.Charsets;
-import com.google.common.base.Joiner;
+import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.collect.HashBasedTable;
 import com.google.common.collect.Table;
@@ -31,10 +31,9 @@
 
 import java.io.BufferedWriter;
 import java.io.File;
-import java.io.FileWriter;
+import java.io.IOError;
 import java.io.IOException;
 import java.nio.charset.Charset;
-import java.nio.file.Path;
 import java.util.*;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -474,24 +473,24 @@
                 }
             }
         }
-        Collection<File> files = new ArrayList<File>();
-        String xdg = System.getenv("XDG_CONFIG_HOME");
-        if (xdg != null && !xdg.isEmpty()) {
-            File f = new File(xdg + "/gnunet.conf");
-            if (f.exists()) {
-                parse(f.getAbsolutePath());
-            }
-        }
     }
 
     /**
-     * Read a filename from the section.
-     * @param section
-     * @param option
-     * @return
+     * Read a filename from an option in the given section.
+     * In contrast to getValueString, getValueFilename $-expands the 
configuration value.
+     *
+     * @param section section of interest
+     * @param option option of interest
+     * @return an optional with the filname, which may be non-present
+     *         if the section/option does not exist
      */
     public Optional<String> getValueFilename(String section, String option) {
-        return null;
+        return getValueString(section, option).transform(new Function<String, 
String>() {
+            @Override
+            public String apply(java.lang.String input) {
+                return expandDollar(input);
+            }
+        });
     }
 
     public void deserialize(String str) {
@@ -504,4 +503,29 @@
             super(string);
         }
     }
+
+    /**
+     * Write the configuration to a temporary file that is
+     * deleted once the JVM exits.
+     *
+     * @return temporary file with the configuration written to it
+     */
+    public File writeTemp() {
+        File f;
+        try {
+            f = File.createTempFile("gnunet-config", ".conf");
+            f.deleteOnExit();
+            Files.write(serialize(), f, Charsets.UTF_8);
+        } catch (IOException e) {
+            throw new IOError(e);
+        }
+        return f;
+    }
+
+    public Configuration clone() {
+        Configuration cfg = new Configuration();
+        cfg.sections.putAll(this.sections);
+        cfg.sectionSources.putAll(this.sectionSources);
+        return cfg;
+    }
 }

Modified: gnunet-java/src/main/java/org/gnunet/util/HashCode.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/HashCode.java     2013-10-21 
17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/util/HashCode.java     2013-10-21 
18:48:51 UTC (rev 30387)
@@ -27,6 +27,7 @@
 
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
 import java.util.Arrays;
 
 
@@ -40,7 +41,7 @@
 
 
     public HashCode() {
-        // construct needs a default c'tor
+        data = new byte[64];
     }
 
     public HashCode(byte[] hash) {
@@ -98,4 +99,10 @@
     }
 
 
+    public static HashCode random() {
+        HashCode hashCode = new HashCode();
+        SecureRandom sr = new SecureRandom();
+        sr.nextBytes(hashCode.data);
+        return hashCode;
+    }
 }
\ No newline at end of file

Modified: gnunet-java/src/main/java/org/gnunet/util/Helper.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/Helper.java       2013-10-21 
17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/util/Helper.java       2013-10-21 
18:48:51 UTC (rev 30387)
@@ -161,16 +161,22 @@
         @Override
         public void run(Scheduler.RunContext ctx) {
             readTaskId = null;
-            int n = 0;
+            int n;
             try {
                 n = mst.readFrom(readThread.pipe.source(), false);
             } catch (IOException e) {
                 logger.warn("helper reader got io exception: {}", e);
                 return;
             }
-            if (n != -1 && readThread.pipe.source().isOpen()) {
-                readTaskId = readTaskConfig.schedule();
+            if (n == -1) {
+                logger.debug("helper reader got EOF");
+                return;
             }
+            if (!readThread.pipe.sink().isOpen()) {
+                logger.debug("read thread closed pipe");
+                return;
+            }
+            readTaskId = readTaskConfig.schedule();
         }
     }
 
@@ -268,6 +274,7 @@
      * @return true on success, false on failure
      */
     public boolean kill(boolean softkill) {
+        logger.debug("killing helper process (soft={})", softkill);
         killed = true;
         if (readTaskId != null) {
             readTaskId.cancel();

Modified: gnunet-java/src/main/java/org/gnunet/util/MessageStreamTokenizer.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/MessageStreamTokenizer.java       
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/util/MessageStreamTokenizer.java       
2013-10-21 18:48:51 UTC (rev 30387)
@@ -62,11 +62,17 @@
         logger.debug("dispatching message");
         if (found) {
             GnunetMessage msg;
+            int oldPos = buffer.position();
             try {
                 msg = Construct.parseAs(buffer, GnunetMessage.class);
             } catch (OutOfMemoryError e) {
                 throw new OutOfMemoryError("oom while parsing " + unionClass);
             }
+            int parsedSize = buffer.position() - oldPos;
+            if (parsedSize != msg.header.messageSize) {
+                throw new AssertionError("mismatch between parsed message and 
header for " +
+                  msg.body.getClass());
+            }
             mstCalllback.onKnownMessage(msg);
         } else {
             UnknownMessageBody b = new UnknownMessageBody();
@@ -82,14 +88,14 @@
      * @return true if message could be extracted, false if not enough data is 
available
      */
     public boolean extractOne() {
-        System.out.println("trying to extract message from buffer");
         if (msgh == null && buffer.position() >= 4) {
             // prepare for reading
             buffer.flip();
             msgh = Construct.parseAs(buffer, GnunetMessage.Header.class);
             // undo read
             buffer.position(0);
-            logger.debug("got header in mst, (" + buffer.limit() + "/" + 
msgh.messageSize + " read)");
+            logger.debug("got header in mst (t: " + msgh.messageType + ", s: " 
+ msgh.messageSize +
+                    " (" + buffer.limit() + "/" + msgh.messageSize + " read)");
             if (buffer.capacity() < msgh.messageSize) {
                 ByteBuffer newBuf = ByteBuffer.allocate(msgh.messageSize);
                 newBuf.put(buffer);
@@ -120,6 +126,7 @@
     public int readFrom(ReadableByteChannel source, boolean oneShot) throws 
IOException {
         int n;
         n = source.read(buffer);
+        logger.debug("read {} bytes from channel", n);
         if (oneShot) {
             extractOne();
         }

Modified: gnunet-java/src/main/java/org/gnunet/util/Program.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/Program.java      2013-10-21 
17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/util/Program.java      2013-10-21 
18:48:51 UTC (rev 30387)
@@ -31,7 +31,7 @@
 
 
 /**
- * Program is the entry point class for everything that uses gnunet services 
or APIs.
+ * Program is the entry point class for everything that uses GNUnet services 
or APIs.
  *
  * Also specifies the default command line arguments using the 
org.gnunet.util.getopt annotations.
  *
@@ -82,7 +82,7 @@
 
 
     /**
-     * A program with the desired environment for a gnunet utility.
+     * A program with the desired environment for a GNUnet utility.
      * While executing, the scheduler is guaranteed to run, command arguments 
are parsed,
      * the default configuration is loaded and the DNS Resolver is initialized.
      *
@@ -176,11 +176,33 @@
     }
 
     /**
-     * Start the Program as the initial task of the Scheduler.
+     * Overridden by specializations of Program, like Service.
      *
-     * @return the exit value of the scheduler
+     * Allows for start() to be final.
      */
-    public final int start() {
+    /* package-private */
+    void runHook() {
+        run();
+    }
+
+    /**
+     * Override to implement the behavior of the Program.
+     */
+    protected abstract void run();
+
+    protected final Configuration getConfiguration() {
+        return cfg;
+    }
+
+
+    /**
+     * Start the Program.  Invokes the run method.
+     *
+     * @param withScheduler determines whether the run-method
+     *                      is invoked inside a scheduler task or not
+     * @return the exit value of the program
+     */
+    public final int start(boolean withScheduler) {
         Parser optParser = new Parser(this);
         unprocessedArgs = optParser.parse(args);
 
@@ -200,32 +222,41 @@
             System.out.println(makeHelpText());
             System.out.print(optParser.getHelp());
         } else {
-            Scheduler.run(new Scheduler.Task() {
-                public void run(Scheduler.RunContext c) {
-                    Program.this.runHook();
-                }
-            });
+            if (withScheduler) {
+                Scheduler.run(new Scheduler.Task() {
+                    public void run(Scheduler.RunContext c) {
+                        Program.this.runHook();
+                    }
+                });
+            } else {
+                Program.this.runHook();
+            }
         }
 
+        if (Scheduler.hasTasks()) {
+            logger.error("scheduler still has pending tasks after program 
returned");
+        }
+
         return returnValue;
     }
 
+
     /**
-     * Overridden by specializations of Program, like Service.
+     * Start the Program as the initial task of the Scheduler.
      *
-     * Allows for start() to be final.
+     * @return the exit value of the program
      */
-    /* package-private */
-    void runHook() {
-        run();
+    public final int start() {
+        return start(true);
     }
 
+
     /**
-     * Override to implement the behavior of the Program.
+     * Start the program without setting up the scheduler.
+     *
+     * @return the exit value of the program
      */
-    protected abstract void run();
-
-    protected final Configuration getConfiguration() {
-        return cfg;
+    public final int startWithoutScheduler() {
+        return start(false);
     }
 }

Modified: gnunet-java/src/main/java/org/gnunet/util/Scheduler.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/Scheduler.java    2013-10-21 
17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/util/Scheduler.java    2013-10-21 
18:48:51 UTC (rev 30387)
@@ -658,6 +658,11 @@
     }
 
 
+    public static boolean hasTasks() {
+        return readyCount != 0 || !pending.isEmpty();
+    }
+
+
     /**
      * A handle to a file system object that can be selected on.
      */

Modified: gnunet-java/src/main/java/org/gnunet/util/Strings.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/Strings.java      2013-10-21 
17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/util/Strings.java      2013-10-21 
18:48:51 UTC (rev 30387)
@@ -26,19 +26,17 @@
 public class Strings {
     private static final String encTable = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
 
-
     /**
      * Convert binary data to ASCII encoding.  The ASCII encoding is rather
      * GNUnet specific.  It was chosen such that it only uses characters
-     * in [0-9A-V], can be produced without complex arithmetics and uses a
+     * in [0-9A-V], can be produced without complex arithmetic and uses a
      * small number of characters.
      *
-     * returned string has length ((size*8) + (((size*8) % 5) > 0 ? 5 - 
((size*8) % 5) : 0)) / 5 bytes
+     * The returned string has length (data.length * 8 + 4) / 5
      *
      * @param data data to encode
      * @return pointer to the next byte in 'out' or NULL on error.
      */
-
     public static String dataToString(byte[] data) {
         StringBuilder sb = new StringBuilder();
 
@@ -50,7 +48,7 @@
         while ((rpos < size) || (vbit > 0)) {
             if ((rpos < size) && (vbit < 5)) {
                 byte b = data[(int) rpos++];
-                // convert double to int without sign extension
+                // convert (possibly negative) byte to int without sign 
extension
                 int s = b >= 0 ? b : (256 + b);
                 // eat 8 more bits
                 bits = (bits << 8) | s;
@@ -68,15 +66,14 @@
     }
 
     /**
-     * Convert ASCII encoding back to data
-     * out_size must match exactly the size of the data before it was encoded.
+     * Convert ASCII encoding back to data. The size of the output array
+     * must exactly match the size of the encoded string.
      *
      * @param string the string to decode
-     * @param outSize size of the output buffer
-     * @return the decoded data on success, null if result has the wrong 
encoding
+     * @param outData size of the output buffer
+     * @return was the encoding successful?
      */
-
-    public static byte[] stringToData(String string, int outSize) {
+    public static boolean stringToData(String string, byte[] outData) {
         long rpos;
         long wpos;
         long bits;
@@ -84,46 +81,82 @@
         long ret;
         long shift;
         int enclen = string.length();
-        int encoded_len = outSize * 8;
-        byte[] out = new byte[outSize];
-        if (encoded_len % 5 > 0) {
+        int encodedLen = outData.length * 8;
+        if (encodedLen % 5 > 0) {
             // padding!
-            vbit = encoded_len % 5;
+            vbit = encodedLen % 5;
             shift = 5 - vbit;
         } else {
             vbit = 0;
             shift = 0;
         }
-        if ((encoded_len + shift) / 5 != enclen) {
-            return null;
+        if ((encodedLen + shift) / 5 != enclen) {
+            return false;
         }
 
-        wpos = outSize;
+        wpos = outData.length;
         rpos = enclen;
-        bits = (ret = getValue__(string.charAt((int) (--rpos)))) >> (5 - 
encoded_len % 5);
+        bits = (ret = getValue(string.charAt((int) (--rpos)))) >> (5 - 
encodedLen % 5);
         if (-1 == ret) {
-            return null;
+            return false;
         }
         while (wpos > 0) {
             assert rpos > 0;
-            bits = ((ret = getValue__(string.charAt((int) (--rpos)))) << vbit) 
| bits;
+            bits = ((ret = getValue(string.charAt((int) (--rpos)))) << vbit) | 
bits;
             if (-1 == ret) {
-                return null;
+                return false;
             }
             vbit += 5;
             if (vbit >= 8) {
-                out[(int)--wpos] = (byte)((char) bits);
+                outData[(int)--wpos] = (byte)((char) bits);
                 bits >>= 8;
                 vbit -= 8;
             }
         }
         assert(rpos == 0);
         assert(vbit == 0);
-        return out;
+        return true;
     }
 
+    /**
+     * Convert ASCII encoding back to data.
+     * The outSize must match exactly the size of the data before it was 
encoded.
+     *
+     * @param string the string to decode
+     * @param outSize size of the output buffer
+     * @return the decoded data on success, null if result has the wrong 
encoding
+     */
+    public static byte[] stringToData(String string, int outSize) {
+        byte[] outData = new byte[outSize];
+        if (stringToData(string, outData)) {
+            return outData;
+        }
+        return null;
+    }
 
-    private static int getValue__ (char a) {
+    /**
+     * Compute the length of the resulting string when encoding data of the 
given size
+     * in bytes.
+     *
+     * @param dataSize size of the data to encode in bytes
+     * @return size of the string that would result from encoding
+     */
+    public static int getEncodedStringLength(int dataSize) {
+        return (dataSize * 8 + 4) / 5;
+    }
+
+    /**
+     * Compute the length of the resulting data in bytes when decoding a 
(valid) string of the
+     * given size.
+     *
+     * @param stringSize size of the string to decode
+     * @return size of the resulting data in bytes
+     */
+    public static int getDecodedDataLength(int stringSize) {
+        return (stringSize * 5) / 8;
+    }
+
+    private static int getValue(char a) {
         if ((a >= '0') && (a <= '9')) {
             return a - '0';
         }
@@ -132,5 +165,4 @@
         }
         return -1;
     }
-
 }

Modified: gnunet-java/src/main/java/org/gnunet/util/crypto/EcdsaPrivateKey.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/crypto/EcdsaPrivateKey.java       
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/util/crypto/EcdsaPrivateKey.java       
2013-10-21 18:48:51 UTC (rev 30387)
@@ -14,7 +14,8 @@
     }
 
     public EcdsaSignature sign(int purpose, byte[] data) {
-        return null;
+        EcdsaSignature signature = new EcdsaSignature();
+        return signature;
     }
 
     public static EcdsaPrivateKey fromFile(String privKeyFilename) {
@@ -22,7 +23,7 @@
     }
 
     public EcdsaPublicKey getPublicKey() {
-        return null;
+        return EcdsaPublicKey.random();
     }
 
     public static EcdsaPrivateKey createRandom() {

Modified: gnunet-java/src/main/java/org/gnunet/util/crypto/EcdsaPublicKey.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/crypto/EcdsaPublicKey.java        
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/util/crypto/EcdsaPublicKey.java        
2013-10-21 18:48:51 UTC (rev 30387)
@@ -1,8 +1,17 @@
 package org.gnunet.util.crypto;
 
 import org.gnunet.construct.FixedSizeIntegerArray;
+import org.gnunet.construct.Message;
+import org.gnunet.util.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-public class EcdsaPublicKey {
+import java.security.SecureRandom;
+import java.util.Arrays;
+
+public class EcdsaPublicKey implements Message {
+    private static final Logger logger = LoggerFactory
+            .getLogger(EcdsaPublicKey.class);
     /**
      * x-coordinate of the point on the curve.
      * The number is stored as little endian.
@@ -21,7 +30,66 @@
 
     }
 
-    public static EcdsaPublicKey fromString(String s) {
-        return null;
+    public EcdsaPublicKey() {
+        x = new byte[32];
+        y = new byte[32];
     }
+
+    public static EcdsaPublicKey fromStringUncompressed(String s) {
+        if (s.length() != org.gnunet.util.Strings.getEncodedStringLength(64)) {
+            logger.debug("invalid key length");
+            return null;
+        }
+        byte[] data = new byte[64];
+        if (!Strings.stringToData(s, data)) {
+            logger.debug("invalid key format");
+            return null;
+        }
+        EcdsaPublicKey publicKey = new EcdsaPublicKey();
+        System.arraycopy(data, 0, publicKey.x, 0, 32);
+        System.arraycopy(data, 32, publicKey.y, 0, 32);
+        return publicKey;
+    }
+
+    /**
+     * Create a random public key.  The generated key might not even be valid
+     * (i.e. not on the curve), thus this method should only be used for 
testing.
+     *
+     * @return a random, possibly invalid public key
+     */
+    public static EcdsaPublicKey random() {
+        SecureRandom sr = new SecureRandom();
+        EcdsaPublicKey publicKey = new EcdsaPublicKey();
+        sr.nextBytes(publicKey.x);
+        sr.nextBytes(publicKey.y);
+        return publicKey;
+    }
+
+    @Override
+    public String toString() {
+        byte[] keyData = new byte[64];
+        System.arraycopy(x, 0, keyData, 0, 32);
+        System.arraycopy(y, 0, keyData, 32, 32);
+        return Strings.dataToString(keyData);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        EcdsaPublicKey publicKey = (EcdsaPublicKey) o;
+
+        if (!Arrays.equals(x, publicKey.x)) return false;
+        if (!Arrays.equals(y, publicKey.y)) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Arrays.hashCode(x);
+        result = 31 * result + Arrays.hashCode(y);
+        return result;
+    }
 }

Modified: gnunet-java/src/main/java/org/gnunet/util/crypto/EcdsaSignature.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/crypto/EcdsaSignature.java        
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/util/crypto/EcdsaSignature.java        
2013-10-21 18:48:51 UTC (rev 30387)
@@ -21,12 +21,55 @@
 package org.gnunet.util.crypto;
 
 
-public class EcdsaSignature {
-    boolean verify() {
+import org.gnunet.construct.FixedSizeIntegerArray;
+import org.gnunet.construct.Message;
+import org.gnunet.util.Strings;
+
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+
+public class EcdsaSignature implements Message {
+    /**
+     * R value of the signature in compressed form.
+     * The number is stored as little endian.
+     */
+    @FixedSizeIntegerArray(bitSize = 8, signed = false, length = 32)
+    public byte[] r;
+
+    /**
+     * S-value of the signature.
+     * The number is stored as little endian.
+     */
+    @FixedSizeIntegerArray(bitSize = 8, signed = false, length = 32)
+    public byte[] s;
+
+    public EcdsaSignature() {
+        this.r = new byte[32];
+        this.s = new byte[32];
+    }
+
+    public boolean verify(byte[] m, int purpose, EddsaPublicKey publicKey) {
         return false;
     }
 
-    public static EcdsaSignature fromString(String s) {
-        return null;  //To change body of created methods use File | Settings 
| File Templates.
+    public static EcdsaSignature fromString(String value) {
+        byte[] data = new byte[64];
+        if (!Strings.stringToData(value, data)) {
+            throw new AssertionError();
+        }
+        EcdsaSignature sig = new EcdsaSignature();
+        System.arraycopy(data, 0, sig.r, 0, 32);
+        System.arraycopy(data, 32, sig.s, 0, 32);
+        return sig;
     }
+
+    @Override
+    public String toString() {
+        byte[] sigData = new byte[64];
+        System.arraycopy(r, 0, sigData, 0, 32);
+        System.arraycopy(s, 0, sigData, 32, 32);
+        return Strings.dataToString(sigData);
+    }
+
+
 }

Modified: gnunet-java/src/main/java/org/gnunet/util/crypto/EddsaSignature.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/crypto/EddsaSignature.java        
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/util/crypto/EddsaSignature.java        
2013-10-21 18:48:51 UTC (rev 30387)
@@ -43,9 +43,11 @@
 
 import org.gnunet.construct.FixedSizeIntegerArray;
 import org.gnunet.construct.Message;
+import org.gnunet.util.Strings;
 
 import java.math.BigInteger;
 import java.nio.ByteBuffer;
+import java.security.SecureRandom;
 
 public class EddsaSignature implements Message {
     /**
@@ -62,6 +64,11 @@
     @FixedSizeIntegerArray(bitSize = 8, signed = false, length = 32)
     public byte[] s;
 
+    public EddsaSignature() {
+        this.r = new byte[32];
+        this.s = new byte[32];
+    }
+
     public EddsaSignature(Ed25519 r, BigInteger s) {
         this.r = r.encode();
         this.s = Ed25519.encodeScalar(s);
@@ -80,15 +87,34 @@
     }
 
     public static EddsaSignature fromString(String value) {
-        return null;
+        byte[] data = new byte[64];
+        if (!Strings.stringToData(value, data)) {
+            throw new AssertionError();
+        }
+        EddsaSignature sig = new EddsaSignature();
+        System.arraycopy(data, 0, sig.r, 0, 32);
+        System.arraycopy(data, 32, sig.s, 0, 32);
+        return sig;
     }
 
     /**
      * Return a signature that is invalid with very, very high probability.
      *
-     * @return
+     * @return signature with random garbage
      */
     public static EddsaSignature randomGarbage() {
-        return null;
+        EddsaSignature sig = new EddsaSignature();
+        SecureRandom r = new SecureRandom();
+        r.nextBytes(sig.r);
+        r.nextBytes(sig.s);
+        return sig;
     }
+
+    @Override
+    public String toString() {
+        byte[] sigData = new byte[64];
+        System.arraycopy(r, 0, sigData, 0, 32);
+        System.arraycopy(s, 0, sigData, 32, 32);
+        return Strings.dataToString(sigData);
+    }
 }

Modified: gnunet-java/src/main/java/org/gnunet/util/getopt/Parser.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/getopt/Parser.java        
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/util/getopt/Parser.java        
2013-10-21 18:48:51 UTC (rev 30387)
@@ -82,6 +82,19 @@
 
                 f.setAccessible(true);
 
+                if (shortOpt.containsKey(opt.shortname())) {
+                    throw new AssertionError(
+                            String.format("short option '%s' specified 
multiple times (fields %s and %s)",
+                                    opt.shortname(), f, 
shortOpt.get(opt.shortname()).f));
+                }
+
+
+                if (longOpt.containsKey(opt.longname())) {
+                    throw new AssertionError(
+                            String.format("long option '%s' specified multiple 
times (fields %s and %s)",
+                                    opt.longname(), f, 
longOpt.get(opt.longname()).f));
+                }
+
                 longOpt.put(opt.longname(), new OptionField(opt, f));
                 shortOpt.put(opt.shortname(), new OptionField(opt, f));
                 arguments.add(opt);

Modified: gnunet-java/src/main/java/org/gnunet/voting/Ballot.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/Ballot.java     2013-10-21 
17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/voting/Ballot.java     2013-10-21 
18:48:51 UTC (rev 30387)
@@ -22,6 +22,7 @@
 
 import com.google.common.base.Joiner;
 import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
 import com.google.common.primitives.Longs;
@@ -37,16 +38,41 @@
 import java.util.regex.Pattern;
 
 /**
- * Public information about an election.
+ * Public information about an election.  The ballot can be serialized / 
deserialized in form
+ * of a GNUnet configuration file.
  */
 public class Ballot {
-
+    /**
+     * Topic of the election.
+     */
     String topic;
+    /**
+     * Elegibility group for the election.
+     */
     String group;
+    /**
+     * List of choices for the election.
+     */
     List<String> choices;
+    /**
+     * When does the election start?
+     */
     AbsoluteTime startTime;
+    /**
+     * When are no further votes accepted?
+     */
     AbsoluteTime closingTime;
+    /**
+     * When must the consensus between the peers be finished?
+     */
+    AbsoluteTime concludeTime;
+    /**
+     * When can results be queried?
+     */
     AbsoluteTime queryTime;
+    /**
+     * When are the election results discarded by the authority?
+     */
     AbsoluteTime endTime;
     BiMap<String,PeerIdentity> authorities;
     SortedMap<String,EddsaSignature> registrationSigs;
@@ -54,10 +80,8 @@
     EcdsaPublicKey issuerPub;
     EcdsaSignature issuerSig;
     EcdsaPublicKey voterPub;
-    EcdsaSignature voterGroupCert;
-    EcdsaSignature permission;
-    EcdsaPublicKey voterSig;
     SortedMap<String,EddsaSignature> confirmationSigs;
+    GroupCert groupCert;
 
     /**
      * Choice in plaintext.
@@ -156,13 +180,13 @@
         if (!optCaPub.isPresent()) {
             throw new InvalidBallotException("no CA pub key given");
         }
-        caPub = EcdsaPublicKey.fromString(optCaPub.get());
+        caPub = EcdsaPublicKey.fromStringUncompressed(optCaPub.get());
         if (null == caPub) {
             throw new InvalidBallotException("CA pub key invalid");
         }
         Optional<String> optIssuerPub = cfg.getValueString("election", 
"ISSUER_PUB");
         if (optIssuerPub.isPresent()) {
-            issuerPub = EcdsaPublicKey.fromString(optIssuerPub.get());
+            issuerPub = 
EcdsaPublicKey.fromStringUncompressed(optIssuerPub.get());
             Optional<String> optIssuerSig = cfg.getValueString("election", 
"ISSUER_SIG");
             if (!optIssuerSig.isPresent()) {
                 throw new InvalidBallotException("issuer public key present, 
but no signature");
@@ -203,13 +227,21 @@
 
         Optional<String> optVoterPub = cfg.getValueString("vote", "VOTER_PUB");
         if (optVoterPub.isPresent()) {
-            voterPub = EcdsaPublicKey.fromString(optVoterPub.get());
+            voterPub = 
EcdsaPublicKey.fromStringUncompressed(optVoterPub.get());
+            if (null == voterPub) {
+                throw new InvalidBallotException("voter public key present but 
invalid");
+            }
         }
 
         startTime = getTime(cfg, "START");
         closingTime = getTime(cfg, "CLOSING");
+        concludeTime = getTime(cfg, "CONCLUDE");
         queryTime = getTime(cfg, "QUERY");
         endTime = getTime(cfg, "END");
+
+        if (cfg.haveValue("vote", "GROUP_SIG")) {
+            groupCert = GroupCert.fromBallotConfig(this, cfg);
+        }
     }
 
     /**
@@ -281,9 +313,10 @@
         cfg.setValueString("election", "CHOICES", 
Joiner.on("//").join(choices));
         cfg.setValueString("election", "CA_PUB", caPub.toString());
         cfg.setValueNumber("election", "TIMESTAMP_START", 
startTime.getSeconds());
-        cfg.setValueNumber("election", "TIMESTAMP_CLOSING", 
startTime.getSeconds());
-        cfg.setValueNumber("election", "TIMESTAMP_QUERY", 
startTime.getSeconds());
-        cfg.setValueNumber("election", "TIMESTAMP_END", 
startTime.getSeconds());
+        cfg.setValueNumber("election", "TIMESTAMP_CLOSING", 
closingTime.getSeconds());
+        cfg.setValueNumber("election", "TIMESTAMP_QUERY", 
queryTime.getSeconds());
+        cfg.setValueNumber("election", "TIMESTAMP_CONCLUDE", 
concludeTime.getSeconds());
+        cfg.setValueNumber("election", "TIMESTAMP_END", endTime.getSeconds());
         for (Map.Entry<String, PeerIdentity> e : authorities.entrySet()) {
             cfg.setValueString("authorities", e.getKey(), 
e.getValue().toString());
         }
@@ -307,6 +340,9 @@
         if (null != voterPub) {
             cfg.setValueString("vote", "VOTER_PUB", voterPub.toString());
         }
+        if (null != groupCert) {
+            groupCert.writeBallotConfig(cfg);
+        }
         return cfg;
     }
 
@@ -343,6 +379,9 @@
         buf.append("Closing Time: ");
         buf.append(closingTime.toFancyString());
         buf.append("\n");
+        buf.append("Conclude Time: ");
+        buf.append(concludeTime.toFancyString());
+        buf.append("\n");
         buf.append("Query Time: ");
         buf.append(queryTime.toFancyString());
         buf.append("\n");
@@ -396,6 +435,18 @@
         } else {
             buf.append("no choice selected\n");
         }
+        if (voterPub != null) {
+            buf.append("voter: ");
+            buf.append(voterPub.toString());
+            buf.append("\n");
+            if (groupCert == null) {
+                buf.append("voter not in group\n");
+            } else {
+                buf.append("voter in group\n");
+            }
+        } else {
+            buf.append("no voter\n");
+        }
         return buf.toString();
     }
 
@@ -472,4 +523,16 @@
     public List<PeerIdentity> getAuthorities() {
         return new ArrayList<PeerIdentity>(authorities.values());
     }
+
+    public void encodeGroup(GroupCert groupCert) {
+        Preconditions.checkNotNull(groupCert);
+        if (this.groupCert != null) {
+            throw new InvalidBallotException("ballot already has group 
information");
+        }
+        if (voterPub != null && 
!groupCert.getMemberPublicKey().equals(voterPub)) {
+            throw new InvalidBallotException("group and voter public key do 
not match");
+        }
+        voterPub = groupCert.getMemberPublicKey();
+        this.groupCert = groupCert;
+    }
 }

Modified: gnunet-java/src/main/java/org/gnunet/voting/BallotTool.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/BallotTool.java 2013-10-21 
17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/voting/BallotTool.java 2013-10-21 
18:48:51 UTC (rev 30387)
@@ -25,221 +25,341 @@
 import com.google.common.io.ByteStreams;
 import com.google.common.io.Files;
 import com.google.common.io.OutputSupplier;
+import org.gnunet.identity.Identity;
+import org.gnunet.identity.IdentityCallback;
+import org.gnunet.mesh.Mesh;
+import org.gnunet.mesh.MeshRunabout;
+import org.gnunet.mesh.TunnelEndHandler;
+import org.gnunet.testbed.CompressedConfig;
+import org.gnunet.util.AbsoluteTime;
+import org.gnunet.util.Configuration;
+import org.gnunet.util.PeerIdentity;
 import org.gnunet.util.Program;
-import org.gnunet.util.crypto.EcdsaPrivateKey;
+import org.gnunet.util.crypto.EddsaSignature;
 import org.gnunet.util.getopt.Argument;
 import org.gnunet.util.getopt.ArgumentAction;
+import org.gnunet.voting.messages.*;
 
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.List;
+import java.util.Random;
 
 /**
  * Tool for creating, manipulating and submitting ballot files.
  */
-public class BallotTool {
-    public static void main(String[] args) {
-        new Program(args) {
-            @Argument(
-                    shortname = "q",
-                    longname = "query",
-                    action = ArgumentAction.SET,
-                    description = "query election result")
-            boolean query = false;
+public class BallotTool extends Program {
+    @Argument(
+            shortname = "e",
+            longname = "ego",
+            action = ArgumentAction.STORE_STRING,
+            description = "ego to use for the operation")
+    String egoName = null;
 
-            @Argument(
-                    shortname = "s",
-                    longname = "submit",
-                    action = ArgumentAction.SET,
-                    description = "submit the vote to the authorities")
-            boolean submit = false;
+    @Argument(
+            shortname = "q",
+            longname = "query",
+            action = ArgumentAction.SET,
+            description = "query election result")
+    boolean query = false;
 
-            @Argument(
-                    shortname = "r",
-                    longname = "register",
-                    action = ArgumentAction.SET,
-                    description = "register an election with the authorities")
-            boolean register = false;
+    @Argument(
+            shortname = "s",
+            longname = "submit",
+            action = ArgumentAction.SET,
+            description = "submit the vote to the authorities")
+    boolean submit = false;
 
-            @Argument(
-                    shortname = "i",
-                    longname = "issue",
-                    action = ArgumentAction.SET,
-                    description = "sign the ballot as issuer")
-            boolean issue = false;
+    @Argument(
+            shortname = "r",
+            longname = "register",
+            action = ArgumentAction.SET,
+            description = "register an election with the authorities")
+    boolean register = false;
 
-            @Argument(
-                    shortname = "x",
-                    longname = "select",
-                    action = ArgumentAction.STORE_STRING,
-                    argumentName = "CHOICE",
-                    description = "select and encrypt a vote option")
-            String select = null;
+    @Argument(
+            shortname = "i",
+            longname = "issue",
+            action = ArgumentAction.SET,
+            description = "sign the ballot as issuer (use with -e)")
+    boolean issue = false;
 
-            @Argument(
-                    shortname = "V",
-                    longname = "verify",
-                    action = ArgumentAction.SET,
-                    description = "verify signatures in the ballot and show 
information")
-            boolean verify = false;
+    @Argument(
+            shortname = "x",
+            longname = "select",
+            action = ArgumentAction.STORE_STRING,
+            argumentName = "CHOICE",
+            description = "select and encrypt a vote option (use with -e)")
+    String select = null;
 
-            @Argument(
-                    shortname = "t",
-                    longname = "template",
-                    action = ArgumentAction.SET,
-                    description = "write a template ballot to the give ballot 
file")
-            boolean template = false;
+    @Argument(
+            shortname = "V",
+            longname = "verify",
+            action = ArgumentAction.SET,
+            description = "verify signatures in the ballot and show 
information")
+    boolean verify = false;
 
-            @Override
-            protected String makeHelpText() {
-                return "gnunet-ballot [OPTIONS]... BALLOT [PRIVKEYFILE]\n" +
-                        "Create, modify and execute operation on ballots.";
-            }
+    @Argument(
+            shortname = "g",
+            longname = "group",
+            action = ArgumentAction.STORE_STRING,
+            description = "incorporate the group cert into the ballot")
+    String groupCertFile = null;
 
-            private void runTemplate(String filename) {
-                File f = new File(filename);
-                if (f.exists()) {
-                    System.err.println("file already exists, not overwriting");
-                    return;
-                }
-                InputStream is = 
getClass().getResourceAsStream("template.espec");
-                OutputSupplier<FileOutputStream> oss = 
Files.newOutputStreamSupplier(f);
-                try {
-                    ByteStreams.copy(is, oss);
-                } catch (IOException e) {
-                    System.err.println("could not copy template file: " + 
e.getMessage());
-                }
-            }
 
-            private void runIssue(String ballotFilename, String 
privKeyFilename) {
-                File bf = new File(ballotFilename);
-                if (!bf.exists()) {
-                    System.err.println("ballot file does not exist");
-                    return;
-                }
-                File kf = new File(privKeyFilename);
-                if (!kf.exists()) {
-                    System.err.println("private-key file does not exist");
-                    return;
-                }
-                Ballot b = new Ballot(ballotFilename);
-                EcdsaPrivateKey privateKey = 
EcdsaPrivateKey.fromFile(privKeyFilename);
-                if (privateKey == null) {
-                    System.err.println("keyfile invalid");
-                    return;
-                }
-                b.issue(privateKey);
-                try {
-                    Files.write(b.serialize(), bf, Charsets.UTF_8);
-                } catch (IOException e) {
-                    System.err.println("could not write ballot file: " + 
e.getMessage());
-                }
-            }
+    @Argument(
+            shortname = "t",
+            longname = "template",
+            action = ArgumentAction.SET,
+            description = "write a template ballot to the give ballot file")
+    boolean template = false;
 
-            public void runSelect(String ballotFilename, String 
privKeyFilename, String choice) {
-                File bf = new File(ballotFilename);
-                if (!bf.exists()) {
-                    System.err.println("ballot file does not exist");
-                    return;
+    private Identity.Ego ego;
+
+    private Ballot ballot;
+    private String ballotFilename;
+
+    private Mesh mesh;
+    private Mesh.Tunnel tunnel;
+
+    private PeerIdentity currentAuthority;
+
+    /**
+     * Are we finished with communicating over the mesh tunnel and don't need 
to worry about
+     * disconnection?
+     */
+    private boolean tunnelCommunicationFinished;
+
+    public class BallotTunnelEndHandler implements TunnelEndHandler {
+        @Override
+        public void onTunnelEnd(Mesh.Tunnel tunnel) {
+            // FIXME
+        }
+    }
+
+    public class BallotRegisterReceiver extends MeshRunabout {
+        public void visit(BallotRegisterSuccessMessage m) {
+            System.out.println("ballot successfully registered");
+            ballot.addRegistrationSignature(currentAuthority, 
m.registrationSignature);
+            writeBallot();
+            tunnel.destroy();
+            mesh.destroy();
+        }
+
+        public void visit(BallotRegisterFailureMessage m) {
+            System.out.println("registering failed: " + m.reason);
+            tunnel.destroy();
+            mesh.destroy();
+        }
+
+    }
+
+    public class QueryReceiver extends MeshRunabout {
+        public void visit(QueryResponseMessage m) {
+            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]);
                 }
-                File kf = new File(privKeyFilename);
-                if (!kf.exists()) {
-                    System.err.println("private-key file does not exist");
-                    return;
-                }
-                EcdsaPrivateKey privateKey = 
EcdsaPrivateKey.fromFile(privKeyFilename);
-                if (privateKey == null) {
-                    System.err.println("keyfile invalid");
-                    return;
-                }
-                Ballot ballot = new Ballot(ballotFilename);
-                ballot.encodeChoice(choice, privateKey);
-                try {
-                    Files.write(ballot.serialize(), bf, Charsets.UTF_8);
-                } catch (IOException e) {
-                    System.err.println("can not write to ballot file");
-                    return;
-                }
-                System.out.println("vote written to ballot file");
             }
 
+            tunnel.destroy();
+            mesh.destroy();
+        }
 
-            public void runVerify(String ballotFilename) {
-                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());
+        public void visit(QueryFailureMessage m) {
+            System.out.println("failure to query result: authority refused");
+            tunnel.destroy();
+            mesh.destroy();
+        }
+    }
+
+    public class SubmitReceiver extends MeshRunabout {
+
+        public void visit(SubmitSuccessMessage m) {
+            System.out.println("vote successfully submitted");
+            ballot.addConfirmation(currentAuthority, m.confirmationSig);
+            writeBallot();
+            tunnel.destroy();
+            mesh.destroy();
+        }
+
+        public void visit(SubmitFailureMessage m) {
+            System.out.println("vote not submitted: " + m.reason);
+            tunnel.destroy();
+            mesh.destroy();
+        }
+    }
+
+    @Override
+    protected String makeHelpText() {
+        return "gnunet-ballot [OPTIONS]... BALLOT\n" +
+                "Create, modify and execute operation on ballots.";
+    }
+
+    private void runTemplate() {
+        File f = new File(ballotFilename);
+        if (f.exists()) {
+            System.err.println("file already exists, not overwriting");
+            return;
+        }
+        InputStream is = 
getClass().getResourceAsStream("ballot-template.espec");
+        OutputSupplier<FileOutputStream> oss = 
Files.newOutputStreamSupplier(f);
+        try {
+            ByteStreams.copy(is, oss);
+        } catch (IOException e) {
+            System.err.println("could not copy template file: " + 
e.getMessage());
+        }
+    }
+
+    private void writeBallot() {
+        try {
+            Files.write(ballot.serialize(), new File(ballotFilename), 
Charsets.UTF_8);
+        } catch (IOException e) {
+            System.err.println("could not write ballot file: " + 
e.getMessage());
+        }
+    }
+
+    void doCommands() {
+        if (null != groupCertFile) {
+            Configuration groupCertConfig = new Configuration();
+            groupCertConfig.parse(groupCertFile);
+            GroupCert groupCert = 
GroupCert.fromGroupCertConfig(groupCertConfig);
+            ballot.encodeGroup(groupCert);
+            writeBallot();
+            return;
+        }
+        if (register) {
+            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(getConfiguration(), new BallotTunnelEndHandler(), 
new BallotRegisterReceiver());
+            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);
+            return;
+        }
+        if (issue) {
+            if (null == ego) {
+                System.out.println("no ego given");
+                return;
+            }
+            ballot.issue(ego.getPrivateKey());
+            writeBallot();
+            return;
+        }
+        if (select != null) {
+            if (null == ego) {
+                System.out.println("no ego given");
+                return;
+            }
+            ballot.encodeChoice(select, ego.getPrivateKey());
+            writeBallot();
+            return;
+        }
+        if (submit) {
+            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());
+            currentAuthority = authority;
+            mesh = new Mesh(cfg, new BallotTunnelEndHandler(), new 
SubmitReceiver());
+            tunnel = mesh.createTunnel(authority, 
TallyAuthorityDaemon.MESH_PORT, true, true, null);
+            SubmitMessage m = new SubmitMessage();
+            if (ballot.voterPub == null) {
+                throw new InvalidBallotException("no voter in ballot");
+            }
+            m.voterPub = ballot.voterPub;
+            if (ballot.groupCert == null) {
+                throw new InvalidBallotException("no group cert in ballot");
+            }
+            m.groupCertExpiration = 
ballot.groupCert.getExpiration().asMessage();
+            m.groupCert = ballot.groupCert.getSignature();
+            m.ballotGuid = ballot.getBallotGuid();
+            m.choiceId = ballot.choiceId;
+            tunnel.send(m);
+            return;
+        }
+        if (verify) {
+            System.out.print(ballot.describe());
+            return;
+        }
+        if (query) {
+            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, new BallotTunnelEndHandler(), new 
QueryReceiver());
+            tunnel = mesh.createTunnel(authority, 
TallyAuthorityDaemon.MESH_PORT, true, true, null);
+            QueryMessage m = new QueryMessage();
+            m.ballotGUID = ballot.getBallotGuid();
+            tunnel.send(m);
+            return;
+        }
+    }
 
-            @Override
-            public void run() {
-                if (template) {
-                    if (this.unprocessedArgs.length != 1) {
-                        System.err.println("-t/--template requires exactly one 
positional argument");
-                        return;
-                    }
-                    runTemplate(unprocessedArgs[0]);
-                    return;
+    @Override
+    public void run() {
+        if (unprocessedArgs.length != 1) {
+            System.err.println("no ballot file specified");
+            setReturnValue(1);
+            return;
+        }
+        ballotFilename = unprocessedArgs[0];
+        if (template) {
+            runTemplate();
+            return;
+        }
+        // all other commands need a loaded ballot file
+        File bf = new File(ballotFilename);
+        if (!bf.exists()) {
+            System.err.println("ballot file does not exist");
+            return;
+        }
+        ballot = new Ballot(ballotFilename);
+        if (null != egoName) {
+            Identity.lookup(getConfiguration(), egoName, new 
IdentityCallback() {
+                @Override
+                public void onEgo(Identity.Ego ego) {
+                    BallotTool.this.ego = ego;
+                    doCommands();
                 }
-
-                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;
+                @Override
+                public void onError(String errorMessage) {
+                    System.err.println("can't retrieve ego: " + errorMessage);
+                    setReturnValue(1);
                 }
-                if (issue) {
-                    if (this.unprocessedArgs.length != 2) {
-                        System.err.println("-i/--issue requires exactly two 
positional arguments");
-                        return;
-                    }
-                    runIssue(unprocessedArgs[0], unprocessedArgs[1]);
-                    return;
-                }
-                if (select != null) {
-                    if (this.unprocessedArgs.length != 2) {
-                        System.err.println("-x/--select requires exactly two 
positional argument");
-                        return;
-                    }
-                    runSelect(unprocessedArgs[0], unprocessedArgs[1], select);
-                    return;
-                }
-                if (submit) {
-                    if (this.unprocessedArgs.length != 1) {
-                        System.err.println("-s/--submit requires exactly one 
positional argument");
-                        return;
-                    }
-                    SubmitCommand c = new SubmitCommand(getConfiguration(), 
unprocessedArgs[0]);
-                    c.run();
-                    return;
-                }
-                if (verify) {
-                    if (this.unprocessedArgs.length != 1) {
-                        System.err.println("-V/--verify requires exactly one 
positional argument");
-                        return;
-                    }
-                    runVerify(unprocessedArgs[0]);
-                    return;
-                }
-                if (query) {
-                    if (this.unprocessedArgs.length != 1) {
-                        System.err.println("-q/--query requires exactly one 
positional argument");
-                        return;
-                    }
-                    QueryCommand c = new QueryCommand(getConfiguration(), 
unprocessedArgs[0]);
-                    c.run();
-                    return;
-                }
-            }
-        }.start();
+            });
+        } else {
+            doCommands();
+        }
+    }
 
+    private BallotTool(String[] args) {
+        super(args);
     }
 
+    public static void main(String args[]) {
+        Program tool = new BallotTool(args);
+        tool.start();
+    }
 }

Modified: gnunet-java/src/main/java/org/gnunet/voting/CertifyGroupTool.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/CertifyGroupTool.java   
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/voting/CertifyGroupTool.java   
2013-10-21 18:48:51 UTC (rev 30387)
@@ -20,6 +20,123 @@
 
 package org.gnunet.voting;
 
+import com.google.common.base.Strings;
+import org.gnunet.identity.Identity;
+import org.gnunet.identity.IdentityCallback;
+import org.gnunet.util.AbsoluteTime;
+import org.gnunet.util.Configuration;
+import org.gnunet.util.Program;
+import org.gnunet.util.crypto.EcdsaPublicKey;
+import org.gnunet.util.getopt.Argument;
+import org.gnunet.util.getopt.ArgumentAction;
 
-public class CertifyGroupTool {
+/**
+ * Tool to create group certificates.
+ */
+public class CertifyGroupTool extends Program {
+    @Argument(
+            shortname = "e",
+            longname = "ego",
+            action = ArgumentAction.STORE_STRING,
+            argumentName = "EGONAME",
+            description = "name of the identity ego to use for signing")
+    public String egoName = null;
+
+    @Argument(
+            shortname = "m",
+            longname = "member",
+            action = ArgumentAction.STORE_STRING,
+            argumentName = "KEY",
+            description = "key to certify membership for")
+    String memberPubKeyString;
+
+    @Argument(
+            shortname = "g",
+            longname = "group",
+            action = ArgumentAction.STORE_STRING,
+            argumentName = "NAME",
+            description = "group to certify membership of key for")
+    String groupName;
+
+    @Argument(
+            shortname = "x",
+            longname = "expire",
+            action = ArgumentAction.STORE_STRING,
+            argumentName = "DATE",
+            description = "expiration date, in local time")
+    String expirationDate;
+
+
+    @Override
+    protected String makeHelpText() {
+        return "gnunet-ballot-group-certify [OPTIONS]...\n" +
+               "Create a certificate attesting group membership for a given 
key.\n" +
+               "The resulting certificate file written to standard output.";
+    }
+
+    private CertifyGroupTool(String... args) {
+        super(args);
+    }
+
+    public static void main(String args[]) {
+        CertifyGroupTool tool = new CertifyGroupTool(args);
+        int ret = tool.start();
+        System.exit(ret);
+    }
+
+    private void certify(Identity.Ego ego, EcdsaPublicKey memberPubKey, 
AbsoluteTime expiration) {
+        GroupCert groupCert = GroupCert.create(memberPubKey, groupName, 
ego.getPrivateKey(),
+                ego.getPublicKey(), expiration);
+        Configuration cfg = new Configuration();
+        groupCert.writeGroupCertConfig(cfg);
+        System.out.println(cfg.serialize());
+    }
+
+    @Override
+    protected void run() {
+        if (Strings.isNullOrEmpty(egoName)) {
+            System.err.println("no ego name given");
+            setReturnValue(1);
+            return;
+        }
+        if (null == memberPubKeyString) {
+            System.err.println("no member pubkey given");
+            setReturnValue(1);
+            return;
+        }
+        if (Strings.isNullOrEmpty(groupName)) {
+            System.err.println("no group identifier given");
+            setReturnValue(1);
+            return;
+        }
+        final EcdsaPublicKey memberPubKey = 
EcdsaPublicKey.fromStringUncompressed(memberPubKeyString);
+        if (null == memberPubKey) {
+            System.err.println("not a valid pubkey: '" + memberPubKeyString + 
"'");
+            setReturnValue(1);
+            return;
+        }
+        final AbsoluteTime expiration;
+        if (null == expirationDate) {
+            expiration = AbsoluteTime.FOREVER;
+        } else {
+            expiration = AbsoluteTime.fromString(expirationDate);
+            if (null == expiration) {
+                System.err.println("invalid expiration date");
+                setReturnValue(1);
+                return;
+            }
+        }
+        Identity.lookup(getConfiguration(), egoName, new IdentityCallback() {
+            @Override
+            public void onEgo(Identity.Ego ego) {
+                certify(ego, memberPubKey, expiration);
+            }
+
+            @Override
+            public void onError(String errorMessage) {
+                System.err.println("can't retrieve ego: " + errorMessage);
+                setReturnValue(2);
+            }
+        });
+    }
 }

Added: gnunet-java/src/main/java/org/gnunet/voting/GroupCert.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/GroupCert.java                  
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/voting/GroupCert.java  2013-10-21 
18:48:51 UTC (rev 30387)
@@ -0,0 +1,162 @@
+/*
+ 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.Optional;
+import com.google.common.base.Preconditions;
+import org.gnunet.util.AbsoluteTime;
+import org.gnunet.util.Configuration;
+import org.gnunet.util.crypto.EcdsaPrivateKey;
+import org.gnunet.util.crypto.EcdsaPublicKey;
+import org.gnunet.util.crypto.EcdsaSignature;
+import org.gnunet.util.crypto.EddsaSignature;
+
+/**
+ * Group certificate.  Attests to the fact that a voter (identified
+ * by a ECDSA public key) belongs to a group (given as string identifier) 
until a
+ * the an expiration date (given as timestamp).
+ */
+public class GroupCert {
+    private String group;
+    private EcdsaPublicKey member;
+    private EcdsaPublicKey signerPublicKey;
+    private AbsoluteTime expiration;
+    private EcdsaSignature signature;
+
+    private GroupCert() {
+        // do nothing, just make constructor private
+    }
+    public static GroupCert create(EcdsaPublicKey member, String group, 
EcdsaPrivateKey signerPrivateKey,
+                                   EcdsaPublicKey signerPublicKey, 
AbsoluteTime expiration) {
+        Preconditions.checkNotNull(member);
+        Preconditions.checkNotNull(group);
+        Preconditions.checkNotNull(signerPrivateKey);
+        Preconditions.checkNotNull(signerPublicKey);
+        Preconditions.checkNotNull(expiration);
+        GroupCert groupCert = new GroupCert();
+        groupCert.member = member;
+        groupCert.expiration = expiration;
+        groupCert.group = group;
+        groupCert.signerPublicKey = signerPublicKey;
+        String signData = "";
+        signData += member.toString();
+        signData += expiration.getSeconds();
+        signData += group;
+        signData += signerPublicKey;
+        groupCert.signature = signerPrivateKey.sign(0 /*FIXME*/, 
signData.getBytes());
+        return groupCert;
+
+    }
+
+    public static GroupCert fromBallotConfig(Ballot ballot, Configuration cfg) 
{
+        GroupCert groupCert = new GroupCert();
+        Optional<String> optGroupSig = cfg.getValueString("vote", "GROUP_SIG");
+        Optional<Long> optGroupExp = cfg.getValueNumber("vote", 
"GROUP_EXPIRATION");
+        groupCert.member = ballot.voterPub;
+        groupCert.group = ballot.group;
+        if (!optGroupSig.isPresent()) {
+            throw new InvalidBallotException("missing group cert signature in 
ballot");
+        }
+        groupCert.signature = EcdsaSignature.fromString(optGroupSig.get());
+        if (groupCert.signature == null) {
+            throw new InvalidBallotException("invalid group cert signature in 
ballot");
+        }
+        if (!optGroupExp.isPresent()) {
+            throw new InvalidBallotException("missing or invalid group cert 
expiration in ballot");
+        }
+        groupCert.expiration = AbsoluteTime.fromSeconds(optGroupExp.get());
+        return groupCert;
+    }
+
+    public static GroupCert fromGroupCertConfig(Configuration cfg) {
+        GroupCert groupCert = new GroupCert();
+        Optional<String> optMember = cfg.getValueString("groupcert", "MEMBER");
+        Optional<String> optCa = cfg.getValueString("groupcert", "CA");
+        Optional<String> optSig = cfg.getValueString("groupcert", "SIG");
+        Optional<Long> optExpiration = cfg.getValueNumber("groupcert", 
"EXPIRATION");
+        Optional<String> optGroup = cfg.getValueString("groupcert", "GROUP");
+        if (!optMember.isPresent()) {
+            throw new InvalidGroupCertException("missing member key in group 
cert");
+        }
+        if (!optCa.isPresent()) {
+            throw new InvalidGroupCertException("missing CA in group cert");
+        }
+        if (!optSig.isPresent()) {
+            throw new InvalidGroupCertException("missing signature in group 
cert");
+        }
+        if (!optExpiration.isPresent()) {
+            throw new InvalidGroupCertException("missing expiration in group 
cert");
+        }
+        if (!optGroup.isPresent()) {
+            throw new InvalidGroupCertException("missing group identifier in 
group cert");
+        }
+        groupCert.group = optGroup.get();
+        groupCert.signature = EcdsaSignature.fromString(optSig.get());
+        if (null == groupCert.signature) {
+            throw new InvalidGroupCertException("invalid signature in group 
cert");
+        }
+        groupCert.expiration = AbsoluteTime.fromSeconds(optExpiration.get());
+        groupCert.signerPublicKey = 
EcdsaPublicKey.fromStringUncompressed(optCa.get());
+        if (null == groupCert.signerPublicKey) {
+            throw new InvalidGroupCertException("invalid CA in group cert");
+        }
+        groupCert.member = 
EcdsaPublicKey.fromStringUncompressed(optMember.get());
+        if (null == groupCert.member) {
+            throw new InvalidGroupCertException("invalid member in group 
cert");
+        }
+        return groupCert;
+    }
+
+    /**
+     * Encode the group certificate in a ballot configuration.
+     *
+     * @param cfg ballot configuration
+     */
+    public void writeBallotConfig(Configuration cfg) {
+        cfg.setValueString("vote", "GROUP_SIG", signature.toString());
+        cfg.setValueNumber("vote", "GROUP_EXPIRATION", 
expiration.getSeconds());
+    }
+
+    /**
+     * Encode the group certificate in a group certificate config file.
+     *
+     * @param cfg group cert config file
+     */
+    public void writeGroupCertConfig(Configuration cfg) {
+        cfg.setValueString("groupcert", "MEMBER", member.toString());
+        cfg.setValueString("groupcert", "CA", signerPublicKey.toString());
+        cfg.setValueString("groupcert", "SIG", signature.toString());
+        cfg.setValueNumber("groupcert", "EXPIRATION", expiration.getSeconds());
+        cfg.setValueString("groupcert", "GROUP", group);
+    }
+
+    public EcdsaPublicKey getMemberPublicKey() {
+        return member;
+    }
+
+    public AbsoluteTime getExpiration() {
+        return expiration;
+    }
+
+    public EcdsaSignature getSignature() {
+        return signature;
+    }
+}

Copied: 
gnunet-java/src/main/java/org/gnunet/voting/InvalidGroupCertException.java 
(from rev 30348, 
gnunet-java/src/main/java/org/gnunet/voting/InvalidBallotException.java)
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/InvalidGroupCertException.java  
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/voting/InvalidGroupCertException.java  
2013-10-21 18:48:51 UTC (rev 30387)
@@ -0,0 +1,50 @@
+/*
+ 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;
+
+/**
+ * Exception thrown when a ballot contains insufficient or invalid data.
+ */
+public class InvalidGroupCertException extends RuntimeException {
+    public InvalidGroupCertException(String msg) {
+        super(msg);
+    }
+}

Deleted: gnunet-java/src/main/java/org/gnunet/voting/QueryCommand.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/QueryCommand.java       
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/voting/QueryCommand.java       
2013-10-21 18:48:51 UTC (rev 30387)
@@ -1,120 +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.
- */
-
-/*
- 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);
-    }
-}

Deleted: gnunet-java/src/main/java/org/gnunet/voting/RegisterCommand.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/RegisterCommand.java    
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/voting/RegisterCommand.java    
2013-10-21 18:48:51 UTC (rev 30387)
@@ -1,129 +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.
- */
-
-/*
- 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.BallotRegisterFailureMessage;
-import org.gnunet.voting.messages.BallotRegisterRequestMessage;
-import org.gnunet.voting.messages.BallotRegisterSuccessMessage;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-import java.util.Random;
-
-/**
- * Command for registering a ballot with an authority.
- */
-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(BallotRegisterSuccessMessage m) {
-        submitted = true;
-        System.out.println("ballot 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 void visit(BallotRegisterFailureMessage m) {
-        submitted = true;
-        System.out.println("registering failed: " + m.reason);
-        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);
-    }
-}

Deleted: gnunet-java/src/main/java/org/gnunet/voting/SubmitCommand.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/SubmitCommand.java      
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/voting/SubmitCommand.java      
2013-10-21 18:48:51 UTC (rev 30387)
@@ -1,116 +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.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.util.AbsoluteTime;
-import org.gnunet.util.Configuration;
-import org.gnunet.util.PeerIdentity;
-import org.gnunet.util.crypto.EddsaSignature;
-import org.gnunet.voting.messages.SubmitFailureMessage;
-import org.gnunet.voting.messages.SubmitMessage;
-import org.gnunet.voting.messages.SubmitSuccessMessage;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-import java.util.Random;
-
-/**
- * Command that submits a ballot with a vote to a remaining authority.
- */
-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;
-    private PeerIdentity currentAuthority;
-
-    @Override
-    public void onTunnelEnd(Mesh.Tunnel tunnel) {
-        if (!submitted)
-            throw new AssertionError();
-    }
-
-    public void visit(SubmitSuccessMessage m) {
-        submitted = true;
-        System.out.println("vote successfully submitted");
-        ballot.addConfirmation(currentAuthority, m.confirmationSig);
-        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 void visit(SubmitFailureMessage m) {
-        submitted = true;
-        System.out.println("vote not submitted: " + m.reason);
-        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());
-        currentAuthority = authority;
-        mesh = new Mesh(cfg, null, this);
-        tunnel = mesh.createTunnel(authority, TallyAuthorityDaemon.MESH_PORT, 
true, true, null);
-        SubmitMessage m = new SubmitMessage();
-        if (ballot.voterPub == null) {
-            throw new InvalidBallotException("no voter in ballot");
-        }
-        m.voterPub = ballot.voterPub;
-        // FIXME: implement certs
-        m.groupCertExpiration = AbsoluteTime.FOREVER.asMessage();
-        m.groupCert = EddsaSignature.randomGarbage();
-        m.ballotGuid = ballot.getBallotGuid();
-        m.choiceId = ballot.choiceId;
-        tunnel.send(m);
-    }
-}

Modified: gnunet-java/src/main/java/org/gnunet/voting/TallyAuthorityDaemon.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/TallyAuthorityDaemon.java       
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/voting/TallyAuthorityDaemon.java       
2013-10-21 18:48:51 UTC (rev 30387)
@@ -21,6 +21,13 @@
 
 
 import com.google.common.collect.Maps;
+import org.gnunet.consensus.Consensus;
+import org.gnunet.consensus.ConsensusCallback;
+import org.gnunet.consensus.ConsensusElement;
+import org.gnunet.construct.Construct;
+import org.gnunet.construct.Message;
+import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UInt32;
 import org.gnunet.mesh.Mesh;
 import org.gnunet.mesh.MeshRunabout;
 import org.gnunet.testbed.CompressedConfig;
@@ -44,6 +51,13 @@
     public static final int MESH_PORT = 1002;
     private Mesh mesh;
 
+    public static class Vote implements Message {
+        @UInt32
+        public int choice;
+        @NestedMessage
+        public EcdsaPublicKey voterPub;
+    }
+
     /**
      * All elections known to this authority
      */
@@ -57,17 +71,64 @@
          * The ballot that describes this election.
          */
         Ballot ballot;
+
         /**
          * Set of voters that have submitted their ballot.
          */
         Set<EcdsaPublicKey> voters = new HashSet<EcdsaPublicKey>();
 
         /**
+         * Consensus with the other authorities on the set of ballots.
+         */
+        Consensus consensus;
+
+        boolean consensusDone;
+
+        /**
          * Maping from choice to number of votes for that choice.
          */
         int[] tally;
     }
 
+    static class ElectionConsensusConclude implements ConsensusCallback {
+        private final ElectionState electionState;
+
+        public ElectionConsensusConclude(ElectionState electionState) {
+            this.electionState = electionState;
+        }
+        @Override
+        public void onElement(ConsensusElement element) {
+            System.out.println("got element from consensus");
+            Vote vote = Construct.parseAs(element.data, Vote.class);
+            if (vote.choice >= 0 && vote.choice < electionState.tally.length) {
+                electionState.tally[vote.choice] += 1;
+            }
+        }
+
+        @Override
+        public void onDone() {
+            electionState.consensusDone = true;
+            electionState.consensus.destroy();
+            electionState.consensus = null;
+        }
+    }
+
+    static class ConsensusConcludeTask implements Scheduler.Task {
+        /**
+         * Which election on this authority is the consensus conclude for?
+         */
+        private final ElectionState electionState;
+
+        public ConsensusConcludeTask(ElectionState electionState) {
+            this.electionState = electionState;
+        }
+        @Override
+        public void run(Scheduler.RunContext ctx) {
+            
electionState.consensus.conclude(electionState.ballot.concludeTime.getRemaining(),
+                    new ElectionConsensusConclude(electionState));
+        }
+    }
+
     private class TallyMeshReceiver extends MeshRunabout {
         public void visit(SubmitMessage m) {
             logger.debug("got submit message");
@@ -98,7 +159,11 @@
             // FIXME: check signatures of voter and CA
             else {
                 electionState.voters.add(m.voterPub);
-                electionState.tally[m.choiceId] += 1;
+                Vote vote = new Vote();
+                vote.choice = m.choiceId;
+                vote.voterPub = m.voterPub;
+                byte[] elem = Construct.toBinary(vote);
+                electionState.consensus.insertElement(new 
ConsensusElement(elem, 0));
                 SubmitSuccessMessage sm = new SubmitSuccessMessage();
                 sm.confirmationSig = EddsaSignature.randomGarbage();
                 getSender().send(sm);
@@ -126,15 +191,25 @@
                 BallotRegisterFailureMessage fm = new 
BallotRegisterFailureMessage();
                 fm.reason = "ballot with same GUID already registered";
                 getSender().send(fm);
+                return;
+            }
+            ElectionState electionState = new ElectionState();
+            electionState.tally = new int[b.choices.size()];
+            electionState.ballot = b;
+            electionState.consensus = new Consensus(getConfiguration(),
+                    (PeerIdentity[]) b.getAuthorities().toArray(),
+                    b.getBallotGuid());
+
+            ConsensusConcludeTask t = new ConsensusConcludeTask(electionState);
+            if (b.concludeTime.isDue()) {
+                Scheduler.add(t);
             } else {
-                ElectionState electionState = new ElectionState();
-                electionState.tally = new int[b.choices.size()];
-                electionState.ballot = b;
-                elections.put(guid, electionState);
-                BallotRegisterSuccessMessage rm = new 
BallotRegisterSuccessMessage();
-                rm.registrationSignature = EddsaSignature.randomGarbage();
-                getSender().send(rm);
+                Scheduler.addDelayed(b.concludeTime.getRemaining(), t);
             }
+            elections.put(guid, electionState);
+            BallotRegisterSuccessMessage rm = new 
BallotRegisterSuccessMessage();
+            rm.registrationSignature = EddsaSignature.randomGarbage();
+            getSender().send(rm);
 
         }
 

Modified: 
gnunet-java/src/main/java/org/gnunet/voting/messages/SubmitMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/messages/SubmitMessage.java     
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/java/org/gnunet/voting/messages/SubmitMessage.java     
2013-10-21 18:48:51 UTC (rev 30387)
@@ -5,6 +5,7 @@
 import org.gnunet.construct.UnionCase;
 import org.gnunet.util.*;
 import org.gnunet.util.crypto.EcdsaPublicKey;
+import org.gnunet.util.crypto.EcdsaSignature;
 import org.gnunet.util.crypto.EddsaSignature;
 
 /**
@@ -26,7 +27,7 @@
      * Group certificate of the voter.
      */
     @NestedMessage
-    public EddsaSignature groupCert;
+    public EcdsaSignature groupCert;
     /**
      * Expiration time of the group certificate, checked by the authority.
      */

Modified: gnunet-java/src/main/resources/org/gnunet/construct/MsgMap.txt
===================================================================
--- gnunet-java/src/main/resources/org/gnunet/construct/MsgMap.txt      
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/resources/org/gnunet/construct/MsgMap.txt      
2013-10-21 18:48:51 UTC (rev 30387)
@@ -35,9 +35,11 @@
 org.gnunet.util.GnunetMessage$Body|168=org.gnunet.statistics.SetMessage
 
org.gnunet.util.GnunetMessage$Body|173=org.gnunet.statistics.WatchResponseMessage
 org.gnunet.util.GnunetMessage$Body|172=org.gnunet.statistics.WatchMessage
-org.gnunet.util.GnunetMessage$Body|524=org.gnunet.consensus.ConcludeMessage
-org.gnunet.util.GnunetMessage$Body|521=org.gnunet.consensus.InsertElementMessage
-org.gnunet.util.GnunetMessage$Body|523=org.gnunet.consensus.NewElementMessage
+org.gnunet.util.GnunetMessage$Body|524=org.gnunet.consensus.messages.ConcludeMessage
+org.gnunet.util.GnunetMessage$Body|525=org.gnunet.consensus.messages.ConcludeDoneMessage
+org.gnunet.util.GnunetMessage$Body|520=org.gnunet.consensus.messages.JoinMessage
+org.gnunet.util.GnunetMessage$Body|521=org.gnunet.consensus.messages.InsertElementMessage
+org.gnunet.util.GnunetMessage$Body|523=org.gnunet.consensus.messages.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
@@ -74,14 +76,14 @@
 org.gnunet.util.GnunetMessage$Body|332=org.gnunet.peerinfo.InfoMessage
 org.gnunet.util.GnunetMessage$Body|333=org.gnunet.peerinfo.InfoEnd
 org.gnunet.util.GnunetMessage$Body|331=org.gnunet.peerinfo.ListAllPeersMessage
-org.gnunet.util.GnunetMessage$Body|374=org.gnunet.transport.RequestConnectMessage
-org.gnunet.util.GnunetMessage$Body|369=org.gnunet.transport.BlacklistInitMessage
-org.gnunet.util.GnunetMessage$Body|371=org.gnunet.transport.BlacklistReplyMessage
-org.gnunet.util.GnunetMessage$Body|370=org.gnunet.transport.BlacklistQueryMessage
-org.gnunet.util.GnunetMessage$Body|380=org.gnunet.transport.AddressIterateMessage
+org.gnunet.util.GnunetMessage$Body|374=org.gnunet.transport.messages.RequestConnectMessage
+org.gnunet.util.GnunetMessage$Body|369=org.gnunet.transport.messages.BlacklistInitMessage
+org.gnunet.util.GnunetMessage$Body|371=org.gnunet.transport.messages.BlacklistReplyMessage
+org.gnunet.util.GnunetMessage$Body|370=org.gnunet.transport.messages.BlacklistQueryMessage
+org.gnunet.util.GnunetMessage$Body|380=org.gnunet.transport.messages.AddressIterateMessage
+org.gnunet.util.GnunetMessage$Body|383=org.gnunet.transport.messages.AddressIterateResponseMessage
 
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|360=org.gnunet.transport.messages.StartMessage
 
org.gnunet.util.GnunetMessage$Body|483=org.gnunet.testbed.messages.ManagePeerServiceMessage
-org.gnunet.construct.MessageUnion|525=org.gnunet.consensus.ConcludeDoneMessage
-# generated 2013/10/15 00:11:37
+# generated 2013/10/21 15:16:23

Copied: gnunet-java/src/main/resources/org/gnunet/voting/ballot-template.espec 
(from rev 30348, 
gnunet-java/src/main/resources/org/gnunet/voting/template.espec)
===================================================================
--- gnunet-java/src/main/resources/org/gnunet/voting/ballot-template.espec      
                        (rev 0)
+++ gnunet-java/src/main/resources/org/gnunet/voting/ballot-template.espec      
2013-10-21 18:48:51 UTC (rev 30387)
@@ -0,0 +1,63 @@
+# Ballot template.
+# A Ballot contains public information at various stages of an election.
+
+[election]
+
+# human-readable topic description of the election
+TOPIC =
+
+# choices for the voter, separated by a double slash
+CHOICES = yes//no
+
+# group of elegible voters, voters must be certified by the CA to
+# belong to this group
+GROUP =
+
+# 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
+TIME_START =
+
+# what is the deadline for vote submission?
+# after this deadline, authorities work together to count ballots
+TIME_CLOSING =
+
+# when should consensus over votes conclude
+TIME_CONCLUDE =
+
+# when may results be queried?
+TIME_QUERY =
+
+# when are the results discarded?
+TIME_END =
+
+# public key of the certificate authority
+CA_PUB =
+
+# public key of the election issuer
+# ISSUER_PUB =
+
+
+[authorities]
+# specified as <authority-alias> = <peer-identity>, one entry for each 
authority; e.g.
+# awesome_authority_one = 123abc
+
+
+[registration-signatures]
+# will be filled in by gnunet-vote-call once
+# authorities agreed to participate, with one entry per authority
+# <authority-alias> = <signature>
+
+
+[registration-issuer]
+# the signature of the issuer will be filled in by gnunet-vote-call,
+# signing the sections 'election' and 'authorities'
+# ISSUER_SIGNATURE =
+
+
+[vote]
+# voter signature, pubkey, group cert,
+# encrypted vote and non-interactive zero knowledge proofs come here
+
+
+[confirmations]
+# signatures by the authorities that counted the ballot, in the form of 
<alias> = <sig>

Deleted: gnunet-java/src/main/resources/org/gnunet/voting/template.espec
===================================================================
--- gnunet-java/src/main/resources/org/gnunet/voting/template.espec     
2013-10-21 17:20:42 UTC (rev 30386)
+++ gnunet-java/src/main/resources/org/gnunet/voting/template.espec     
2013-10-21 18:48:51 UTC (rev 30387)
@@ -1,60 +0,0 @@
-# Ballot template.
-# A Ballot contains public information at various stages of an election.
-
-[election]
-
-# human-readable topic description of the election
-TOPIC =
-
-# choices for the voter, separated by a double slash
-CHOICES = yes//no
-
-# group of elegible voters, voters must be certified by the CA to
-# belong to this group
-GROUP =
-
-# 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
-TIME_START =
-
-# what is the deadline for vote submission?
-# after this deadline, authorities work together to count ballots
-TIME_CLOSING =
-
-# when may results be queried?
-TIME_QUERY =
-
-# FIXME: specify more parameters for consensus? which ones?
-# CONSENSUS_PARAMETERS =
-
-# public key of the certificate authority
-CA_PUB =
-
-# public key of the election issuer
-# ISSUER_PUB =
-
-
-[authorities]
-# specified as <authority-alias> = <peer-identity>, one entry for each 
authority; e.g.
-# awesome_authority_one = 123abc
-
-
-[registration-signatures]
-# will be filled in by gnunet-vote-call once
-# authorities agreed to participate, with one entry per authority
-# <authority-alias> = <signature>
-
-
-[registration-issuer]
-# the signature of the issuer will be filled in by gnunet-vote-call,
-# signing the sections 'election' and 'authorities'
-# ISSUER_SIGNATURE =
-
-
-[vote]
-# voter signature, pubkey, group cert,
-# encrypted vote and non-interactive zero knowledge proofs come here
-
-
-[vouchers]
-# signatures by the authorities that counted the ballot, in the form of 
<alias> = <sig>

Added: gnunet-java/src/test/java/org/gnunet/consensus/ConsensusSingleTest.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/consensus/ConsensusSingleTest.java     
                        (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/consensus/ConsensusSingleTest.java     
2013-10-21 18:48:51 UTC (rev 30387)
@@ -0,0 +1,74 @@
+/*
+ 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.consensus;
+
+import org.gnunet.testing.TestingSubsystem;
+import org.gnunet.util.*;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Test consensus with only one peer.
+ */
+public class ConsensusSingleTest {
+    @Test
+    public void test_consensus_single() {
+        Configuration armConf = new Configuration();
+        armConf.setValueString("arm", "DEFAULTSERVICES", "consensus set");
+        Program.configureLogging("DEBUG");
+
+        TestingSubsystem ts = new TestingSubsystem("arm", 
armConf.writeTemp().getAbsolutePath());
+        final Wrapper<Boolean> isDone = new Wrapper<Boolean>(false);
+
+        final Consensus consensus = new Consensus(ts.getConfiguration(), new 
PeerIdentity[0],
+                HashCode.random());
+
+        consensus.insertElement(new ConsensusElement("foo".getBytes(), 0));
+        consensus.insertElement(new ConsensusElement("bar".getBytes(), 0));
+        consensus.insertElement(new ConsensusElement("baz".getBytes(), 0));
+
+        final List<String> received = new LinkedList<String>();
+
+        consensus.conclude(RelativeTime.SECOND, new ConsensusCallback() {
+            @Override
+            public void onElement(ConsensusElement element) {
+                Assert.assertNotNull(element);
+                received.add(new String(element.data));
+            }
+
+            @Override
+            public void onDone() {
+                consensus.destroy();
+                isDone.set(true);
+            }
+        });
+
+        Scheduler.run();
+        Assert.assertTrue(isDone.get());
+        Assert.assertEquals(3, received.size());
+        Assert.assertTrue(received.contains("foo"));
+        Assert.assertTrue(received.contains("bar"));
+        Assert.assertTrue(received.contains("baz"));
+    }
+}

Added: gnunet-java/src/test/java/org/gnunet/consensus/ConsensusTestbedTest.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/consensus/ConsensusTestbedTest.java    
                        (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/consensus/ConsensusTestbedTest.java    
2013-10-21 18:48:51 UTC (rev 30387)
@@ -0,0 +1,180 @@
+package org.gnunet.consensus;
+
+import org.gnunet.testbed.Controller;
+import org.gnunet.testbed.ControllerProc;
+import org.gnunet.testbed.Host;
+import org.gnunet.testbed.SimpleTestbed;
+import org.gnunet.testbed.callbacks.*;
+import org.gnunet.util.*;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Test for consensus with multiple peers using testbed.
+ */
+public class ConsensusTestbedTest {
+    @Test
+    public void test_consensus_testbed_2peers() {
+        final Wrapper<Boolean> done = new Wrapper<Boolean>(false);
+        new Program("-LDEBUG") {
+            ControllerProc cp;
+            Host h;
+            Controller c;
+            Controller.Peer[] peers = new Controller.Peer[2];
+            Consensus[] consensi = new Consensus[2];
+            PeerIdentity[] identities = new PeerIdentity[2];
+            @SuppressWarnings("unchecked")
+            HashSet<String>[] received = new HashSet[]{new HashSet(), new 
HashSet()};
+            int peersCreated;
+            int peersInfoDone;
+            int peersStarted;
+            int peersConcludeDone;
+            HashCode sessionId = HashCode.random();
+
+            class MyConsensusCallback implements ConsensusCallback {
+                int n;
+                public MyConsensusCallback(int n) {
+                    this.n = n;
+                }
+                @Override
+                public void onElement(ConsensusElement element) {
+                    received[n].add(new String(element.data));
+                }
+
+                @Override
+                public void onDone() {
+                    System.out.println("peer " + n + " conclude done");
+                    peersConcludeDone++;
+                    if (peersConcludeDone == 2) {
+                        Assert.assertEquals(4, received[0].size());
+                        Assert.assertEquals(4, received[1].size());
+
+                        done.set(true);
+                        consensi[0].destroy();
+                        consensi[1].destroy();
+                        c.disconnect();
+                        cp.stop();
+                    }
+                }
+            }
+
+            class ConsensusAdapter implements ServiceAdapter {
+                int n;
+                public ConsensusAdapter(int n) {
+                    this.n = n;
+                }
+                @Override
+                public void onConnect(Configuration cfg) {
+                    System.out.println("connecting to consensus");
+                    consensi[n] = new Consensus(cfg, identities, sessionId);
+                    consensi[n].insertElement(new 
ConsensusElement("foo".getBytes(), 0));
+                    consensi[n].insertElement(new 
ConsensusElement("bar".getBytes(), 0));
+                    consensi[n].insertElement(new ConsensusElement(("num" + 
n).getBytes(), 0));
+                    consensi[n].conclude(RelativeTime.SECOND.multiply(10), new 
MyConsensusCallback(n));
+                }
+
+                @Override
+                public void onDisconnect() {
+                    // fixme: why??
+                    consensi[n].destroy();
+                    consensi[n] = null;
+                }
+            }
+
+            class ConnectedHandler implements OperationCompletionCallback {
+                @Override
+                public void onCompletion() {
+                    peers[0].getServiceConnection("consensus", new 
ConsensusAdapter(0));
+                    peers[1].getServiceConnection("consensus", new 
ConsensusAdapter(1));
+                }
+
+                @Override
+                public void onError(String emsg) {
+                    Assert.fail(emsg);
+                }
+            }
+
+            class InfoCallback implements PeerInformationCallback {
+                int n;
+                public InfoCallback(int n) {
+                    this.n = n;
+                }
+                @Override
+                public void onSuccess(PeerIdentity peerIdentity, Configuration 
configuration) {
+                    peersInfoDone++;
+                    identities[n] = peerIdentity;
+                    if (peersInfoDone == 2) {
+                        peers[0].connectOverlay(peers[1], new 
ConnectedHandler());
+                    }
+                }
+            }
+
+            class ChurnStart implements PeerChurnCallback {
+                @Override
+                public void onChurnSuccess() {
+                    peersStarted++;
+                    if (peersStarted == 2) {
+                        peers[0].requestInformation(new InfoCallback(0));
+                        peers[1].requestInformation(new InfoCallback(1));
+                    }
+                }
+
+                @Override
+                public void onChurnError(String emsg) {
+                    Assert.fail(emsg);
+                }
+            }
+
+            class PCB implements PeerCreateCallback {
+                int n;
+                public PCB(int n) {
+                    this.n = n;
+                }
+                @Override
+                public void onPeerCreated(Controller.Peer peer) {
+                    System.out.println("peer created!");
+                    peers[n] = peer;
+                    peersCreated++;
+                    if (peersCreated == 2) {
+                        peers[0].start(new ChurnStart());
+                        peers[1].start(new ChurnStart());
+                    }
+                }
+
+                @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);
+                        // FIXME: use config from resource
+                        Configuration peerCfg = getConfiguration().clone();
+                        peerCfg.setValueString("arm", "DEFAULTSERVICES", "set 
consensus");
+                        c.createPeer(h, peerCfg, new PCB(0));
+                        c.createPeer(h, peerCfg, new PCB(1));
+                    }
+                    @Override
+                    public void onStartupFailure() {
+                        Assert.fail();
+                    }
+                });
+            }
+        }.start();
+
+        Assert.assertTrue(done.get());
+
+    }
+}




reply via email to

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