aboutsummaryrefslogtreecommitdiff
path: root/src/contrib/service
diff options
context:
space:
mode:
Diffstat (limited to 'src/contrib/service')
-rw-r--r--src/contrib/service/Makefile.am3
-rw-r--r--src/contrib/service/consensus/.gitignore6
-rw-r--r--src/contrib/service/consensus/Makefile.am106
-rw-r--r--src/contrib/service/consensus/consensus-simulation.py.in112
-rw-r--r--src/contrib/service/consensus/consensus.conf.in10
-rw-r--r--src/contrib/service/consensus/consensus.h92
-rw-r--r--src/contrib/service/consensus/consensus_api.c346
-rw-r--r--src/contrib/service/consensus/consensus_protocol.h137
-rw-r--r--src/contrib/service/consensus/gnunet-service-consensus.c3572
-rw-r--r--src/contrib/service/consensus/meson.build67
-rw-r--r--src/contrib/service/consensus/plugin_block_consensus.c222
-rw-r--r--src/contrib/service/consensus/test_consensus.conf87
-rw-r--r--src/contrib/service/consensus/test_consensus_api.c120
-rw-r--r--src/contrib/service/secretsharing/.gitignore3
-rw-r--r--src/contrib/service/secretsharing/Makefile.am74
-rw-r--r--src/contrib/service/secretsharing/gnunet-secretsharing-profiler.c662
-rw-r--r--src/contrib/service/secretsharing/gnunet-service-secretsharing.c2414
-rw-r--r--src/contrib/service/secretsharing/meson.build46
-rw-r--r--src/contrib/service/secretsharing/secretsharing.conf.in20
-rw-r--r--src/contrib/service/secretsharing/secretsharing.h227
-rw-r--r--src/contrib/service/secretsharing/secretsharing_api.c492
-rw-r--r--src/contrib/service/secretsharing/secretsharing_common.c159
-rw-r--r--src/contrib/service/secretsharing/secretsharing_protocol.h147
-rw-r--r--src/contrib/service/secretsharing/test_secretsharing.conf40
-rw-r--r--src/contrib/service/secretsharing/test_secretsharing_api.c103
-rw-r--r--src/contrib/service/set/.gitignore7
-rw-r--r--src/contrib/service/set/Makefile.am122
-rw-r--r--src/contrib/service/set/gnunet-service-set.c1983
-rw-r--r--src/contrib/service/set/gnunet-service-set.h665
-rw-r--r--src/contrib/service/set/gnunet-service-set_intersection.c1324
-rw-r--r--src/contrib/service/set/gnunet-service-set_intersection.h79
-rw-r--r--src/contrib/service/set/gnunet-service-set_protocol.h226
-rw-r--r--src/contrib/service/set/gnunet-service-set_union.c2463
-rw-r--r--src/contrib/service/set/gnunet-service-set_union.h246
-rw-r--r--src/contrib/service/set/gnunet-service-set_union_strata_estimator.c297
-rw-r--r--src/contrib/service/set/gnunet-service-set_union_strata_estimator.h169
-rw-r--r--src/contrib/service/set/gnunet-set-ibf-profiler.c308
-rw-r--r--src/contrib/service/set/gnunet-set-profiler.c508
-rw-r--r--src/contrib/service/set/ibf.c410
-rw-r--r--src/contrib/service/set/ibf.h256
-rw-r--r--src/contrib/service/set/ibf_sim.c143
-rw-r--r--src/contrib/service/set/meson.build52
-rw-r--r--src/contrib/service/set/plugin_block_set_test.c167
-rw-r--r--src/contrib/service/set/set.conf.in12
-rw-r--r--src/contrib/service/set/set.h400
-rw-r--r--src/contrib/service/set/set_api.c1199
-rw-r--r--src/contrib/service/set/test_set.conf33
-rw-r--r--src/contrib/service/set/test_set_api.c403
-rw-r--r--src/contrib/service/set/test_set_intersection_result_full.c393
-rw-r--r--src/contrib/service/set/test_set_union_copy.c311
-rw-r--r--src/contrib/service/set/test_set_union_result_symmetric.c455
51 files changed, 21898 insertions, 0 deletions
diff --git a/src/contrib/service/Makefile.am b/src/contrib/service/Makefile.am
index d7cb9946d..cfab56206 100644
--- a/src/contrib/service/Makefile.am
+++ b/src/contrib/service/Makefile.am
@@ -7,4 +7,7 @@ endif
7 7
8SUBDIRS = \ 8SUBDIRS = \
9 template \ 9 template \
10 set \
11 consensus \
12 secretsharing \
10 $(EXP_DIR) 13 $(EXP_DIR)
diff --git a/src/contrib/service/consensus/.gitignore b/src/contrib/service/consensus/.gitignore
new file mode 100644
index 000000000..8050d760e
--- /dev/null
+++ b/src/contrib/service/consensus/.gitignore
@@ -0,0 +1,6 @@
1gnunet-service-evil-consensus
2gnunet-consensus-profiler
3gnunet-service-consensus
4test_consensus_api
5resource.log.master
6consensus-simulation.py
diff --git a/src/contrib/service/consensus/Makefile.am b/src/contrib/service/consensus/Makefile.am
new file mode 100644
index 000000000..69405b90e
--- /dev/null
+++ b/src/contrib/service/consensus/Makefile.am
@@ -0,0 +1,106 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4pkgcfgdir= $(pkgdatadir)/config.d/
5
6libexecdir= $(pkglibdir)/libexec/
7
8plugindir = $(libdir)/gnunet
9
10pkgcfg_DATA = \
11 consensus.conf
12
13if USE_COVERAGE
14 AM_CFLAGS = -fprofile-arcs -ftest-coverage
15endif
16
17
18libexec_PROGRAMS = \
19 gnunet-service-consensus
20
21if ENABLE_MALICIOUS
22libexec_PROGRAMS += \
23 gnunet-service-evil-consensus
24endif
25
26SUFFIXES = .py.in .py
27
28.py.in.py:
29 $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/bin/dosubst.awk < $< > $@
30 chmod +x $@
31
32check-python-style:
33 flake8 consensus-simulation.py.in
34
35lib_LTLIBRARIES = \
36 libgnunetconsensus.la
37
38gnunet_service_consensus_SOURCES = \
39 gnunet-service-consensus.c
40gnunet_service_consensus_LDADD = \
41 $(top_builddir)/src/lib/util/libgnunetutil.la \
42 $(top_builddir)/src/service/core/libgnunetcore.la \
43 $(top_builddir)/src/contrib/service/set/libgnunetset.la \
44 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
45 $(GN_LIBINTL)
46
47gnunet_service_evil_consensus_SOURCES = \
48 gnunet-service-consensus.c \
49 consensus_protocol.h
50gnunet_service_evil_consensus_LDADD = \
51 $(top_builddir)/src/lib/util/libgnunetutil.la \
52 $(top_builddir)/src/service/core/libgnunetcore.la \
53 $(top_builddir)/src/contrib/service/set/libgnunetset.la \
54 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
55 $(GN_LIBINTL)
56gnunet_service_evil_consensus_CFLAGS = -DEVIL
57
58libgnunetconsensus_la_SOURCES = \
59 consensus_api.c \
60 consensus.h
61libgnunetconsensus_la_LIBADD = \
62 $(top_builddir)/src/lib/util/libgnunetutil.la \
63 $(LTLIBINTL)
64libgnunetconsensus_la_LDFLAGS = \
65 $(GN_LIB_LDFLAGS)
66
67
68plugin_LTLIBRARIES = \
69 libgnunet_plugin_block_consensus.la
70
71libgnunet_plugin_block_consensus_la_SOURCES = \
72 plugin_block_consensus.c
73libgnunet_plugin_block_consensus_la_LIBADD = \
74 $(top_builddir)/src/lib/block/libgnunetblock.la \
75 $(top_builddir)/src/lib/block/libgnunetblockgroup.la \
76 $(top_builddir)/src/lib/util/libgnunetutil.la \
77 $(LTLIBINTL)
78libgnunet_plugin_block_consensus_la_LDFLAGS = \
79 $(GN_PLUGIN_LDFLAGS)
80
81
82
83check_PROGRAMS = \
84 test_consensus_api
85
86if ENABLE_TEST_RUN
87AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
88TESTS = $(check_PROGRAMS)
89endif
90
91test_consensus_api_SOURCES = \
92 test_consensus_api.c
93test_consensus_api_LDADD = \
94 $(top_builddir)/src/lib/util/libgnunetutil.la \
95 $(top_builddir)/src/service/testing/libgnunettesting.la \
96 libgnunetconsensus.la
97
98noinst_SCRIPTS = \
99 consensus-simulation.py
100
101CLEANFILES = \
102 $(noinst_SCRIPTS)
103
104EXTRA_DIST = \
105 test_consensus.conf \
106 consensus-simulation.py.in
diff --git a/src/contrib/service/consensus/consensus-simulation.py.in b/src/contrib/service/consensus/consensus-simulation.py.in
new file mode 100644
index 000000000..272a52da2
--- /dev/null
+++ b/src/contrib/service/consensus/consensus-simulation.py.in
@@ -0,0 +1,112 @@
1#!@PYTHONEXE@
2# This file is part of GNUnet
3# (C) 2013, 2018 Christian Grothoff (and other contributing authors)
4#
5# GNUnet is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Affero General Public License as published
7# by the Free Software Foundation, either version 3 of the License,
8# or (at your option) any later version.
9#
10# GNUnet is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# Affero General Public License for more details.
14#
15# You should have received a copy of the GNU Affero General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17#
18# SPDX-License-Identifier: AGPL3.0-or-later
19
20import argparse
21import random
22from math import ceil, log, floor
23
24
25def bsc(n):
26 """ count the bits set in n"""
27 l = n.bit_length()
28 c = 0
29 x = 1
30 for _ in range(0, l):
31 if n & x:
32 c = c + 1
33 x = x << 1
34 return c
35
36
37def simulate(k, n, verbose):
38 assert k < n
39 largest_arc = int(2**ceil(log(n, 2))) // 2
40 num_ghosts = (2 * largest_arc) - n
41 if verbose:
42 print("we have", num_ghosts, "ghost peers")
43 # n.b. all peers with idx<k are evil
44 peers = list(range(n))
45 info = [1 << x for x in range(n)]
46
47 def done_p():
48 for x in range(k, n):
49 if bsc(info[x]) < n - k:
50 return False
51 return True
52
53 rounds = 0
54 while not done_p():
55 if verbose:
56 print("-- round --")
57 arc = 1
58 while arc <= largest_arc:
59 if verbose:
60 print("-- subround --")
61 new_info = [x for x in info]
62 for peer_physical in range(n):
63 peer_logical = peers[peer_physical]
64 peer_type = None
65 partner_logical = (peer_logical + arc) % n
66 partner_physical = peers.index(partner_logical)
67 if peer_physical < k or partner_physical < k:
68 if verbose:
69 print(
70 "bad peer in connection", peer_physical, "--",
71 partner_physical
72 )
73 continue
74 if peer_logical & arc == 0:
75 # we are outgoing
76 if verbose:
77 print(peer_physical, "connects to", partner_physical)
78 peer_type = "outgoing"
79 if peer_logical < num_ghosts:
80 # we have a ghost, check if the peer who connects
81 # to our ghost is actually outgoing
82 ghost_partner_logical = (peer_logical - arc) % n
83 if ghost_partner_logical & arc == 0:
84 peer_type = peer_type + ", ghost incoming"
85 new_info[peer_physical] = new_info[peer_physical] | info[
86 peer_physical] | info[partner_physical]
87 new_info[partner_physical
88 ] = new_info[partner_physical] | info[
89 peer_physical] | info[partner_physical]
90 else:
91 peer_type = "incoming"
92 if verbose > 1:
93 print("type of", str(peer_physical) + ":", peer_type)
94 info = new_info
95 arc = arc << 1
96 rounds = rounds + 1
97 random.shuffle(peers)
98 return rounds
99
100
101if __name__ == "__main__":
102 parser = argparse.ArgumentParser()
103 parser.add_argument("k", metavar="k", type=int, help="#(bad peers)")
104 parser.add_argument("n", metavar="n", type=int, help="#(all peers)")
105 parser.add_argument("r", metavar="r", type=int, help="#(rounds)")
106 parser.add_argument('--verbose', '-v', action='count')
107
108 args = parser.parse_args()
109 sum = 0.0
110 for n in range(0, args.r):
111 sum += simulate(args.k, args.n, args.verbose)
112 print(sum // args.r)
diff --git a/src/contrib/service/consensus/consensus.conf.in b/src/contrib/service/consensus/consensus.conf.in
new file mode 100644
index 000000000..b0fbcaf5a
--- /dev/null
+++ b/src/contrib/service/consensus/consensus.conf.in
@@ -0,0 +1,10 @@
1[consensus]
2START_ON_DEMAND = @START_ON_DEMAND@
3@JAVAPORT@PORT = 2103
4HOSTNAME = localhost
5BINARY = gnunet-service-consensus
6ACCEPT_FROM = 127.0.0.1;
7ACCEPT_FROM6 = ::1;
8UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-consensus.sock
9UNIX_MATCH_UID = YES
10UNIX_MATCH_GID = YES
diff --git a/src/contrib/service/consensus/consensus.h b/src/contrib/service/consensus/consensus.h
new file mode 100644
index 000000000..888213d55
--- /dev/null
+++ b/src/contrib/service/consensus/consensus.h
@@ -0,0 +1,92 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @author Florian Dold
23 * @file consensus/consensus.h
24 * @brief
25 */
26#ifndef CONSENSUS_H
27#define CONSENSUS_H
28
29#include "gnunet_common.h"
30
31GNUNET_NETWORK_STRUCT_BEGIN
32
33/**
34 * Sent by the client to the service,
35 * when the client wants the service to join a consensus session.
36 */
37struct GNUNET_CONSENSUS_JoinMessage
38{
39 /**
40 * Type: GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_JOIN
41 */
42 struct GNUNET_MessageHeader header;
43
44 /**
45 * Number of peers (at the end of this message) that want to
46 * participate in the consensus.
47 */
48 uint32_t num_peers GNUNET_PACKED;
49
50 /**
51 * Session id of the consensus.
52 */
53 struct GNUNET_HashCode session_id;
54
55 /**
56 * Start time for the consensus.
57 */
58 struct GNUNET_TIME_AbsoluteNBO start;
59
60 /**
61 * Deadline for conclude.
62 */
63 struct GNUNET_TIME_AbsoluteNBO deadline;
64
65 /* GNUNET_PeerIdentity[num_peers] */
66};
67
68
69/**
70 * Message with an element
71 */
72struct GNUNET_CONSENSUS_ElementMessage
73{
74 /**
75 * Type:
76 * Either GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_RECEIVED_ELEMENT
77 * or GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_INSERT_ELEMENT
78 */
79 struct GNUNET_MessageHeader header;
80
81 /**
82 * Type: GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_NEW_ELEMENT
83 */
84 uint16_t element_type GNUNET_PACKED; /* FIXME: alignment? => uint32_t */
85
86 /* rest: element data */
87};
88
89
90GNUNET_NETWORK_STRUCT_END
91
92#endif
diff --git a/src/contrib/service/consensus/consensus_api.c b/src/contrib/service/consensus/consensus_api.c
new file mode 100644
index 000000000..01d0ad3de
--- /dev/null
+++ b/src/contrib/service/consensus/consensus_api.c
@@ -0,0 +1,346 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file consensus/consensus_api.c
23 * @brief
24 * @author Florian Dold
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_protocols.h"
29#include "gnunet_consensus_service.h"
30#include "consensus.h"
31
32
33#define LOG(kind, ...) GNUNET_log_from (kind, "consensus-api", __VA_ARGS__)
34
35
36/**
37 * Handle for the service.
38 */
39struct GNUNET_CONSENSUS_Handle
40{
41 /**
42 * Configuration to use.
43 */
44 const struct GNUNET_CONFIGURATION_Handle *cfg;
45
46 /**
47 * Callback for new elements. Not called for elements added locally.
48 */
49 GNUNET_CONSENSUS_ElementCallback new_element_cb;
50
51 /**
52 * Closure for @e new_element_cb
53 */
54 void *new_element_cls;
55
56 /**
57 * The (local) session identifier for the consensus session.
58 */
59 struct GNUNET_HashCode session_id;
60
61 /**
62 * #GNUNET_YES iff the join message has been sent to the service.
63 */
64 int joined;
65
66 /**
67 * Called when the conclude operation finishes or fails.
68 */
69 GNUNET_CONSENSUS_ConcludeCallback conclude_cb;
70
71 /**
72 * Closure for the @e conclude_cb callback.
73 */
74 void *conclude_cls;
75
76 /**
77 * Deadline for the conclude operation.
78 */
79 struct GNUNET_TIME_Absolute conclude_deadline;
80
81 /**
82 * Message queue for the client.
83 */
84 struct GNUNET_MQ_Handle *mq;
85};
86
87
88/**
89 * FIXME: this should not bee necessary when the API
90 * issue has been fixed
91 */
92struct InsertDoneInfo
93{
94 GNUNET_CONSENSUS_InsertDoneCallback idc;
95 void *cls;
96};
97
98
99/**
100 * Called when the server has sent is a new element
101 *
102 * @param cls consensus handle
103 * @param msg element message
104 */
105static int
106check_new_element (void *cls,
107 const struct GNUNET_CONSENSUS_ElementMessage *msg)
108{
109 /* any size is fine, elements are variable-size */
110 return GNUNET_OK;
111}
112
113
114/**
115 * Called when the server has sent is a new element
116 *
117 * @param cls consensus handle
118 * @param msg element message
119 */
120static void
121handle_new_element (void *cls,
122 const struct GNUNET_CONSENSUS_ElementMessage *msg)
123{
124 struct GNUNET_CONSENSUS_Handle *consensus = cls;
125 struct GNUNET_SET_Element element;
126
127 LOG (GNUNET_ERROR_TYPE_DEBUG,
128 "received new element\n");
129 element.element_type = msg->element_type;
130 element.size = ntohs (msg->header.size) - sizeof(struct
131 GNUNET_CONSENSUS_ElementMessage);
132 element.data = &msg[1];
133 consensus->new_element_cb (consensus->new_element_cls,
134 &element);
135}
136
137
138/**
139 * Called when the server has announced
140 * that the conclusion is over.
141 *
142 * @param cls consensus handle
143 * @param msg conclude done message
144 */
145static void
146handle_conclude_done (void *cls,
147 const struct GNUNET_MessageHeader *msg)
148{
149 struct GNUNET_CONSENSUS_Handle *consensus = cls;
150 GNUNET_CONSENSUS_ConcludeCallback cc;
151
152 GNUNET_MQ_destroy (consensus->mq);
153 consensus->mq = NULL;
154 GNUNET_assert (NULL != (cc = consensus->conclude_cb));
155 consensus->conclude_cb = NULL;
156 cc (consensus->conclude_cls);
157}
158
159
160/**
161 * Generic error handler, called with the appropriate
162 * error code and the same closure specified at the creation of
163 * the message queue.
164 * Not every message queue implementation supports an error handler.
165 *
166 * @param cls closure, same closure as for the message handlers
167 * @param error error code
168 */
169static void
170mq_error_handler (void *cls,
171 enum GNUNET_MQ_Error error)
172{
173 LOG (GNUNET_ERROR_TYPE_WARNING,
174 "consensus service disconnected us\n");
175}
176
177
178/**
179 * Create a consensus session.
180 *
181 * @param cfg configuration to use for connecting to the consensus service
182 * @param num_peers number of peers in the peers array
183 * @param peers array of peers participating in this consensus session
184 * Inclusion of the local peer is optional.
185 * @param session_id session identifier
186 * Allows a group of peers to have more than consensus session.
187 * @param start start time of the consensus, conclude should be called before
188 * the start time.
189 * @param deadline time when the consensus should have concluded
190 * @param new_element_cb callback, called when a new element is added to the set by
191 * another peer
192 * @param new_element_cls closure for new_element
193 * @return handle to use, NULL on error
194 */
195struct GNUNET_CONSENSUS_Handle *
196GNUNET_CONSENSUS_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
197 unsigned int num_peers,
198 const struct GNUNET_PeerIdentity *peers,
199 const struct GNUNET_HashCode *session_id,
200 struct GNUNET_TIME_Absolute start,
201 struct GNUNET_TIME_Absolute deadline,
202 GNUNET_CONSENSUS_ElementCallback new_element_cb,
203 void *new_element_cls)
204{
205 struct GNUNET_CONSENSUS_Handle *consensus
206 = GNUNET_new (struct GNUNET_CONSENSUS_Handle);
207 struct GNUNET_MQ_MessageHandler mq_handlers[] = {
208 GNUNET_MQ_hd_var_size (new_element,
209 GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_RECEIVED_ELEMENT,
210 struct GNUNET_CONSENSUS_ElementMessage,
211 consensus),
212 GNUNET_MQ_hd_fixed_size (conclude_done,
213 GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_CONCLUDE_DONE,
214 struct GNUNET_MessageHeader,
215 consensus),
216 GNUNET_MQ_handler_end ()
217 };
218 struct GNUNET_CONSENSUS_JoinMessage *join_msg;
219 struct GNUNET_MQ_Envelope *ev;
220
221 consensus->cfg = cfg;
222 consensus->new_element_cb = new_element_cb;
223 consensus->new_element_cls = new_element_cls;
224 consensus->session_id = *session_id;
225 consensus->mq = GNUNET_CLIENT_connect (cfg,
226 "consensus",
227 mq_handlers,
228 &mq_error_handler,
229 consensus);
230 if (NULL == consensus->mq)
231 {
232 GNUNET_free (consensus);
233 return NULL;
234 }
235 ev = GNUNET_MQ_msg_extra (join_msg,
236 (num_peers * sizeof(struct GNUNET_PeerIdentity)),
237 GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_JOIN);
238
239 join_msg->session_id = consensus->session_id;
240 join_msg->start = GNUNET_TIME_absolute_hton (start);
241 join_msg->deadline = GNUNET_TIME_absolute_hton (deadline);
242 join_msg->num_peers = htonl (num_peers);
243 GNUNET_memcpy (&join_msg[1],
244 peers,
245 num_peers * sizeof(struct GNUNET_PeerIdentity));
246
247 GNUNET_MQ_send (consensus->mq, ev);
248 return consensus;
249}
250
251
252static void
253idc_adapter (void *cls)
254{
255 struct InsertDoneInfo *i = cls;
256
257 i->idc (i->cls, GNUNET_OK);
258 GNUNET_free (i);
259}
260
261
262/**
263 * Insert an element in the set being reconsiled. Must not be called after
264 * "GNUNET_CONSENSUS_conclude".
265 *
266 * @param consensus handle for the consensus session
267 * @param element the element to be inserted
268 * @param idc function called when we are done with this element and it
269 * is thus allowed to call GNUNET_CONSENSUS_insert again
270 * @param idc_cls closure for 'idc'
271 */
272void
273GNUNET_CONSENSUS_insert (struct GNUNET_CONSENSUS_Handle *consensus,
274 const struct GNUNET_SET_Element *element,
275 GNUNET_CONSENSUS_InsertDoneCallback idc,
276 void *idc_cls)
277{
278 struct GNUNET_CONSENSUS_ElementMessage *element_msg;
279 struct GNUNET_MQ_Envelope *ev;
280 struct InsertDoneInfo *i;
281
282 LOG (GNUNET_ERROR_TYPE_DEBUG, "inserting, size=%u\n", element->size);
283
284 ev = GNUNET_MQ_msg_extra (element_msg, element->size,
285 GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_INSERT);
286
287 GNUNET_memcpy (&element_msg[1], element->data, element->size);
288
289 if (NULL != idc)
290 {
291 i = GNUNET_new (struct InsertDoneInfo);
292 i->idc = idc;
293 i->cls = idc_cls;
294 GNUNET_MQ_notify_sent (ev, idc_adapter, i);
295 }
296 GNUNET_MQ_send (consensus->mq, ev);
297}
298
299
300/**
301 * We are done with inserting new elements into the consensus;
302 * try to conclude the consensus within a given time window.
303 * After conclude has been called, no further elements may be
304 * inserted by the client.
305 *
306 * @param consensus consensus session
307 * @param conclude called when the conclusion was successful
308 * @param conclude_cls closure for the conclude callback
309 */
310void
311GNUNET_CONSENSUS_conclude (struct GNUNET_CONSENSUS_Handle *consensus,
312 GNUNET_CONSENSUS_ConcludeCallback conclude,
313 void *conclude_cls)
314{
315 struct GNUNET_MQ_Envelope *ev;
316
317 GNUNET_assert (NULL != conclude);
318 GNUNET_assert (NULL == consensus->conclude_cb);
319
320 consensus->conclude_cls = conclude_cls;
321 consensus->conclude_cb = conclude;
322
323 ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_CONCLUDE);
324 GNUNET_MQ_send (consensus->mq, ev);
325}
326
327
328/**
329 * Destroy a consensus handle (free all state associated with
330 * it, no longer call any of the callbacks).
331 *
332 * @param consensus handle to destroy
333 */
334void
335GNUNET_CONSENSUS_destroy (struct GNUNET_CONSENSUS_Handle *consensus)
336{
337 if (NULL != consensus->mq)
338 {
339 GNUNET_MQ_destroy (consensus->mq);
340 consensus->mq = NULL;
341 }
342 GNUNET_free (consensus);
343}
344
345
346/* end of consensus_api.c */
diff --git a/src/contrib/service/consensus/consensus_protocol.h b/src/contrib/service/consensus/consensus_protocol.h
new file mode 100644
index 000000000..0afd56b27
--- /dev/null
+++ b/src/contrib/service/consensus/consensus_protocol.h
@@ -0,0 +1,137 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21
22/**
23 * @file consensus/consensus_protocol.h
24 * @brief p2p message definitions for consensus
25 * @author Florian Dold
26 */
27
28#ifndef GNUNET_CONSENSUS_PROTOCOL_H
29#define GNUNET_CONSENSUS_PROTOCOL_H
30
31#include "platform.h"
32#include "gnunet_util_lib.h"
33#include "gnunet_common.h"
34#include "gnunet_protocols.h"
35
36
37GNUNET_NETWORK_STRUCT_BEGIN
38
39/**
40 * Sent as context message for set reconciliation.
41 *
42 * Essentially contains all the fields
43 * from 'struct TaskKey', but in NBO.
44 */
45struct GNUNET_CONSENSUS_RoundContextMessage
46{
47 /**
48 * Type: #GNUNET_MESSAGE_TYPE_CONSENSUS_P2P_ROUND_CONTEXT
49 */
50 struct GNUNET_MessageHeader header;
51
52 /**
53 * A value from 'enum PhaseKind'.
54 */
55 uint16_t kind GNUNET_PACKED;
56
57 /**
58 * Number of the first peer
59 * in canonical order.
60 */
61 int16_t peer1 GNUNET_PACKED;
62
63 /**
64 * Number of the second peer in canonical order.
65 */
66 int16_t peer2 GNUNET_PACKED;
67
68 /**
69 * Repetition of the gradecast phase.
70 */
71 int16_t repetition GNUNET_PACKED;
72
73 /**
74 * Leader in the gradecast phase.
75 *
76 * Can be different from both peer1 and peer2.
77 */
78 int16_t leader GNUNET_PACKED;
79
80 /**
81 * Non-zero if this set reconciliation
82 * had elements removed because they were contested.
83 *
84 * Will be considered when grading broadcasts.
85 *
86 * Ignored for set operations that are not within gradecasts.
87 */
88 uint16_t is_contested GNUNET_PACKED;
89};
90
91
92enum
93{
94 CONSENSUS_MARKER_CONTESTED = 1,
95 CONSENSUS_MARKER_SIZE = 2,
96};
97
98
99/**
100 * Consensus element, either marker or payload.
101 */
102struct ConsensusElement
103{
104 /**
105 * Payload element_type, only valid
106 * if this is not a marker element.
107 */
108 uint16_t payload_type GNUNET_PACKED;
109
110 /**
111 * Is this a marker element?
112 */
113 uint8_t marker;
114
115 /* rest: element data */
116};
117
118
119struct ConsensusSizeElement
120{
121 struct ConsensusElement ce;
122
123 uint64_t size GNUNET_PACKED;
124 uint8_t sender_index;
125};
126
127
128struct ConsensusStuffedElement
129{
130 struct ConsensusElement ce;
131 struct GNUNET_HashCode rand GNUNET_PACKED;
132};
133
134
135GNUNET_NETWORK_STRUCT_END
136
137#endif
diff --git a/src/contrib/service/consensus/gnunet-service-consensus.c b/src/contrib/service/consensus/gnunet-service-consensus.c
new file mode 100644
index 000000000..5b6b9bbd7
--- /dev/null
+++ b/src/contrib/service/consensus/gnunet-service-consensus.c
@@ -0,0 +1,3572 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012, 2013, 2017, 2020 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file consensus/gnunet-service-consensus.c
22 * @brief multi-peer set reconciliation
23 * @author Florian Dold <flo@dold.me>
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_block_lib.h"
28#include "gnunet_protocols.h"
29#include "gnunet_applications.h"
30#include "gnunet_set_service.h"
31#include "gnunet_statistics_service.h"
32#include "gnunet_consensus_service.h"
33#include "consensus_protocol.h"
34#include "consensus.h"
35
36
37enum ReferendumVote
38{
39 /**
40 * Vote that nothing should change.
41 * This option is never voted explicitly.
42 */
43 VOTE_STAY = 0,
44 /**
45 * Vote that an element should be added.
46 */
47 VOTE_ADD = 1,
48 /**
49 * Vote that an element should be removed.
50 */
51 VOTE_REMOVE = 2,
52};
53
54
55enum EarlyStoppingPhase
56{
57 EARLY_STOPPING_NONE = 0,
58 EARLY_STOPPING_ONE_MORE = 1,
59 EARLY_STOPPING_DONE = 2,
60};
61
62
63enum PhaseKind
64{
65 PHASE_KIND_ALL_TO_ALL,
66 PHASE_KIND_ALL_TO_ALL_2,
67 PHASE_KIND_GRADECAST_LEADER,
68 PHASE_KIND_GRADECAST_ECHO,
69 PHASE_KIND_GRADECAST_ECHO_GRADE,
70 PHASE_KIND_GRADECAST_CONFIRM,
71 PHASE_KIND_GRADECAST_CONFIRM_GRADE,
72 /**
73 * Apply a repetition of the all-to-all
74 * gradecast to the current set.
75 */
76 PHASE_KIND_APPLY_REP,
77 PHASE_KIND_FINISH,
78};
79
80
81enum SetKind
82{
83 SET_KIND_NONE = 0,
84 SET_KIND_CURRENT,
85 /**
86 * Last result set from a gradecast
87 */
88 SET_KIND_LAST_GRADECAST,
89 SET_KIND_LEADER_PROPOSAL,
90 SET_KIND_ECHO_RESULT,
91};
92
93enum DiffKind
94{
95 DIFF_KIND_NONE = 0,
96 DIFF_KIND_LEADER_PROPOSAL,
97 DIFF_KIND_LEADER_CONSENSUS,
98 DIFF_KIND_GRADECAST_RESULT,
99};
100
101enum RfnKind
102{
103 RFN_KIND_NONE = 0,
104 RFN_KIND_ECHO,
105 RFN_KIND_CONFIRM,
106 RFN_KIND_GRADECAST_RESULT
107};
108
109
110GNUNET_NETWORK_STRUCT_BEGIN
111
112/**
113 * Tuple of integers that together identify a task uniquely.
114 */
115struct TaskKey
116{
117 /**
118 * A value from 'enum PhaseKind'.
119 */
120 uint16_t kind GNUNET_PACKED;
121
122 /**
123 * Number of the first peer
124 * in canonical order.
125 */
126 int16_t peer1 GNUNET_PACKED;
127
128 /**
129 * Number of the second peer in canonical order.
130 */
131 int16_t peer2 GNUNET_PACKED;
132
133 /**
134 * Repetition of the gradecast phase.
135 */
136 int16_t repetition GNUNET_PACKED;
137
138 /**
139 * Leader in the gradecast phase.
140 *
141 * Can be different from both peer1 and peer2.
142 */
143 int16_t leader GNUNET_PACKED;
144};
145
146
147struct SetKey
148{
149 enum SetKind set_kind GNUNET_PACKED;
150 /**
151 * Repetition counter.
152 */
153 int k1 GNUNET_PACKED;
154 /**
155 * Leader (or 0).
156 */
157 int k2 GNUNET_PACKED;
158};
159
160
161struct SetEntry
162{
163 struct SetKey key;
164 struct GNUNET_SET_Handle *h;
165
166 /**
167 * #GNUNET_YES if the set resulted from applying a referendum with contested
168 * elements.
169 */
170 int is_contested;
171};
172
173
174struct DiffKey
175{
176 enum DiffKind diff_kind GNUNET_PACKED;
177
178 int k1 GNUNET_PACKED;
179
180 int k2 GNUNET_PACKED;
181};
182
183struct RfnKey
184{
185 enum RfnKind rfn_kind GNUNET_PACKED;
186 int k1 GNUNET_PACKED;
187 int k2 GNUNET_PACKED;
188};
189
190
191GNUNET_NETWORK_STRUCT_END
192
193
194struct SetOpCls
195{
196 struct SetKey input_set;
197
198 struct SetKey output_set;
199 struct RfnKey output_rfn;
200 struct DiffKey output_diff;
201
202 int do_not_remove;
203
204 int transceive_contested;
205
206 struct GNUNET_SET_OperationHandle *op;
207};
208
209
210struct FinishCls
211{
212 struct SetKey input_set;
213};
214
215/**
216 * Closure for both @a start_task
217 * and @a cancel_task.
218 */
219union TaskFuncCls
220{
221 struct SetOpCls setop;
222 struct FinishCls finish;
223};
224
225
226struct TaskEntry;
227
228
229typedef void
230(*TaskFunc) (struct TaskEntry *task);
231
232
233/*
234 * Node in the consensus task graph.
235 */
236struct TaskEntry
237{
238 struct TaskKey key;
239
240 struct Step *step;
241
242 int is_started;
243
244 int is_finished;
245
246 TaskFunc start;
247 TaskFunc cancel;
248
249 union TaskFuncCls cls;
250};
251
252
253struct Step
254{
255 /**
256 * All steps of one session are in a
257 * linked list for easier deallocation.
258 */
259 struct Step *prev;
260
261 /**
262 * All steps of one session are in a
263 * linked list for easier deallocation.
264 */
265 struct Step *next;
266
267 struct ConsensusSession *session;
268
269 /**
270 * Tasks that this step is composed of.
271 */
272 struct TaskEntry **tasks;
273 unsigned int tasks_len;
274 unsigned int tasks_cap;
275
276 unsigned int finished_tasks;
277
278 /*
279 * Tasks that have this task as dependency.
280 *
281 * We store pointers to subordinates rather
282 * than to prerequisites since it makes
283 * tracking the readiness of a task easier.
284 */
285 struct Step **subordinates;
286 unsigned int subordinates_len;
287 unsigned int subordinates_cap;
288
289 /**
290 * Counter for the prerequisites of this step.
291 */
292 size_t pending_prereq;
293
294 /**
295 * Task that will run this step despite any pending prerequisites.
296 */
297 struct GNUNET_SCHEDULER_Task *timeout_task;
298
299 unsigned int is_running;
300
301 unsigned int is_finished;
302
303 /**
304 * Synchrony round of the task. Determines the deadline for the task.
305 */
306 unsigned int round;
307
308 /**
309 * Human-readable name for the task, used for debugging.
310 */
311 char *debug_name;
312
313 /**
314 * When we're doing an early finish, how should this step be treated? If
315 * #GNUNET_YES, the step will be marked as finished without actually running
316 * its tasks. Otherwise, the step will still be run even after an early
317 * finish.
318 *
319 * Note that a task may never be finished early if it is already running.
320 */
321 int early_finishable;
322};
323
324
325struct RfnElementInfo
326{
327 const struct GNUNET_SET_Element *element;
328
329 /**
330 * #GNUNET_YES if the peer votes for the proposal.
331 */
332 int *votes;
333
334 /**
335 * Proposal for this element, can only be #VOTE_ADD or #VOTE_REMOVE.
336 */
337 enum ReferendumVote proposal;
338};
339
340
341struct ReferendumEntry
342{
343 struct RfnKey key;
344
345 /*
346 * Elements where there is at least one proposed change.
347 *
348 * Maps the hash of the GNUNET_SET_Element
349 * to 'struct RfnElementInfo'.
350 */
351 struct GNUNET_CONTAINER_MultiHashMap *rfn_elements;
352
353 unsigned int num_peers;
354
355 /**
356 * Stores, for every peer in the session,
357 * whether the peer finished the whole referendum.
358 *
359 * Votes from peers are only counted if they're
360 * marked as committed (#GNUNET_YES) in the referendum.
361 *
362 * Otherwise (#GNUNET_NO), the requested changes are
363 * not counted for majority votes or thresholds.
364 */
365 int *peer_commited;
366
367
368 /**
369 * Contestation state of the peer. If a peer is contested, the values it
370 * contributed are still counted for applying changes, but the grading is
371 * affected.
372 */
373 int *peer_contested;
374};
375
376
377struct DiffElementInfo
378{
379 const struct GNUNET_SET_Element *element;
380
381 /**
382 * Positive weight for 'add', negative
383 * weights for 'remove'.
384 */
385 int weight;
386};
387
388
389/**
390 * Weighted diff.
391 */
392struct DiffEntry
393{
394 struct DiffKey key;
395 struct GNUNET_CONTAINER_MultiHashMap *changes;
396};
397
398struct SetHandle
399{
400 struct SetHandle *prev;
401 struct SetHandle *next;
402
403 struct GNUNET_SET_Handle *h;
404};
405
406
407/**
408 * A consensus session consists of one local client and the remote authorities.
409 */
410struct ConsensusSession
411{
412 /**
413 * Consensus sessions are kept in a DLL.
414 */
415 struct ConsensusSession *next;
416
417 /**
418 * Consensus sessions are kept in a DLL.
419 */
420 struct ConsensusSession *prev;
421
422 unsigned int num_client_insert_pending;
423
424 struct GNUNET_CONTAINER_MultiHashMap *setmap;
425 struct GNUNET_CONTAINER_MultiHashMap *rfnmap;
426 struct GNUNET_CONTAINER_MultiHashMap *diffmap;
427
428 /**
429 * Array of peers with length 'num_peers'.
430 */
431 int *peers_blacklisted;
432
433 /*
434 * Mapping from (hashed) TaskKey to TaskEntry.
435 *
436 * We map the application_id for a round to the task that should be
437 * executed, so we don't have to go through all task whenever we get
438 * an incoming set op request.
439 */
440 struct GNUNET_CONTAINER_MultiHashMap *taskmap;
441
442 struct Step *steps_head;
443 struct Step *steps_tail;
444
445 int conclude_started;
446
447 int conclude_done;
448
449 /**
450 * Global consensus identification, computed
451 * from the session id and participating authorities.
452 */
453 struct GNUNET_HashCode global_id;
454
455 /**
456 * Client that inhabits the session
457 */
458 struct GNUNET_SERVICE_Client *client;
459
460 /**
461 * Queued messages to the client.
462 */
463 struct GNUNET_MQ_Handle *client_mq;
464
465 /**
466 * Time when the conclusion of the consensus should begin.
467 */
468 struct GNUNET_TIME_Absolute conclude_start;
469
470 /**
471 * Timeout for all rounds together, single rounds will schedule a timeout task
472 * with a fraction of the conclude timeout.
473 * Only valid once the current round is not CONSENSUS_ROUND_BEGIN.
474 */
475 struct GNUNET_TIME_Absolute conclude_deadline;
476
477 struct GNUNET_PeerIdentity *peers;
478
479 /**
480 * Number of other peers in the consensus.
481 */
482 unsigned int num_peers;
483
484 /**
485 * Index of the local peer in the peers array
486 */
487 unsigned int local_peer_idx;
488
489 /**
490 * Listener for requests from other peers.
491 * Uses the session's global id as app id.
492 */
493 struct GNUNET_SET_ListenHandle *set_listener;
494
495 /**
496 * State of our early stopping scheme.
497 */
498 int early_stopping;
499
500 /**
501 * Our set size from the first round.
502 */
503 uint64_t first_size;
504
505 uint64_t *first_sizes_received;
506
507 /**
508 * Bounded Eppstein lower bound.
509 */
510 uint64_t lower_bound;
511
512 struct SetHandle *set_handles_head;
513 struct SetHandle *set_handles_tail;
514};
515
516/**
517 * Linked list of sessions this peer participates in.
518 */
519static struct ConsensusSession *sessions_head;
520
521/**
522 * Linked list of sessions this peer participates in.
523 */
524static struct ConsensusSession *sessions_tail;
525
526/**
527 * Configuration of the consensus service.
528 */
529static const struct GNUNET_CONFIGURATION_Handle *cfg;
530
531/**
532 * Peer that runs this service.
533 */
534static struct GNUNET_PeerIdentity my_peer;
535
536/**
537 * Statistics handle.
538 */
539struct GNUNET_STATISTICS_Handle *statistics;
540
541
542static void
543finish_task (struct TaskEntry *task);
544
545
546static void
547run_ready_steps (struct ConsensusSession *session);
548
549
550static const char *
551phasename (uint16_t phase)
552{
553 switch (phase)
554 {
555 case PHASE_KIND_ALL_TO_ALL: return "ALL_TO_ALL";
556
557 case PHASE_KIND_ALL_TO_ALL_2: return "ALL_TO_ALL_2";
558
559 case PHASE_KIND_FINISH: return "FINISH";
560
561 case PHASE_KIND_GRADECAST_LEADER: return "GRADECAST_LEADER";
562
563 case PHASE_KIND_GRADECAST_ECHO: return "GRADECAST_ECHO";
564
565 case PHASE_KIND_GRADECAST_ECHO_GRADE: return "GRADECAST_ECHO_GRADE";
566
567 case PHASE_KIND_GRADECAST_CONFIRM: return "GRADECAST_CONFIRM";
568
569 case PHASE_KIND_GRADECAST_CONFIRM_GRADE: return "GRADECAST_CONFIRM_GRADE";
570
571 case PHASE_KIND_APPLY_REP: return "APPLY_REP";
572
573 default: return "(unknown)";
574 }
575}
576
577
578static const char *
579setname (uint16_t kind)
580{
581 switch (kind)
582 {
583 case SET_KIND_CURRENT: return "CURRENT";
584
585 case SET_KIND_LEADER_PROPOSAL: return "LEADER_PROPOSAL";
586
587 case SET_KIND_NONE: return "NONE";
588
589 default: return "(unknown)";
590 }
591}
592
593
594static const char *
595rfnname (uint16_t kind)
596{
597 switch (kind)
598 {
599 case RFN_KIND_NONE: return "NONE";
600
601 case RFN_KIND_ECHO: return "ECHO";
602
603 case RFN_KIND_CONFIRM: return "CONFIRM";
604
605 default: return "(unknown)";
606 }
607}
608
609
610static const char *
611diffname (uint16_t kind)
612{
613 switch (kind)
614 {
615 case DIFF_KIND_NONE: return "NONE";
616
617 case DIFF_KIND_LEADER_CONSENSUS: return "LEADER_CONSENSUS";
618
619 case DIFF_KIND_GRADECAST_RESULT: return "GRADECAST_RESULT";
620
621 case DIFF_KIND_LEADER_PROPOSAL: return "LEADER_PROPOSAL";
622
623 default: return "(unknown)";
624 }
625}
626
627
628#ifdef GNUNET_EXTRA_LOGGING
629
630
631static const char *
632debug_str_element (const struct GNUNET_SET_Element *el)
633{
634 struct GNUNET_HashCode hash;
635
636 GNUNET_SET_element_hash (el, &hash);
637
638 return GNUNET_h2s (&hash);
639}
640
641
642static const char *
643debug_str_task_key (const struct TaskKey *tk)
644{
645 static char buf[256];
646
647 snprintf (buf, sizeof(buf),
648 "TaskKey kind=%s, p1=%d, p2=%d, l=%d, rep=%d",
649 phasename (tk->kind), tk->peer1, tk->peer2,
650 tk->leader, tk->repetition);
651
652 return buf;
653}
654
655
656static const char *
657debug_str_diff_key (const struct DiffKey *dk)
658{
659 static char buf[256];
660
661 snprintf (buf, sizeof(buf),
662 "DiffKey kind=%s, k1=%d, k2=%d",
663 diffname (dk->diff_kind), dk->k1, dk->k2);
664
665 return buf;
666}
667
668
669static const char *
670debug_str_set_key (const struct SetKey *sk)
671{
672 static char buf[256];
673
674 snprintf (buf, sizeof(buf),
675 "SetKey kind=%s, k1=%d, k2=%d",
676 setname (sk->set_kind),
677 sk->k1,
678 sk->k2);
679 return buf;
680}
681
682
683static const char *
684debug_str_rfn_key (const struct RfnKey *rk)
685{
686 static char buf[256];
687
688 snprintf (buf, sizeof(buf),
689 "RfnKey kind=%s, k1=%d, k2=%d",
690 rfnname (rk->rfn_kind),
691 rk->k1,
692 rk->k2);
693 return buf;
694}
695
696
697#endif /* GNUNET_EXTRA_LOGGING */
698
699
700/**
701 * Send the final result set of the consensus to the client, element by
702 * element.
703 *
704 * @param cls closure
705 * @param element the current element, NULL if all elements have been
706 * iterated over
707 * @return #GNUNET_YES to continue iterating, #GNUNET_NO to stop.
708 */
709static int
710send_to_client_iter (void *cls,
711 const struct GNUNET_SET_Element *element)
712{
713 struct TaskEntry *task = (struct TaskEntry *) cls;
714 struct ConsensusSession *session = task->step->session;
715 struct GNUNET_MQ_Envelope *ev;
716
717 if (NULL != element)
718 {
719 struct GNUNET_CONSENSUS_ElementMessage *m;
720 const struct ConsensusElement *ce;
721
722 GNUNET_assert (GNUNET_BLOCK_TYPE_CONSENSUS_ELEMENT ==
723 element->element_type);
724 ce = element->data;
725
726 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "marker is %u\n",
727 (unsigned) ce->marker);
728
729 if (0 != ce->marker)
730 return GNUNET_YES;
731
732 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
733 "P%d: sending element %s to client\n",
734 session->local_peer_idx,
735 debug_str_element (element));
736
737 ev = GNUNET_MQ_msg_extra (m,
738 element->size - sizeof(struct ConsensusElement),
739 GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_RECEIVED_ELEMENT);
740 m->element_type = ce->payload_type;
741 GNUNET_memcpy (&m[1],
742 &ce[1],
743 element->size - sizeof(struct ConsensusElement));
744 GNUNET_MQ_send (session->client_mq,
745 ev);
746 }
747 else
748 {
749 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
750 "P%d: finished iterating elements for client\n",
751 session->local_peer_idx);
752 ev = GNUNET_MQ_msg_header (
753 GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_CONCLUDE_DONE);
754 GNUNET_MQ_send (session->client_mq,
755 ev);
756 }
757 return GNUNET_YES;
758}
759
760
761static struct SetEntry *
762lookup_set (struct ConsensusSession *session,
763 const struct SetKey *key)
764{
765 struct GNUNET_HashCode hash;
766
767 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
768 "P%u: looking up set {%s}\n",
769 session->local_peer_idx,
770 debug_str_set_key (key));
771
772 GNUNET_assert (SET_KIND_NONE != key->set_kind);
773 GNUNET_CRYPTO_hash (key,
774 sizeof(struct SetKey),
775 &hash);
776 return GNUNET_CONTAINER_multihashmap_get (session->setmap,
777 &hash);
778}
779
780
781static struct DiffEntry *
782lookup_diff (struct ConsensusSession *session,
783 const struct DiffKey *key)
784{
785 struct GNUNET_HashCode hash;
786
787 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
788 "P%u: looking up diff {%s}\n",
789 session->local_peer_idx,
790 debug_str_diff_key (key));
791 GNUNET_assert (DIFF_KIND_NONE != key->diff_kind);
792 GNUNET_CRYPTO_hash (key,
793 sizeof(struct DiffKey),
794 &hash);
795 return GNUNET_CONTAINER_multihashmap_get (session->diffmap,
796 &hash);
797}
798
799
800static struct ReferendumEntry *
801lookup_rfn (struct ConsensusSession *session,
802 const struct RfnKey *key)
803{
804 struct GNUNET_HashCode hash;
805
806 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
807 "P%u: looking up rfn {%s}\n",
808 session->local_peer_idx,
809 debug_str_rfn_key (key));
810 GNUNET_assert (RFN_KIND_NONE != key->rfn_kind);
811 GNUNET_CRYPTO_hash (key,
812 sizeof(struct RfnKey),
813 &hash);
814 return GNUNET_CONTAINER_multihashmap_get (session->rfnmap,
815 &hash);
816}
817
818
819static void
820diff_insert (struct DiffEntry *diff,
821 int weight,
822 const struct GNUNET_SET_Element *element)
823{
824 struct DiffElementInfo *di;
825 struct GNUNET_HashCode hash;
826
827 GNUNET_assert ((1 == weight) || (-1 == weight));
828
829 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
830 "diff_insert with element size %u\n",
831 element->size);
832
833 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
834 "hashing element\n");
835
836 GNUNET_SET_element_hash (element, &hash);
837
838 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
839 "hashed element\n");
840
841 di = GNUNET_CONTAINER_multihashmap_get (diff->changes, &hash);
842
843 if (NULL == di)
844 {
845 di = GNUNET_new (struct DiffElementInfo);
846 di->element = GNUNET_SET_element_dup (element);
847 GNUNET_assert (GNUNET_OK ==
848 GNUNET_CONTAINER_multihashmap_put (diff->changes,
849 &hash,
850 di,
851 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
852 }
853
854 di->weight = weight;
855}
856
857
858static void
859rfn_commit (struct ReferendumEntry *rfn,
860 uint16_t commit_peer)
861{
862 GNUNET_assert (commit_peer < rfn->num_peers);
863
864 rfn->peer_commited[commit_peer] = GNUNET_YES;
865}
866
867
868static void
869rfn_contest (struct ReferendumEntry *rfn,
870 uint16_t contested_peer)
871{
872 GNUNET_assert (contested_peer < rfn->num_peers);
873
874 rfn->peer_contested[contested_peer] = GNUNET_YES;
875}
876
877
878static uint16_t
879rfn_noncontested (struct ReferendumEntry *rfn)
880{
881 uint16_t ret;
882
883 ret = 0;
884 for (uint16_t i = 0; i < rfn->num_peers; i++)
885 if ((GNUNET_YES == rfn->peer_commited[i]) && (GNUNET_NO ==
886 rfn->peer_contested[i]))
887 ret++;
888
889 return ret;
890}
891
892
893static void
894rfn_vote (struct ReferendumEntry *rfn,
895 uint16_t voting_peer,
896 enum ReferendumVote vote,
897 const struct GNUNET_SET_Element *element)
898{
899 struct RfnElementInfo *ri;
900 struct GNUNET_HashCode hash;
901
902 GNUNET_assert (voting_peer < rfn->num_peers);
903
904 /* Explicit voting only makes sense with VOTE_ADD or VOTE_REMOTE,
905 since VOTE_KEEP is implicit in not voting. */
906 GNUNET_assert ((VOTE_ADD == vote) || (VOTE_REMOVE == vote));
907
908 GNUNET_SET_element_hash (element, &hash);
909 ri = GNUNET_CONTAINER_multihashmap_get (rfn->rfn_elements, &hash);
910
911 if (NULL == ri)
912 {
913 ri = GNUNET_new (struct RfnElementInfo);
914 ri->element = GNUNET_SET_element_dup (element);
915 ri->votes = GNUNET_new_array (rfn->num_peers, int);
916 GNUNET_assert (GNUNET_OK ==
917 GNUNET_CONTAINER_multihashmap_put (rfn->rfn_elements,
918 &hash, ri,
919 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
920 }
921
922 ri->votes[voting_peer] = GNUNET_YES;
923 ri->proposal = vote;
924}
925
926
927static uint16_t
928task_other_peer (struct TaskEntry *task)
929{
930 uint16_t me = task->step->session->local_peer_idx;
931
932 if (task->key.peer1 == me)
933 return task->key.peer2;
934 return task->key.peer1;
935}
936
937
938static int
939cmp_uint64_t (const void *pa, const void *pb)
940{
941 uint64_t a = *(uint64_t *) pa;
942 uint64_t b = *(uint64_t *) pb;
943
944 if (a == b)
945 return 0;
946 if (a < b)
947 return -1;
948 return 1;
949}
950
951
952/**
953 * Callback for set operation results. Called for each element
954 * in the result set.
955 *
956 * @param cls closure
957 * @param element a result element, only valid if status is #GNUNET_SET_STATUS_OK
958 * @param current_size current set size
959 * @param status see enum GNUNET_SET_Status
960 */
961static void
962set_result_cb (void *cls,
963 const struct GNUNET_SET_Element *element,
964 uint64_t current_size,
965 enum GNUNET_SET_Status status)
966{
967 struct TaskEntry *task = cls;
968 struct ConsensusSession *session = task->step->session;
969 struct SetEntry *output_set = NULL;
970 struct DiffEntry *output_diff = NULL;
971 struct ReferendumEntry *output_rfn = NULL;
972 unsigned int other_idx;
973 struct SetOpCls *setop;
974 const struct ConsensusElement *consensus_element = NULL;
975
976 if (NULL != element)
977 {
978 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
979 "P%u: got element of type %u, status %u\n",
980 session->local_peer_idx,
981 (unsigned) element->element_type,
982 (unsigned) status);
983 GNUNET_assert (GNUNET_BLOCK_TYPE_CONSENSUS_ELEMENT ==
984 element->element_type);
985 consensus_element = element->data;
986 }
987
988 setop = &task->cls.setop;
989
990
991 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
992 "P%u: got set result for {%s}, status %u\n",
993 session->local_peer_idx,
994 debug_str_task_key (&task->key),
995 status);
996
997 if (GNUNET_NO == task->is_started)
998 {
999 GNUNET_break_op (0);
1000 return;
1001 }
1002
1003 if (GNUNET_YES == task->is_finished)
1004 {
1005 GNUNET_break_op (0);
1006 return;
1007 }
1008
1009 other_idx = task_other_peer (task);
1010
1011 if (SET_KIND_NONE != setop->output_set.set_kind)
1012 {
1013 output_set = lookup_set (session,
1014 &setop->output_set);
1015 GNUNET_assert (NULL != output_set);
1016 }
1017
1018 if (DIFF_KIND_NONE != setop->output_diff.diff_kind)
1019 {
1020 output_diff = lookup_diff (session, &setop->output_diff);
1021 GNUNET_assert (NULL != output_diff);
1022 }
1023
1024 if (RFN_KIND_NONE != setop->output_rfn.rfn_kind)
1025 {
1026 output_rfn = lookup_rfn (session, &setop->output_rfn);
1027 GNUNET_assert (NULL != output_rfn);
1028 }
1029
1030 if (GNUNET_YES == session->peers_blacklisted[other_idx])
1031 {
1032 /* Peer might have been blacklisted
1033 by a gradecast running in parallel, ignore elements from now */
1034 if (GNUNET_SET_STATUS_ADD_LOCAL == status)
1035 return;
1036 if (GNUNET_SET_STATUS_ADD_REMOTE == status)
1037 return;
1038 }
1039
1040 if ((NULL != consensus_element) && (0 != consensus_element->marker))
1041 {
1042 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1043 "P%u: got some marker\n",
1044 session->local_peer_idx);
1045 if ((GNUNET_YES == setop->transceive_contested) &&
1046 (CONSENSUS_MARKER_CONTESTED == consensus_element->marker))
1047 {
1048 GNUNET_assert (NULL != output_rfn);
1049 rfn_contest (output_rfn, task_other_peer (task));
1050 return;
1051 }
1052
1053 if (CONSENSUS_MARKER_SIZE == consensus_element->marker)
1054 {
1055 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1056 "P%u: got size marker\n",
1057 session->local_peer_idx);
1058
1059
1060 struct ConsensusSizeElement *cse = (void *) consensus_element;
1061
1062 if (cse->sender_index == other_idx)
1063 {
1064 if (NULL == session->first_sizes_received)
1065 session->first_sizes_received = GNUNET_new_array (session->num_peers,
1066 uint64_t);
1067 session->first_sizes_received[other_idx] = GNUNET_ntohll (cse->size);
1068
1069 uint64_t *copy = GNUNET_memdup (session->first_sizes_received,
1070 sizeof(uint64_t) * session->num_peers);
1071 qsort (copy, session->num_peers, sizeof(uint64_t), cmp_uint64_t);
1072 session->lower_bound = copy[session->num_peers / 3 + 1];
1073 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1074 "P%u: lower bound %llu\n",
1075 session->local_peer_idx,
1076 (long long) session->lower_bound);
1077 GNUNET_free (copy);
1078 }
1079 return;
1080 }
1081
1082 return;
1083 }
1084
1085 switch (status)
1086 {
1087 case GNUNET_SET_STATUS_ADD_LOCAL:
1088 GNUNET_assert (NULL != consensus_element);
1089 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1090 "Adding element in Task {%s}\n",
1091 debug_str_task_key (&task->key));
1092 if (NULL != output_set)
1093 {
1094 // FIXME: record pending adds, use callback
1095 GNUNET_SET_add_element (output_set->h,
1096 element,
1097 NULL,
1098 NULL);
1099#ifdef GNUNET_EXTRA_LOGGING
1100 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1101 "P%u: adding element %s into set {%s} of task {%s}\n",
1102 session->local_peer_idx,
1103 debug_str_element (element),
1104 debug_str_set_key (&setop->output_set),
1105 debug_str_task_key (&task->key));
1106#endif
1107 }
1108 if (NULL != output_diff)
1109 {
1110 diff_insert (output_diff, 1, element);
1111#ifdef GNUNET_EXTRA_LOGGING
1112 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1113 "P%u: adding element %s into diff {%s} of task {%s}\n",
1114 session->local_peer_idx,
1115 debug_str_element (element),
1116 debug_str_diff_key (&setop->output_diff),
1117 debug_str_task_key (&task->key));
1118#endif
1119 }
1120 if (NULL != output_rfn)
1121 {
1122 rfn_vote (output_rfn, task_other_peer (task), VOTE_ADD, element);
1123#ifdef GNUNET_EXTRA_LOGGING
1124 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1125 "P%u: adding element %s into rfn {%s} of task {%s}\n",
1126 session->local_peer_idx,
1127 debug_str_element (element),
1128 debug_str_rfn_key (&setop->output_rfn),
1129 debug_str_task_key (&task->key));
1130#endif
1131 }
1132 // XXX: add result to structures in task
1133 break;
1134
1135 case GNUNET_SET_STATUS_ADD_REMOTE:
1136 GNUNET_assert (NULL != consensus_element);
1137 if (GNUNET_YES == setop->do_not_remove)
1138 break;
1139 if (CONSENSUS_MARKER_CONTESTED == consensus_element->marker)
1140 break;
1141 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1142 "Removing element in Task {%s}\n",
1143 debug_str_task_key (&task->key));
1144 if (NULL != output_set)
1145 {
1146 // FIXME: record pending adds, use callback
1147 GNUNET_SET_remove_element (output_set->h,
1148 element,
1149 NULL,
1150 NULL);
1151#ifdef GNUNET_EXTRA_LOGGING
1152 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1153 "P%u: removing element %s from set {%s} of task {%s}\n",
1154 session->local_peer_idx,
1155 debug_str_element (element),
1156 debug_str_set_key (&setop->output_set),
1157 debug_str_task_key (&task->key));
1158#endif
1159 }
1160 if (NULL != output_diff)
1161 {
1162 diff_insert (output_diff, -1, element);
1163#ifdef GNUNET_EXTRA_LOGGING
1164 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1165 "P%u: removing element %s from diff {%s} of task {%s}\n",
1166 session->local_peer_idx,
1167 debug_str_element (element),
1168 debug_str_diff_key (&setop->output_diff),
1169 debug_str_task_key (&task->key));
1170#endif
1171 }
1172 if (NULL != output_rfn)
1173 {
1174 rfn_vote (output_rfn, task_other_peer (task), VOTE_REMOVE, element);
1175#ifdef GNUNET_EXTRA_LOGGING
1176 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1177 "P%u: removing element %s from rfn {%s} of task {%s}\n",
1178 session->local_peer_idx,
1179 debug_str_element (element),
1180 debug_str_rfn_key (&setop->output_rfn),
1181 debug_str_task_key (&task->key));
1182#endif
1183 }
1184 break;
1185
1186 case GNUNET_SET_STATUS_DONE:
1187 // XXX: check first if any changes to the underlying
1188 // set are still pending
1189 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1190 "P%u: Finishing setop in Task {%s} (%u/%u)\n",
1191 session->local_peer_idx,
1192 debug_str_task_key (&task->key),
1193 (unsigned int) task->step->finished_tasks,
1194 (unsigned int) task->step->tasks_len);
1195 if (NULL != output_rfn)
1196 {
1197 rfn_commit (output_rfn, task_other_peer (task));
1198 }
1199 if (PHASE_KIND_ALL_TO_ALL == task->key.kind)
1200 {
1201 session->first_size = current_size;
1202 }
1203 finish_task (task);
1204 break;
1205
1206 case GNUNET_SET_STATUS_FAILURE:
1207 // XXX: cleanup
1208 GNUNET_break_op (0);
1209 finish_task (task);
1210 return;
1211
1212 default:
1213 /* not reached */
1214 GNUNET_assert (0);
1215 }
1216}
1217
1218
1219#ifdef EVIL
1220
1221enum EvilnessType
1222{
1223 EVILNESS_NONE,
1224 EVILNESS_CRAM_ALL,
1225 EVILNESS_CRAM_LEAD,
1226 EVILNESS_CRAM_ECHO,
1227 EVILNESS_SLACK,
1228 EVILNESS_SLACK_A2A,
1229};
1230
1231enum EvilnessSubType
1232{
1233 EVILNESS_SUB_NONE,
1234 EVILNESS_SUB_REPLACEMENT,
1235 EVILNESS_SUB_NO_REPLACEMENT,
1236};
1237
1238struct Evilness
1239{
1240 enum EvilnessType type;
1241 enum EvilnessSubType subtype;
1242 unsigned int num;
1243};
1244
1245
1246static int
1247parse_evilness_cram_subtype (const char *evil_subtype_str,
1248 struct Evilness *evil)
1249{
1250 if (0 == strcmp ("replace", evil_subtype_str))
1251 {
1252 evil->subtype = EVILNESS_SUB_REPLACEMENT;
1253 }
1254 else if (0 == strcmp ("noreplace", evil_subtype_str))
1255 {
1256 evil->subtype = EVILNESS_SUB_NO_REPLACEMENT;
1257 }
1258 else
1259 {
1260 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1261 "Malformed field '%s' in EVIL_SPEC (unknown subtype), behaving like a good peer.\n",
1262 evil_subtype_str);
1263 return GNUNET_SYSERR;
1264 }
1265 return GNUNET_OK;
1266}
1267
1268
1269static void
1270get_evilness (struct ConsensusSession *session, struct Evilness *evil)
1271{
1272 char *evil_spec;
1273 char *field;
1274 char *evil_type_str = NULL;
1275 char *evil_subtype_str = NULL;
1276
1277 GNUNET_assert (NULL != evil);
1278
1279 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "consensus",
1280 "EVIL_SPEC",
1281 &evil_spec))
1282 {
1283 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1284 "P%u: no evilness\n",
1285 session->local_peer_idx);
1286 evil->type = EVILNESS_NONE;
1287 return;
1288 }
1289 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1290 "P%u: got evilness spec\n",
1291 session->local_peer_idx);
1292
1293 for (field = strtok (evil_spec, "/");
1294 NULL != field;
1295 field = strtok (NULL, "/"))
1296 {
1297 unsigned int peer_num;
1298 unsigned int evil_num;
1299 int ret;
1300
1301 evil_type_str = NULL;
1302 evil_subtype_str = NULL;
1303
1304 ret = sscanf (field, "%u;%m[a-z-];%m[a-z-];%u", &peer_num, &evil_type_str,
1305 &evil_subtype_str, &evil_num);
1306
1307 if (ret != 4)
1308 {
1309 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1310 "Malformed field '%s' in EVIL_SPEC (expected 4 components got %d), behaving like a good peer.\n",
1311 field,
1312 ret);
1313 goto not_evil;
1314 }
1315
1316 GNUNET_assert (NULL != evil_type_str);
1317 GNUNET_assert (NULL != evil_subtype_str);
1318
1319 if (peer_num == session->local_peer_idx)
1320 {
1321 if (0 == strcmp ("slack", evil_type_str))
1322 {
1323 evil->type = EVILNESS_SLACK;
1324 }
1325 if (0 == strcmp ("slack-a2a", evil_type_str))
1326 {
1327 evil->type = EVILNESS_SLACK_A2A;
1328 }
1329 else if (0 == strcmp ("cram-all", evil_type_str))
1330 {
1331 evil->type = EVILNESS_CRAM_ALL;
1332 evil->num = evil_num;
1333 if (GNUNET_OK != parse_evilness_cram_subtype (evil_subtype_str, evil))
1334 goto not_evil;
1335 }
1336 else if (0 == strcmp ("cram-lead", evil_type_str))
1337 {
1338 evil->type = EVILNESS_CRAM_LEAD;
1339 evil->num = evil_num;
1340 if (GNUNET_OK != parse_evilness_cram_subtype (evil_subtype_str, evil))
1341 goto not_evil;
1342 }
1343 else if (0 == strcmp ("cram-echo", evil_type_str))
1344 {
1345 evil->type = EVILNESS_CRAM_ECHO;
1346 evil->num = evil_num;
1347 if (GNUNET_OK != parse_evilness_cram_subtype (evil_subtype_str, evil))
1348 goto not_evil;
1349 }
1350 else
1351 {
1352 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1353 "Malformed field '%s' in EVIL_SPEC (unknown type), behaving like a good peer.\n",
1354 evil_type_str);
1355 goto not_evil;
1356 }
1357 goto cleanup;
1358 }
1359 /* No GNUNET_free since memory was allocated by libc */
1360 free (evil_type_str);
1361 evil_type_str = NULL;
1362 evil_subtype_str = NULL;
1363 }
1364not_evil:
1365 evil->type = EVILNESS_NONE;
1366cleanup:
1367 GNUNET_free (evil_spec);
1368 /* no GNUNET_free since it wasn't
1369 * allocated with GNUNET_malloc */
1370 if (NULL != evil_type_str)
1371 free (evil_type_str);
1372 if (NULL != evil_subtype_str)
1373 free (evil_subtype_str);
1374}
1375
1376
1377#endif
1378
1379
1380/**
1381 * Commit the appropriate set for a task.
1382 */
1383static void
1384commit_set (struct ConsensusSession *session,
1385 struct TaskEntry *task)
1386{
1387 struct SetEntry *set;
1388 struct SetOpCls *setop = &task->cls.setop;
1389
1390 GNUNET_assert (NULL != setop->op);
1391 set = lookup_set (session, &setop->input_set);
1392 GNUNET_assert (NULL != set);
1393
1394 if ((GNUNET_YES == setop->transceive_contested) && (GNUNET_YES ==
1395 set->is_contested))
1396 {
1397 struct GNUNET_SET_Element element;
1398 struct ConsensusElement ce = { 0 };
1399
1400 ce.marker = CONSENSUS_MARKER_CONTESTED;
1401 element.data = &ce;
1402 element.size = sizeof(struct ConsensusElement);
1403 element.element_type = GNUNET_BLOCK_TYPE_CONSENSUS_ELEMENT;
1404 GNUNET_SET_add_element (set->h, &element, NULL, NULL);
1405 }
1406
1407 if (PHASE_KIND_ALL_TO_ALL_2 == task->key.kind)
1408 {
1409 struct GNUNET_SET_Element element;
1410 struct ConsensusSizeElement cse = {
1411 .size = 0,
1412 .sender_index = 0
1413 };
1414 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "inserting size marker\n");
1415 cse.ce.marker = CONSENSUS_MARKER_SIZE;
1416 cse.size = GNUNET_htonll (session->first_size);
1417 cse.sender_index = session->local_peer_idx;
1418 element.data = &cse;
1419 element.size = sizeof(struct ConsensusSizeElement);
1420 element.element_type = GNUNET_BLOCK_TYPE_CONSENSUS_ELEMENT;
1421 GNUNET_SET_add_element (set->h, &element, NULL, NULL);
1422 }
1423
1424#ifdef EVIL
1425 {
1426 struct Evilness evil;
1427
1428 get_evilness (session, &evil);
1429 if (EVILNESS_NONE != evil.type)
1430 {
1431 /* Useful for evaluation */
1432 GNUNET_STATISTICS_set (statistics,
1433 "is evil",
1434 1,
1435 GNUNET_NO);
1436 }
1437 switch (evil.type)
1438 {
1439 case EVILNESS_CRAM_ALL:
1440 case EVILNESS_CRAM_LEAD:
1441 case EVILNESS_CRAM_ECHO:
1442 /* We're not cramming elements in the
1443 all-to-all round, since that would just
1444 add more elements to the result set, but
1445 wouldn't test robustness. */
1446 if (PHASE_KIND_ALL_TO_ALL == task->key.kind)
1447 {
1448 GNUNET_SET_commit (setop->op, set->h);
1449 break;
1450 }
1451 if ((EVILNESS_CRAM_LEAD == evil.type) &&
1452 ((PHASE_KIND_GRADECAST_LEADER != task->key.kind) ||
1453 (SET_KIND_CURRENT != set->key.set_kind) ))
1454 {
1455 GNUNET_SET_commit (setop->op, set->h);
1456 break;
1457 }
1458 if ((EVILNESS_CRAM_ECHO == evil.type) && (PHASE_KIND_GRADECAST_ECHO !=
1459 task->key.kind))
1460 {
1461 GNUNET_SET_commit (setop->op, set->h);
1462 break;
1463 }
1464 for (unsigned int i = 0; i < evil.num; i++)
1465 {
1466 struct GNUNET_SET_Element element;
1467 struct ConsensusStuffedElement se = {
1468 .ce.payload_type = 0,
1469 .ce.marker = 0,
1470 };
1471 element.data = &se;
1472 element.size = sizeof(struct ConsensusStuffedElement);
1473 element.element_type = GNUNET_BLOCK_TYPE_CONSENSUS_ELEMENT;
1474
1475 if (EVILNESS_SUB_REPLACEMENT == evil.subtype)
1476 {
1477 /* Always generate a new element. */
1478 GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK,
1479 &se.rand);
1480 }
1481 else if (EVILNESS_SUB_NO_REPLACEMENT == evil.subtype)
1482 {
1483 /* Always cram the same elements, derived from counter. */
1484 GNUNET_CRYPTO_hash (&i, sizeof(i), &se.rand);
1485 }
1486 else
1487 {
1488 GNUNET_assert (0);
1489 }
1490 GNUNET_SET_add_element (set->h, &element, NULL, NULL);
1491#ifdef GNUNET_EXTRA_LOGGING
1492 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1493 "P%u: evil peer: cramming element %s into set {%s} of task {%s}\n",
1494 session->local_peer_idx,
1495 debug_str_element (&element),
1496 debug_str_set_key (&setop->input_set),
1497 debug_str_task_key (&task->key));
1498#endif
1499 }
1500 GNUNET_STATISTICS_update (statistics,
1501 "# stuffed elements",
1502 evil.num,
1503 GNUNET_NO);
1504 GNUNET_SET_commit (setop->op, set->h);
1505 break;
1506
1507 case EVILNESS_SLACK:
1508 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1509 "P%u: evil peer: slacking\n",
1510 (unsigned int) session->local_peer_idx);
1511
1512 /* Do nothing. */
1513 case EVILNESS_SLACK_A2A:
1514 if ((PHASE_KIND_ALL_TO_ALL_2 == task->key.kind) ||
1515 (PHASE_KIND_ALL_TO_ALL == task->key.kind))
1516 {
1517 struct GNUNET_SET_Handle *empty_set;
1518 empty_set = GNUNET_SET_create (cfg, GNUNET_SET_OPERATION_UNION);
1519 GNUNET_SET_commit (setop->op, empty_set);
1520 GNUNET_SET_destroy (empty_set);
1521 }
1522 else
1523 {
1524 GNUNET_SET_commit (setop->op,
1525 set->h);
1526 }
1527 break;
1528
1529 case EVILNESS_NONE:
1530 GNUNET_SET_commit (setop->op,
1531 set->h);
1532 break;
1533 }
1534 }
1535#else
1536 if (GNUNET_NO == session->peers_blacklisted[task_other_peer (task)])
1537 {
1538 GNUNET_SET_commit (setop->op, set->h);
1539 }
1540 else
1541 {
1542 /* For our testcases, we don't want the blacklisted
1543 peers to wait. */
1544 GNUNET_SET_operation_cancel (setop->op);
1545 setop->op = NULL;
1546 finish_task (task);
1547 }
1548#endif
1549}
1550
1551
1552static void
1553put_diff (struct ConsensusSession *session,
1554 struct DiffEntry *diff)
1555{
1556 struct GNUNET_HashCode hash;
1557
1558 GNUNET_CRYPTO_hash (&diff->key,
1559 sizeof(struct DiffKey),
1560 &hash);
1561 GNUNET_assert (GNUNET_OK ==
1562 GNUNET_CONTAINER_multihashmap_put (session->diffmap,
1563 &hash,
1564 diff,
1565 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1566}
1567
1568
1569static void
1570put_set (struct ConsensusSession *session,
1571 struct SetEntry *set)
1572{
1573 struct GNUNET_HashCode hash;
1574
1575 GNUNET_assert (NULL != set->h);
1576 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1577 "Putting set %s\n",
1578 debug_str_set_key (&set->key));
1579 GNUNET_CRYPTO_hash (&set->key,
1580 sizeof(struct SetKey),
1581 &hash);
1582 GNUNET_assert (GNUNET_SYSERR !=
1583 GNUNET_CONTAINER_multihashmap_put (session->setmap,
1584 &hash,
1585 set,
1586 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE));
1587}
1588
1589
1590static void
1591put_rfn (struct ConsensusSession *session,
1592 struct ReferendumEntry *rfn)
1593{
1594 struct GNUNET_HashCode hash;
1595
1596 GNUNET_CRYPTO_hash (&rfn->key, sizeof(struct RfnKey), &hash);
1597 GNUNET_assert (GNUNET_OK ==
1598 GNUNET_CONTAINER_multihashmap_put (session->rfnmap,
1599 &hash,
1600 rfn,
1601 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1602}
1603
1604
1605static void
1606task_cancel_reconcile (struct TaskEntry *task)
1607{
1608 /* not implemented yet */
1609 GNUNET_assert (0);
1610}
1611
1612
1613static void
1614apply_diff_to_rfn (struct DiffEntry *diff,
1615 struct ReferendumEntry *rfn,
1616 uint16_t voting_peer,
1617 uint16_t num_peers)
1618{
1619 struct GNUNET_CONTAINER_MultiHashMapIterator *iter;
1620 struct DiffElementInfo *di;
1621
1622 iter = GNUNET_CONTAINER_multihashmap_iterator_create (diff->changes);
1623
1624 while (GNUNET_YES ==
1625 GNUNET_CONTAINER_multihashmap_iterator_next (iter,
1626 NULL,
1627 (const void **) &di))
1628 {
1629 if (di->weight > 0)
1630 {
1631 rfn_vote (rfn, voting_peer, VOTE_ADD, di->element);
1632 }
1633 if (di->weight < 0)
1634 {
1635 rfn_vote (rfn, voting_peer, VOTE_REMOVE, di->element);
1636 }
1637 }
1638
1639 GNUNET_CONTAINER_multihashmap_iterator_destroy (iter);
1640}
1641
1642
1643static struct DiffEntry *
1644diff_create (void)
1645{
1646 struct DiffEntry *d = GNUNET_new (struct DiffEntry);
1647
1648 d->changes = GNUNET_CONTAINER_multihashmap_create (8,
1649 GNUNET_NO);
1650
1651 return d;
1652}
1653
1654
1655#if 0
1656static struct DiffEntry *
1657diff_compose (struct DiffEntry *diff_1,
1658 struct DiffEntry *diff_2)
1659{
1660 struct DiffEntry *diff_new;
1661 struct GNUNET_CONTAINER_MultiHashMapIterator *iter;
1662 struct DiffElementInfo *di;
1663
1664 diff_new = diff_create ();
1665
1666 iter = GNUNET_CONTAINER_multihashmap_iterator_create (diff_1->changes);
1667 while (GNUNET_YES ==
1668 GNUNET_CONTAINER_multihashmap_iterator_next (iter,
1669 NULL,
1670 (const void **) &di))
1671 {
1672 diff_insert (diff_new,
1673 di->weight,
1674 di->element);
1675 }
1676 GNUNET_CONTAINER_multihashmap_iterator_destroy (iter);
1677
1678 iter = GNUNET_CONTAINER_multihashmap_iterator_create (diff_2->changes);
1679 while (GNUNET_YES ==
1680 GNUNET_CONTAINER_multihashmap_iterator_next (iter,
1681 NULL,
1682 (const void **) &di))
1683 {
1684 diff_insert (diff_new,
1685 di->weight,
1686 di->element);
1687 }
1688 GNUNET_CONTAINER_multihashmap_iterator_destroy (iter);
1689
1690 return diff_new;
1691}
1692
1693
1694#endif
1695
1696
1697struct ReferendumEntry *
1698rfn_create (uint16_t size)
1699{
1700 struct ReferendumEntry *rfn;
1701
1702 rfn = GNUNET_new (struct ReferendumEntry);
1703 rfn->rfn_elements = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
1704 rfn->peer_commited = GNUNET_new_array (size, int);
1705 rfn->peer_contested = GNUNET_new_array (size, int);
1706 rfn->num_peers = size;
1707
1708 return rfn;
1709}
1710
1711
1712#if UNUSED
1713static void
1714diff_destroy (struct DiffEntry *diff)
1715{
1716 GNUNET_CONTAINER_multihashmap_destroy (diff->changes);
1717 GNUNET_free (diff);
1718}
1719
1720
1721#endif
1722
1723
1724/**
1725 * For a given majority, count what the outcome
1726 * is (add/remove/keep), and give the number
1727 * of peers that voted for this outcome.
1728 */
1729static void
1730rfn_majority (const struct ReferendumEntry *rfn,
1731 const struct RfnElementInfo *ri,
1732 uint16_t *ret_majority,
1733 enum ReferendumVote *ret_vote)
1734{
1735 uint16_t votes_yes = 0;
1736 uint16_t num_commited = 0;
1737
1738 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1739 "Computing rfn majority for element %s of rfn {%s}\n",
1740 debug_str_element (ri->element),
1741 debug_str_rfn_key (&rfn->key));
1742
1743 for (uint16_t i = 0; i < rfn->num_peers; i++)
1744 {
1745 if (GNUNET_NO == rfn->peer_commited[i])
1746 continue;
1747 num_commited++;
1748
1749 if (GNUNET_YES == ri->votes[i])
1750 votes_yes++;
1751 }
1752
1753 if (votes_yes > (num_commited) / 2)
1754 {
1755 *ret_vote = ri->proposal;
1756 *ret_majority = votes_yes;
1757 }
1758 else
1759 {
1760 *ret_vote = VOTE_STAY;
1761 *ret_majority = num_commited - votes_yes;
1762 }
1763}
1764
1765
1766struct SetCopyCls
1767{
1768 struct TaskEntry *task;
1769 struct SetKey dst_set_key;
1770};
1771
1772
1773static void
1774set_copy_cb (void *cls,
1775 struct GNUNET_SET_Handle *copy)
1776{
1777 struct SetCopyCls *scc = cls;
1778 struct TaskEntry *task = scc->task;
1779 struct SetKey dst_set_key = scc->dst_set_key;
1780 struct SetEntry *set;
1781 struct SetHandle *sh = GNUNET_new (struct SetHandle);
1782
1783 sh->h = copy;
1784 GNUNET_CONTAINER_DLL_insert (task->step->session->set_handles_head,
1785 task->step->session->set_handles_tail,
1786 sh);
1787
1788 GNUNET_free (scc);
1789 set = GNUNET_new (struct SetEntry);
1790 set->h = copy;
1791 set->key = dst_set_key;
1792 put_set (task->step->session, set);
1793
1794 task->start (task);
1795}
1796
1797
1798/**
1799 * Call the start function of the given
1800 * task again after we created a copy of the given set.
1801 */
1802static void
1803create_set_copy_for_task (struct TaskEntry *task,
1804 struct SetKey *src_set_key,
1805 struct SetKey *dst_set_key)
1806{
1807 struct SetEntry *src_set;
1808 struct SetCopyCls *scc = GNUNET_new (struct SetCopyCls);
1809
1810 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1811 "Copying set {%s} to {%s} for task {%s}\n",
1812 debug_str_set_key (src_set_key),
1813 debug_str_set_key (dst_set_key),
1814 debug_str_task_key (&task->key));
1815
1816 scc->task = task;
1817 scc->dst_set_key = *dst_set_key;
1818 src_set = lookup_set (task->step->session, src_set_key);
1819 GNUNET_assert (NULL != src_set);
1820 GNUNET_SET_copy_lazy (src_set->h,
1821 set_copy_cb,
1822 scc);
1823}
1824
1825
1826struct SetMutationProgressCls
1827{
1828 int num_pending;
1829 /**
1830 * Task to finish once all changes are through.
1831 */
1832 struct TaskEntry *task;
1833};
1834
1835
1836static void
1837set_mutation_done (void *cls)
1838{
1839 struct SetMutationProgressCls *pc = cls;
1840
1841 GNUNET_assert (pc->num_pending > 0);
1842
1843 pc->num_pending--;
1844
1845 if (0 == pc->num_pending)
1846 {
1847 struct TaskEntry *task = pc->task;
1848 GNUNET_free (pc);
1849 finish_task (task);
1850 }
1851}
1852
1853
1854static void
1855try_finish_step_early (struct Step *step)
1856{
1857 if (GNUNET_YES == step->is_running)
1858 return;
1859 if (GNUNET_YES == step->is_finished)
1860 return;
1861 if (GNUNET_NO == step->early_finishable)
1862 return;
1863
1864 step->is_finished = GNUNET_YES;
1865
1866#ifdef GNUNET_EXTRA_LOGGING
1867 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1868 "Finishing step `%s' early.\n",
1869 step->debug_name);
1870#endif
1871
1872 for (unsigned int i = 0; i < step->subordinates_len; i++)
1873 {
1874 GNUNET_assert (step->subordinates[i]->pending_prereq > 0);
1875 step->subordinates[i]->pending_prereq--;
1876#ifdef GNUNET_EXTRA_LOGGING
1877 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1878 "Decreased pending_prereq to %u for step `%s'.\n",
1879 (unsigned int) step->subordinates[i]->pending_prereq,
1880 step->subordinates[i]->debug_name);
1881#endif
1882 try_finish_step_early (step->subordinates[i]);
1883 }
1884
1885 // XXX: maybe schedule as task to avoid recursion?
1886 run_ready_steps (step->session);
1887}
1888
1889
1890static void
1891finish_step (struct Step *step)
1892{
1893 GNUNET_assert (step->finished_tasks == step->tasks_len);
1894 GNUNET_assert (GNUNET_YES == step->is_running);
1895 GNUNET_assert (GNUNET_NO == step->is_finished);
1896
1897#ifdef GNUNET_EXTRA_LOGGING
1898 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1899 "All tasks of step `%s' with %u subordinates finished.\n",
1900 step->debug_name,
1901 step->subordinates_len);
1902#endif
1903
1904 for (unsigned int i = 0; i < step->subordinates_len; i++)
1905 {
1906 GNUNET_assert (step->subordinates[i]->pending_prereq > 0);
1907 step->subordinates[i]->pending_prereq--;
1908#ifdef GNUNET_EXTRA_LOGGING
1909 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1910 "Decreased pending_prereq to %u for step `%s'.\n",
1911 (unsigned int) step->subordinates[i]->pending_prereq,
1912 step->subordinates[i]->debug_name);
1913#endif
1914 }
1915
1916 step->is_finished = GNUNET_YES;
1917
1918 // XXX: maybe schedule as task to avoid recursion?
1919 run_ready_steps (step->session);
1920}
1921
1922
1923/**
1924 * Apply the result from one round of gradecasts (i.e. every peer
1925 * should have gradecasted) to the peer's current set.
1926 *
1927 * @param task the task with context information
1928 */
1929static void
1930task_start_apply_round (struct TaskEntry *task)
1931{
1932 struct ConsensusSession *session = task->step->session;
1933 struct SetKey sk_in;
1934 struct SetKey sk_out;
1935 struct RfnKey rk_in;
1936 struct SetEntry *set_out;
1937 struct ReferendumEntry *rfn_in;
1938 struct GNUNET_CONTAINER_MultiHashMapIterator *iter;
1939 struct RfnElementInfo *ri;
1940 struct SetMutationProgressCls *progress_cls;
1941 uint16_t worst_majority = UINT16_MAX;
1942
1943 sk_in = (struct SetKey) { SET_KIND_CURRENT, task->key.repetition };
1944 rk_in = (struct RfnKey) { RFN_KIND_GRADECAST_RESULT, task->key.repetition };
1945 sk_out = (struct SetKey) { SET_KIND_CURRENT, task->key.repetition + 1 };
1946
1947 set_out = lookup_set (session, &sk_out);
1948 if (NULL == set_out)
1949 {
1950 create_set_copy_for_task (task,
1951 &sk_in,
1952 &sk_out);
1953 return;
1954 }
1955
1956 rfn_in = lookup_rfn (session, &rk_in);
1957 GNUNET_assert (NULL != rfn_in);
1958
1959 progress_cls = GNUNET_new (struct SetMutationProgressCls);
1960 progress_cls->task = task;
1961
1962 iter = GNUNET_CONTAINER_multihashmap_iterator_create (rfn_in->rfn_elements);
1963
1964 while (GNUNET_YES ==
1965 GNUNET_CONTAINER_multihashmap_iterator_next (iter,
1966 NULL,
1967 (const void **) &ri))
1968 {
1969 uint16_t majority_num;
1970 enum ReferendumVote majority_vote;
1971
1972 rfn_majority (rfn_in, ri, &majority_num, &majority_vote);
1973
1974 if (worst_majority > majority_num)
1975 worst_majority = majority_num;
1976
1977 switch (majority_vote)
1978 {
1979 case VOTE_ADD:
1980 progress_cls->num_pending++;
1981 GNUNET_assert (GNUNET_OK ==
1982 GNUNET_SET_add_element (set_out->h,
1983 ri->element,
1984 &set_mutation_done,
1985 progress_cls));
1986 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1987 "P%u: apply round: adding element %s with %u-majority.\n",
1988 session->local_peer_idx,
1989 debug_str_element (ri->element), majority_num);
1990 break;
1991
1992 case VOTE_REMOVE:
1993 progress_cls->num_pending++;
1994 GNUNET_assert (GNUNET_OK ==
1995 GNUNET_SET_remove_element (set_out->h,
1996 ri->element,
1997 &set_mutation_done,
1998 progress_cls));
1999 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2000 "P%u: apply round: deleting element %s with %u-majority.\n",
2001 session->local_peer_idx,
2002 debug_str_element (ri->element), majority_num);
2003 break;
2004
2005 case VOTE_STAY:
2006 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2007 "P%u: apply round: keeping element %s with %u-majority.\n",
2008 session->local_peer_idx,
2009 debug_str_element (ri->element), majority_num);
2010 // do nothing
2011 break;
2012
2013 default:
2014 GNUNET_assert (0);
2015 break;
2016 }
2017 }
2018
2019 if (0 == progress_cls->num_pending)
2020 {
2021 // call closure right now, no pending ops
2022 GNUNET_free (progress_cls);
2023 finish_task (task);
2024 }
2025
2026 {
2027 uint16_t thresh = (session->num_peers / 3) * 2;
2028
2029 if (worst_majority >= thresh)
2030 {
2031 switch (session->early_stopping)
2032 {
2033 case EARLY_STOPPING_NONE:
2034 session->early_stopping = EARLY_STOPPING_ONE_MORE;
2035 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2036 "P%u: Stopping early (after one more superround)\n",
2037 session->local_peer_idx);
2038 break;
2039
2040 case EARLY_STOPPING_ONE_MORE:
2041 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2042 "P%u: finishing steps due to early finish\n",
2043 session->local_peer_idx);
2044 session->early_stopping = EARLY_STOPPING_DONE;
2045 {
2046 struct Step *step;
2047 for (step = session->steps_head; NULL != step; step = step->next)
2048 try_finish_step_early (step);
2049 }
2050 break;
2051
2052 case EARLY_STOPPING_DONE:
2053 /* We shouldn't be here anymore after early stopping */
2054 GNUNET_break (0);
2055 break;
2056
2057 default:
2058 GNUNET_assert (0);
2059 break;
2060 }
2061 }
2062 else if (EARLY_STOPPING_NONE != session->early_stopping)
2063 {
2064 // Our assumption about the number of bad peers
2065 // has been broken.
2066 GNUNET_break_op (0);
2067 }
2068 else
2069 {
2070 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2071 "P%u: NOT finishing early (majority not good enough)\n",
2072 session->local_peer_idx);
2073 }
2074 }
2075 GNUNET_CONTAINER_multihashmap_iterator_destroy (iter);
2076}
2077
2078
2079static void
2080task_start_grade (struct TaskEntry *task)
2081{
2082 struct ConsensusSession *session = task->step->session;
2083 struct ReferendumEntry *output_rfn;
2084 struct ReferendumEntry *input_rfn;
2085 struct DiffEntry *input_diff;
2086 struct RfnKey rfn_key;
2087 struct DiffKey diff_key;
2088 struct GNUNET_CONTAINER_MultiHashMapIterator *iter;
2089 struct RfnElementInfo *ri;
2090 unsigned int gradecast_confidence = 2;
2091
2092 rfn_key = (struct RfnKey) { RFN_KIND_GRADECAST_RESULT, task->key.repetition };
2093 output_rfn = lookup_rfn (session, &rfn_key);
2094 if (NULL == output_rfn)
2095 {
2096 output_rfn = rfn_create (session->num_peers);
2097 output_rfn->key = rfn_key;
2098 put_rfn (session, output_rfn);
2099 }
2100
2101 diff_key = (struct DiffKey) { DIFF_KIND_LEADER_PROPOSAL, task->key.repetition,
2102 task->key.leader };
2103 input_diff = lookup_diff (session, &diff_key);
2104 GNUNET_assert (NULL != input_diff);
2105
2106 rfn_key = (struct RfnKey) { RFN_KIND_ECHO, task->key.repetition,
2107 task->key.leader };
2108 input_rfn = lookup_rfn (session, &rfn_key);
2109 GNUNET_assert (NULL != input_rfn);
2110
2111 iter = GNUNET_CONTAINER_multihashmap_iterator_create (
2112 input_rfn->rfn_elements);
2113
2114 apply_diff_to_rfn (input_diff, output_rfn, task->key.leader,
2115 session->num_peers);
2116
2117 while (GNUNET_YES ==
2118 GNUNET_CONTAINER_multihashmap_iterator_next (iter,
2119 NULL,
2120 (const void **) &ri))
2121 {
2122 uint16_t majority_num;
2123 enum ReferendumVote majority_vote;
2124
2125 // XXX: we need contested votes and non-contested votes here
2126 rfn_majority (input_rfn, ri, &majority_num, &majority_vote);
2127
2128 if (majority_num <= session->num_peers / 3)
2129 majority_vote = VOTE_REMOVE;
2130
2131 switch (majority_vote)
2132 {
2133 case VOTE_STAY:
2134 break;
2135
2136 case VOTE_ADD:
2137 rfn_vote (output_rfn, task->key.leader, VOTE_ADD, ri->element);
2138 break;
2139
2140 case VOTE_REMOVE:
2141 rfn_vote (output_rfn, task->key.leader, VOTE_REMOVE, ri->element);
2142 break;
2143
2144 default:
2145 GNUNET_assert (0);
2146 break;
2147 }
2148 }
2149 GNUNET_CONTAINER_multihashmap_iterator_destroy (iter);
2150
2151 {
2152 uint16_t noncontested;
2153 noncontested = rfn_noncontested (input_rfn);
2154 if (noncontested < (session->num_peers / 3) * 2)
2155 {
2156 gradecast_confidence = GNUNET_MIN (1, gradecast_confidence);
2157 }
2158 if (noncontested < (session->num_peers / 3) + 1)
2159 {
2160 gradecast_confidence = 0;
2161 }
2162 }
2163
2164 if (gradecast_confidence >= 1)
2165 rfn_commit (output_rfn, task->key.leader);
2166
2167 if (gradecast_confidence <= 1)
2168 session->peers_blacklisted[task->key.leader] = GNUNET_YES;
2169
2170 finish_task (task);
2171}
2172
2173
2174static void
2175task_start_reconcile (struct TaskEntry *task)
2176{
2177 struct SetEntry *input;
2178 struct SetOpCls *setop = &task->cls.setop;
2179 struct ConsensusSession *session = task->step->session;
2180
2181 input = lookup_set (session, &setop->input_set);
2182 GNUNET_assert (NULL != input);
2183 GNUNET_assert (NULL != input->h);
2184
2185 /* We create the outputs for the operation here
2186 (rather than in the set operation callback)
2187 because we want something valid in there, even
2188 if the other peer doesn't talk to us */
2189
2190 if (SET_KIND_NONE != setop->output_set.set_kind)
2191 {
2192 /* If we don't have an existing output set,
2193 we clone the input set. */
2194 if (NULL == lookup_set (session, &setop->output_set))
2195 {
2196 create_set_copy_for_task (task,
2197 &setop->input_set,
2198 &setop->output_set);
2199 return;
2200 }
2201 }
2202
2203 if (RFN_KIND_NONE != setop->output_rfn.rfn_kind)
2204 {
2205 if (NULL == lookup_rfn (session, &setop->output_rfn))
2206 {
2207 struct ReferendumEntry *rfn;
2208
2209 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2210 "P%u: output rfn <%s> missing, creating.\n",
2211 session->local_peer_idx,
2212 debug_str_rfn_key (&setop->output_rfn));
2213
2214 rfn = rfn_create (session->num_peers);
2215 rfn->key = setop->output_rfn;
2216 put_rfn (session, rfn);
2217 }
2218 }
2219
2220 if (DIFF_KIND_NONE != setop->output_diff.diff_kind)
2221 {
2222 if (NULL == lookup_diff (session, &setop->output_diff))
2223 {
2224 struct DiffEntry *diff;
2225
2226 diff = diff_create ();
2227 diff->key = setop->output_diff;
2228 put_diff (session, diff);
2229 }
2230 }
2231
2232 if ((task->key.peer1 == session->local_peer_idx) && (task->key.peer2 ==
2233 session->local_peer_idx))
2234 {
2235 /* XXX: mark the corresponding rfn as committed if necessary */
2236 finish_task (task);
2237 return;
2238 }
2239
2240 if (task->key.peer1 == session->local_peer_idx)
2241 {
2242 struct GNUNET_CONSENSUS_RoundContextMessage rcm;
2243
2244 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2245 "P%u: Looking up set {%s} to run remote union\n",
2246 session->local_peer_idx,
2247 debug_str_set_key (&setop->input_set));
2248 rcm.header.type = htons (GNUNET_MESSAGE_TYPE_CONSENSUS_P2P_ROUND_CONTEXT);
2249 rcm.header.size = htons (sizeof(rcm));
2250 rcm.kind = htons (task->key.kind);
2251 rcm.peer1 = htons (task->key.peer1);
2252 rcm.peer2 = htons (task->key.peer2);
2253 rcm.leader = htons (task->key.leader);
2254 rcm.repetition = htons (task->key.repetition);
2255 rcm.is_contested = htons (0);
2256
2257 GNUNET_assert (NULL == setop->op);
2258 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2259 "P%u: initiating set op with P%u, our set is %s\n",
2260 session->local_peer_idx,
2261 task->key.peer2,
2262 debug_str_set_key (&setop->input_set));
2263
2264 struct GNUNET_SET_Option opts[] = {
2265 { GNUNET_SET_OPTION_BYZANTINE, { .num = session->lower_bound } },
2266 { GNUNET_SET_OPTION_END },
2267 };
2268
2269 // XXX: maybe this should be done while
2270 // setting up tasks alreays?
2271 setop->op = GNUNET_SET_prepare (&session->peers[task->key.peer2],
2272 &session->global_id,
2273 &rcm.header,
2274 GNUNET_SET_RESULT_SYMMETRIC,
2275 opts,
2276 set_result_cb,
2277 task);
2278
2279 commit_set (session, task);
2280 }
2281 else if (task->key.peer2 == session->local_peer_idx)
2282 {
2283 /* Wait for the other peer to contact us */
2284 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "P%u: waiting set op with P%u\n",
2285 session->local_peer_idx, task->key.peer1);
2286
2287 if (NULL != setop->op)
2288 {
2289 commit_set (session, task);
2290 }
2291 }
2292 else
2293 {
2294 /* We made an error while constructing the task graph. */
2295 GNUNET_assert (0);
2296 }
2297}
2298
2299
2300static void
2301task_start_eval_echo (struct TaskEntry *task)
2302{
2303 struct GNUNET_CONTAINER_MultiHashMapIterator *iter;
2304 struct ReferendumEntry *input_rfn;
2305 struct RfnElementInfo *ri;
2306 struct SetEntry *output_set;
2307 struct SetMutationProgressCls *progress_cls;
2308 struct ConsensusSession *session = task->step->session;
2309 struct SetKey sk_in;
2310 struct SetKey sk_out;
2311 struct RfnKey rk_in;
2312
2313 sk_in = (struct SetKey) { SET_KIND_LEADER_PROPOSAL, task->key.repetition,
2314 task->key.leader };
2315 sk_out = (struct SetKey) { SET_KIND_ECHO_RESULT, task->key.repetition,
2316 task->key.leader };
2317 output_set = lookup_set (session, &sk_out);
2318 if (NULL == output_set)
2319 {
2320 create_set_copy_for_task (task,
2321 &sk_in,
2322 &sk_out);
2323 return;
2324 }
2325
2326 {
2327 // FIXME: should be marked as a shallow copy, so
2328 // we can destroy everything correctly
2329 struct SetEntry *last_set = GNUNET_new (struct SetEntry);
2330
2331 last_set->h = output_set->h;
2332 last_set->key = (struct SetKey) { SET_KIND_LAST_GRADECAST };
2333 put_set (session, last_set);
2334 }
2335
2336 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2337 "Evaluating referendum in Task {%s}\n",
2338 debug_str_task_key (&task->key));
2339
2340 progress_cls = GNUNET_new (struct SetMutationProgressCls);
2341 progress_cls->task = task;
2342 rk_in = (struct RfnKey) { RFN_KIND_ECHO, task->key.repetition,
2343 task->key.leader };
2344 input_rfn = lookup_rfn (session, &rk_in);
2345 GNUNET_assert (NULL != input_rfn);
2346 iter = GNUNET_CONTAINER_multihashmap_iterator_create (
2347 input_rfn->rfn_elements);
2348 GNUNET_assert (NULL != iter);
2349
2350 while (GNUNET_YES ==
2351 GNUNET_CONTAINER_multihashmap_iterator_next (iter,
2352 NULL,
2353 (const void **) &ri))
2354 {
2355 enum ReferendumVote majority_vote;
2356 uint16_t majority_num;
2357
2358 rfn_majority (input_rfn, ri, &majority_num, &majority_vote);
2359
2360 if (majority_num < session->num_peers / 3)
2361 {
2362 /* It is not the case that all nonfaulty peers
2363 echoed the same value. Since we're doing a set reconciliation, we
2364 can't simply send "nothing" for the value. Thus we mark our 'confirm'
2365 reconciliation as contested. Other peers might not know that the
2366 leader is faulty, thus we still re-distribute in the confirmation
2367 round. *///
2368 output_set->is_contested = GNUNET_YES;
2369 }
2370
2371 switch (majority_vote)
2372 {
2373 case VOTE_ADD:
2374 progress_cls->num_pending++;
2375 GNUNET_assert (GNUNET_OK ==
2376 GNUNET_SET_add_element (output_set->h,
2377 ri->element,
2378 set_mutation_done,
2379 progress_cls));
2380 break;
2381
2382 case VOTE_REMOVE:
2383 progress_cls->num_pending++;
2384 GNUNET_assert (GNUNET_OK ==
2385 GNUNET_SET_remove_element (output_set->h,
2386 ri->element,
2387 set_mutation_done,
2388 progress_cls));
2389 break;
2390
2391 case VOTE_STAY:
2392 /* Nothing to do. */
2393 break;
2394
2395 default:
2396 /* not reached */
2397 GNUNET_assert (0);
2398 }
2399 }
2400
2401 GNUNET_CONTAINER_multihashmap_iterator_destroy (iter);
2402
2403 if (0 == progress_cls->num_pending)
2404 {
2405 // call closure right now, no pending ops
2406 GNUNET_free (progress_cls);
2407 finish_task (task);
2408 }
2409}
2410
2411
2412static void
2413task_start_finish (struct TaskEntry *task)
2414{
2415 struct SetEntry *final_set;
2416 struct ConsensusSession *session = task->step->session;
2417
2418 final_set = lookup_set (session,
2419 &task->cls.finish.input_set);
2420 GNUNET_assert (NULL != final_set);
2421 GNUNET_SET_iterate (final_set->h,
2422 &send_to_client_iter,
2423 task);
2424}
2425
2426
2427static void
2428start_task (struct ConsensusSession *session,
2429 struct TaskEntry *task)
2430{
2431 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2432 "P%u: starting task {%s}\n",
2433 session->local_peer_idx,
2434 debug_str_task_key (&task->key));
2435 GNUNET_assert (GNUNET_NO == task->is_started);
2436 GNUNET_assert (GNUNET_NO == task->is_finished);
2437 GNUNET_assert (NULL != task->start);
2438 task->start (task);
2439 task->is_started = GNUNET_YES;
2440}
2441
2442
2443/*
2444 * Run all steps of the session that don't any
2445 * more dependencies.
2446 */
2447static void
2448run_ready_steps (struct ConsensusSession *session)
2449{
2450 struct Step *step;
2451
2452 step = session->steps_head;
2453
2454 while (NULL != step)
2455 {
2456 if ((GNUNET_NO == step->is_running) && (0 == step->pending_prereq) &&
2457 (GNUNET_NO == step->is_finished))
2458 {
2459 GNUNET_assert (0 == step->finished_tasks);
2460
2461#ifdef GNUNET_EXTRA_LOGGING
2462 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2463 "P%u: Running step `%s' of round %d with %d tasks and %d subordinates\n",
2464 session->local_peer_idx,
2465 step->debug_name,
2466 step->round, step->tasks_len, step->subordinates_len);
2467#endif
2468
2469 step->is_running = GNUNET_YES;
2470 for (size_t i = 0; i < step->tasks_len; i++)
2471 start_task (session, step->tasks[i]);
2472
2473 /* Sometimes there is no task to trigger finishing the step, so we have to do it here. */
2474 if ((step->finished_tasks == step->tasks_len) && (GNUNET_NO ==
2475 step->is_finished))
2476 finish_step (step);
2477
2478 /* Running the next ready steps will be triggered by task completion */
2479 return;
2480 }
2481 step = step->next;
2482 }
2483
2484 return;
2485}
2486
2487
2488static void
2489finish_task (struct TaskEntry *task)
2490{
2491 GNUNET_assert (GNUNET_NO == task->is_finished);
2492 task->is_finished = GNUNET_YES;
2493 task->step->finished_tasks++;
2494 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2495 "P%u: Finishing Task {%s} (now %u/%u tasks finished in step)\n",
2496 task->step->session->local_peer_idx,
2497 debug_str_task_key (&task->key),
2498 (unsigned int) task->step->finished_tasks,
2499 (unsigned int) task->step->tasks_len);
2500
2501 if (task->step->finished_tasks == task->step->tasks_len)
2502 finish_step (task->step);
2503}
2504
2505
2506/**
2507 * Search peer in the list of peers in session.
2508 *
2509 * @param peer peer to find
2510 * @param session session with peer
2511 * @return index of peer, -1 if peer is not in session
2512 */
2513static int
2514get_peer_idx (const struct GNUNET_PeerIdentity *peer,
2515 const struct ConsensusSession *session)
2516{
2517 for (int i = 0; i < session->num_peers; i++)
2518 if (0 == GNUNET_memcmp (peer, &session->peers[i]))
2519 return i;
2520 return -1;
2521}
2522
2523
2524/**
2525 * Compute a global, (hopefully) unique consensus session id,
2526 * from the local id of the consensus session, and the identities of all participants.
2527 * Thus, if the local id of two consensus sessions coincide, but are not comprised of
2528 * exactly the same peers, the global id will be different.
2529 *
2530 * @param session session to generate the global id for
2531 * @param local_session_id local id of the consensus session
2532 */
2533static void
2534compute_global_id (struct ConsensusSession *session,
2535 const struct GNUNET_HashCode *local_session_id)
2536{
2537 const char *salt = "gnunet-service-consensus/session_id";
2538
2539 GNUNET_assert (GNUNET_YES ==
2540 GNUNET_CRYPTO_kdf (&session->global_id,
2541 sizeof(struct GNUNET_HashCode),
2542 salt,
2543 strlen (salt),
2544 session->peers,
2545 session->num_peers * sizeof(struct
2546 GNUNET_PeerIdentity),
2547 local_session_id,
2548 sizeof(struct GNUNET_HashCode),
2549 NULL));
2550}
2551
2552
2553/**
2554 * Compare two peer identities (for qsort()).
2555 *
2556 * @param h1 some peer identity
2557 * @param h2 some peer identity
2558 * @return 1 if h1 > h2, -1 if h1 < h2 and 0 if h1 == h2.
2559 */
2560static int
2561peer_id_cmp (const void *h1,
2562 const void *h2)
2563{
2564 return memcmp (h1, h2, sizeof(struct GNUNET_PeerIdentity));
2565}
2566
2567
2568/**
2569 * Create the sorted list of peers for the session,
2570 * add the local peer if not in the join message.
2571 *
2572 * @param session session to initialize
2573 * @param join_msg join message with the list of peers participating at the end
2574 */
2575static void
2576initialize_session_peer_list (
2577 struct ConsensusSession *session,
2578 const struct GNUNET_CONSENSUS_JoinMessage *join_msg)
2579{
2580 const struct GNUNET_PeerIdentity *msg_peers
2581 = (const struct GNUNET_PeerIdentity *) &join_msg[1];
2582 int local_peer_in_list;
2583
2584 session->num_peers = ntohl (join_msg->num_peers);
2585
2586 /* Peers in the join message, may or may not include the local peer,
2587 Add it if it is missing. */
2588 local_peer_in_list = GNUNET_NO;
2589 for (unsigned int i = 0; i < session->num_peers; i++)
2590 {
2591 if (0 == GNUNET_memcmp (&msg_peers[i],
2592 &my_peer))
2593 {
2594 local_peer_in_list = GNUNET_YES;
2595 break;
2596 }
2597 }
2598 if (GNUNET_NO == local_peer_in_list)
2599 session->num_peers++;
2600
2601 session->peers = GNUNET_new_array (session->num_peers,
2602 struct GNUNET_PeerIdentity);
2603 if (GNUNET_NO == local_peer_in_list)
2604 session->peers[session->num_peers - 1] = my_peer;
2605 GNUNET_memcpy (session->peers,
2606 msg_peers,
2607 ntohl (join_msg->num_peers)
2608 * sizeof(struct GNUNET_PeerIdentity));
2609 qsort (session->peers,
2610 session->num_peers,
2611 sizeof (struct GNUNET_PeerIdentity),
2612 &peer_id_cmp);
2613}
2614
2615
2616static struct TaskEntry *
2617lookup_task (const struct ConsensusSession *session,
2618 const struct TaskKey *key)
2619{
2620 struct GNUNET_HashCode hash;
2621
2622 GNUNET_CRYPTO_hash (key,
2623 sizeof(struct TaskKey),
2624 &hash);
2625 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2626 "Looking up task hash %s\n",
2627 GNUNET_h2s (&hash));
2628 return GNUNET_CONTAINER_multihashmap_get (session->taskmap,
2629 &hash);
2630}
2631
2632
2633/**
2634 * Called when another peer wants to do a set operation with the
2635 * local peer.
2636 *
2637 * @param cls closure
2638 * @param other_peer the other peer
2639 * @param context_msg message with application specific information from
2640 * the other peer
2641 * @param request request from the other peer, use GNUNET_SET_accept
2642 * to accept it, otherwise the request will be refused
2643 * Note that we don't use a return value here, as it is also
2644 * necessary to specify the set we want to do the operation with,
2645 * which sometimes can be derived from the context message.
2646 * Also necessary to specify the timeout.
2647 */
2648static void
2649set_listen_cb (void *cls,
2650 const struct GNUNET_PeerIdentity *other_peer,
2651 const struct GNUNET_MessageHeader *context_msg,
2652 struct GNUNET_SET_Request *request)
2653{
2654 struct ConsensusSession *session = cls;
2655 struct TaskKey tk;
2656 struct TaskEntry *task;
2657 struct GNUNET_CONSENSUS_RoundContextMessage *cm;
2658
2659 if (NULL == context_msg)
2660 {
2661 GNUNET_break_op (0);
2662 return;
2663 }
2664
2665 if (GNUNET_MESSAGE_TYPE_CONSENSUS_P2P_ROUND_CONTEXT != ntohs (
2666 context_msg->type))
2667 {
2668 GNUNET_break_op (0);
2669 return;
2670 }
2671
2672 if (sizeof(struct GNUNET_CONSENSUS_RoundContextMessage) != ntohs (
2673 context_msg->size))
2674 {
2675 GNUNET_break_op (0);
2676 return;
2677 }
2678
2679 cm = (struct GNUNET_CONSENSUS_RoundContextMessage *) context_msg;
2680
2681 tk = ((struct TaskKey) {
2682 .kind = ntohs (cm->kind),
2683 .peer1 = ntohs (cm->peer1),
2684 .peer2 = ntohs (cm->peer2),
2685 .repetition = ntohs (cm->repetition),
2686 .leader = ntohs (cm->leader),
2687 });
2688
2689 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "P%u: got req for task %s\n",
2690 session->local_peer_idx, debug_str_task_key (&tk));
2691
2692 task = lookup_task (session, &tk);
2693
2694 if (NULL == task)
2695 {
2696 GNUNET_break_op (0);
2697 return;
2698 }
2699
2700 if (GNUNET_YES == task->is_finished)
2701 {
2702 GNUNET_break_op (0);
2703 return;
2704 }
2705
2706 if (task->key.peer2 != session->local_peer_idx)
2707 {
2708 /* We're being asked, so we must be the 2nd peer. */
2709 GNUNET_break_op (0);
2710 return;
2711 }
2712
2713 GNUNET_assert (! ((task->key.peer1 == session->local_peer_idx) &&
2714 (task->key.peer2 == session->local_peer_idx)));
2715
2716 struct GNUNET_SET_Option opts[] = {
2717 { GNUNET_SET_OPTION_BYZANTINE, { .num = session->lower_bound } },
2718 { GNUNET_SET_OPTION_END },
2719 };
2720
2721 task->cls.setop.op = GNUNET_SET_accept (request,
2722 GNUNET_SET_RESULT_SYMMETRIC,
2723 opts,
2724 &set_result_cb,
2725 task);
2726
2727 /* If the task hasn't been started yet,
2728 we wait for that until we commit. */
2729
2730 if (GNUNET_YES == task->is_started)
2731 {
2732 commit_set (session, task);
2733 }
2734}
2735
2736
2737static void
2738put_task (struct GNUNET_CONTAINER_MultiHashMap *taskmap,
2739 struct TaskEntry *t)
2740{
2741 struct GNUNET_HashCode round_hash;
2742 struct Step *s;
2743
2744 GNUNET_assert (NULL != t->step);
2745 t = GNUNET_memdup (t, sizeof(struct TaskEntry));
2746 s = t->step;
2747 if (s->tasks_len == s->tasks_cap)
2748 {
2749 unsigned int target_size = 3 * (s->tasks_cap + 1) / 2;
2750 GNUNET_array_grow (s->tasks,
2751 s->tasks_cap,
2752 target_size);
2753 }
2754
2755#ifdef GNUNET_EXTRA_LOGGING
2756 GNUNET_assert (NULL != s->debug_name);
2757 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Putting task <%s> into step `%s'\n",
2758 debug_str_task_key (&t->key),
2759 s->debug_name);
2760#endif
2761
2762 s->tasks[s->tasks_len] = t;
2763 s->tasks_len++;
2764
2765 GNUNET_CRYPTO_hash (&t->key,
2766 sizeof(struct TaskKey),
2767 &round_hash);
2768 GNUNET_assert (GNUNET_OK ==
2769 GNUNET_CONTAINER_multihashmap_put (taskmap,
2770 &round_hash,
2771 t,
2772 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2773}
2774
2775
2776static void
2777install_step_timeouts (struct ConsensusSession *session)
2778{
2779 /* Given the fully constructed task graph
2780 with rounds for tasks, we can give the tasks timeouts. */
2781
2782 // unsigned int max_round;
2783
2784 /* XXX: implement! */
2785}
2786
2787
2788/*
2789 * Arrange two peers in some canonical order.
2790 */
2791static void
2792arrange_peers (uint16_t *p1,
2793 uint16_t *p2,
2794 uint16_t n)
2795{
2796 uint16_t a;
2797 uint16_t b;
2798
2799 GNUNET_assert (*p1 < n);
2800 GNUNET_assert (*p2 < n);
2801
2802 if (*p1 < *p2)
2803 {
2804 a = *p1;
2805 b = *p2;
2806 }
2807 else
2808 {
2809 a = *p2;
2810 b = *p1;
2811 }
2812
2813 /* For uniformly random *p1, *p2,
2814 this condition is true with 50% chance */
2815 if (((b - a) + n) % n <= n / 2)
2816 {
2817 *p1 = a;
2818 *p2 = b;
2819 }
2820 else
2821 {
2822 *p1 = b;
2823 *p2 = a;
2824 }
2825}
2826
2827
2828/**
2829 * Record @a dep as a dependency of @a step.
2830 */
2831static void
2832step_depend_on (struct Step *step,
2833 struct Step *dep)
2834{
2835 /* We're not checking for cyclic dependencies,
2836 but this is a cheap sanity check. */
2837 GNUNET_assert (step != dep);
2838 GNUNET_assert (NULL != step);
2839 GNUNET_assert (NULL != dep);
2840 GNUNET_assert (dep->round <= step->round);
2841
2842#ifdef GNUNET_EXTRA_LOGGING
2843 /* Make sure we have complete debugging information.
2844 Also checks that we don't screw up too badly
2845 constructing the task graph. */
2846 GNUNET_assert (NULL != step->debug_name);
2847 GNUNET_assert (NULL != dep->debug_name);
2848 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2849 "Making step `%s' depend on `%s'\n",
2850 step->debug_name,
2851 dep->debug_name);
2852#endif
2853
2854 if (dep->subordinates_cap == dep->subordinates_len)
2855 {
2856 unsigned int target_size = 3 * (dep->subordinates_cap + 1) / 2;
2857 GNUNET_array_grow (dep->subordinates,
2858 dep->subordinates_cap,
2859 target_size);
2860 }
2861
2862 GNUNET_assert (dep->subordinates_len <= dep->subordinates_cap);
2863
2864 dep->subordinates[dep->subordinates_len] = step;
2865 dep->subordinates_len++;
2866
2867 step->pending_prereq++;
2868}
2869
2870
2871static struct Step *
2872create_step (struct ConsensusSession *session,
2873 int round,
2874 int early_finishable)
2875{
2876 struct Step *step;
2877
2878 step = GNUNET_new (struct Step);
2879 step->session = session;
2880 step->round = round;
2881 step->early_finishable = early_finishable;
2882 GNUNET_CONTAINER_DLL_insert_tail (session->steps_head,
2883 session->steps_tail,
2884 step);
2885 return step;
2886}
2887
2888
2889/**
2890 * Construct the task graph for a single gradecast.
2891 */
2892static void
2893construct_task_graph_gradecast (struct ConsensusSession *session,
2894 uint16_t rep,
2895 uint16_t lead,
2896 struct Step *step_before,
2897 struct Step *step_after)
2898{
2899 uint16_t n = session->num_peers;
2900 uint16_t me = session->local_peer_idx;
2901 uint16_t p1;
2902 uint16_t p2;
2903 /* The task we're currently setting up. */
2904 struct TaskEntry task;
2905 struct Step *step;
2906 struct Step *prev_step;
2907 uint16_t round;
2908
2909 round = step_before->round + 1;
2910
2911 /* gcast step 1: leader disseminates */
2912 step = create_step (session,
2913 round,
2914 GNUNET_YES);
2915#ifdef GNUNET_EXTRA_LOGGING
2916 GNUNET_asprintf (&step->debug_name,
2917 "disseminate leader %u rep %u",
2918 lead,
2919 rep);
2920#endif
2921 step_depend_on (step,
2922 step_before);
2923
2924 if (lead == me)
2925 {
2926 for (unsigned int k = 0; k < n; k++)
2927 {
2928 if (k == me)
2929 continue;
2930 p1 = me;
2931 p2 = k;
2932 arrange_peers (&p1, &p2, n);
2933 task = ((struct TaskEntry) {
2934 .step = step,
2935 .start = task_start_reconcile,
2936 .cancel = task_cancel_reconcile,
2937 .key = (struct TaskKey) { PHASE_KIND_GRADECAST_LEADER, p1, p2, rep,
2938 me },
2939 });
2940 task.cls.setop.input_set = (struct SetKey) { SET_KIND_CURRENT, rep };
2941 put_task (session->taskmap, &task);
2942 }
2943 /* We run this task to make sure that the leader
2944 has the stored the SET_KIND_LEADER set of himself,
2945 so it can participate in the rest of the gradecast
2946 without the code having to handle any special cases. */
2947 task = ((struct TaskEntry) {
2948 .step = step,
2949 .key = (struct TaskKey) { PHASE_KIND_GRADECAST_LEADER, me, me, rep, me },
2950 .start = task_start_reconcile,
2951 .cancel = task_cancel_reconcile,
2952 });
2953 task.cls.setop.input_set = (struct SetKey) { SET_KIND_CURRENT, rep };
2954 task.cls.setop.output_set = (struct SetKey) { SET_KIND_LEADER_PROPOSAL, rep,
2955 me };
2956 task.cls.setop.output_diff = (struct DiffKey) { DIFF_KIND_LEADER_PROPOSAL,
2957 rep, me };
2958 put_task (session->taskmap, &task);
2959 }
2960 else
2961 {
2962 p1 = me;
2963 p2 = lead;
2964 arrange_peers (&p1, &p2, n);
2965 task = ((struct TaskEntry) {
2966 .step = step,
2967 .key = (struct TaskKey) { PHASE_KIND_GRADECAST_LEADER, p1, p2, rep,
2968 lead },
2969 .start = task_start_reconcile,
2970 .cancel = task_cancel_reconcile,
2971 });
2972 task.cls.setop.input_set = (struct SetKey) { SET_KIND_CURRENT, rep };
2973 task.cls.setop.output_set = (struct SetKey) { SET_KIND_LEADER_PROPOSAL, rep,
2974 lead };
2975 task.cls.setop.output_diff = (struct DiffKey) { DIFF_KIND_LEADER_PROPOSAL,
2976 rep, lead };
2977 put_task (session->taskmap, &task);
2978 }
2979
2980 /* gcast phase 2: echo */
2981 prev_step = step;
2982 round += 1;
2983 step = create_step (session,
2984 round,
2985 GNUNET_YES);
2986#ifdef GNUNET_EXTRA_LOGGING
2987 GNUNET_asprintf (&step->debug_name,
2988 "echo leader %u rep %u",
2989 lead,
2990 rep);
2991#endif
2992 step_depend_on (step,
2993 prev_step);
2994
2995 for (unsigned int k = 0; k < n; k++)
2996 {
2997 p1 = k;
2998 p2 = me;
2999 arrange_peers (&p1, &p2, n);
3000 task = ((struct TaskEntry) {
3001 .step = step,
3002 .key = (struct TaskKey) { PHASE_KIND_GRADECAST_ECHO, p1, p2, rep, lead },
3003 .start = task_start_reconcile,
3004 .cancel = task_cancel_reconcile,
3005 });
3006 task.cls.setop.input_set = (struct SetKey) { SET_KIND_LEADER_PROPOSAL, rep,
3007 lead };
3008 task.cls.setop.output_rfn = (struct RfnKey) { RFN_KIND_ECHO, rep, lead };
3009 put_task (session->taskmap, &task);
3010 }
3011
3012 prev_step = step;
3013 /* Same round, since step only has local tasks */
3014 step = create_step (session, round, GNUNET_YES);
3015#ifdef GNUNET_EXTRA_LOGGING
3016 GNUNET_asprintf (&step->debug_name, "echo grade leader %u rep %u", lead, rep);
3017#endif
3018 step_depend_on (step, prev_step);
3019
3020 arrange_peers (&p1, &p2, n);
3021 task = ((struct TaskEntry) {
3022 .key = (struct TaskKey) { PHASE_KIND_GRADECAST_ECHO_GRADE, -1, -1, rep,
3023 lead },
3024 .step = step,
3025 .start = task_start_eval_echo
3026 });
3027 put_task (session->taskmap, &task);
3028
3029 prev_step = step;
3030 round += 1;
3031 step = create_step (session, round, GNUNET_YES);
3032#ifdef GNUNET_EXTRA_LOGGING
3033 GNUNET_asprintf (&step->debug_name, "confirm leader %u rep %u", lead, rep);
3034#endif
3035 step_depend_on (step, prev_step);
3036
3037 /* gcast phase 3: confirmation and grading */
3038 for (unsigned int k = 0; k < n; k++)
3039 {
3040 p1 = k;
3041 p2 = me;
3042 arrange_peers (&p1, &p2, n);
3043 task = ((struct TaskEntry) {
3044 .step = step,
3045 .start = task_start_reconcile,
3046 .cancel = task_cancel_reconcile,
3047 .key = (struct TaskKey) { PHASE_KIND_GRADECAST_CONFIRM, p1, p2, rep,
3048 lead },
3049 });
3050 task.cls.setop.input_set = (struct SetKey) { SET_KIND_ECHO_RESULT, rep,
3051 lead };
3052 task.cls.setop.output_rfn = (struct RfnKey) { RFN_KIND_CONFIRM, rep, lead };
3053 /* If there was at least one element in the echo round that was
3054 contested (i.e. it had no n-t majority), then we let the other peers
3055 know, and other peers let us know. The contested flag for each peer is
3056 stored in the rfn. */
3057 task.cls.setop.transceive_contested = GNUNET_YES;
3058 put_task (session->taskmap, &task);
3059 }
3060
3061 prev_step = step;
3062 /* Same round, since step only has local tasks */
3063 step = create_step (session, round, GNUNET_YES);
3064#ifdef GNUNET_EXTRA_LOGGING
3065 GNUNET_asprintf (&step->debug_name, "confirm grade leader %u rep %u", lead,
3066 rep);
3067#endif
3068 step_depend_on (step, prev_step);
3069
3070 task = ((struct TaskEntry) {
3071 .step = step,
3072 .key = (struct TaskKey) { PHASE_KIND_GRADECAST_CONFIRM_GRADE, -1, -1, rep,
3073 lead },
3074 .start = task_start_grade,
3075 });
3076 put_task (session->taskmap, &task);
3077
3078 step_depend_on (step_after, step);
3079}
3080
3081
3082static void
3083construct_task_graph (struct ConsensusSession *session)
3084{
3085 uint16_t n = session->num_peers;
3086 uint16_t t = n / 3;
3087 uint16_t me = session->local_peer_idx;
3088 /* The task we're currently setting up. */
3089 struct TaskEntry task;
3090 /* Current leader */
3091 unsigned int lead;
3092 struct Step *step;
3093 struct Step *prev_step;
3094 unsigned int round = 0;
3095
3096 // XXX: introduce first step,
3097 // where we wait for all insert acks
3098 // from the set service
3099
3100 /* faster but brittle all-to-all */
3101
3102 // XXX: Not implemented yet
3103
3104 /* all-to-all step */
3105
3106 step = create_step (session, round, GNUNET_NO);
3107
3108#ifdef GNUNET_EXTRA_LOGGING
3109 step->debug_name = GNUNET_strdup ("all to all");
3110#endif
3111
3112 for (unsigned int i = 0; i < n; i++)
3113 {
3114 uint16_t p1;
3115 uint16_t p2;
3116
3117 p1 = me;
3118 p2 = i;
3119 arrange_peers (&p1, &p2, n);
3120 task = ((struct TaskEntry) {
3121 .key = (struct TaskKey) { PHASE_KIND_ALL_TO_ALL, p1, p2, -1, -1 },
3122 .step = step,
3123 .start = task_start_reconcile,
3124 .cancel = task_cancel_reconcile,
3125 });
3126 task.cls.setop.input_set = (struct SetKey) { SET_KIND_CURRENT, 0 };
3127 task.cls.setop.output_set = task.cls.setop.input_set;
3128 task.cls.setop.do_not_remove = GNUNET_YES;
3129 put_task (session->taskmap, &task);
3130 }
3131
3132 round += 1;
3133 prev_step = step;
3134 step = create_step (session, round, GNUNET_NO);;
3135#ifdef GNUNET_EXTRA_LOGGING
3136 step->debug_name = GNUNET_strdup ("all to all 2");
3137#endif
3138 step_depend_on (step, prev_step);
3139
3140
3141 for (unsigned int i = 0; i < n; i++)
3142 {
3143 uint16_t p1;
3144 uint16_t p2;
3145
3146 p1 = me;
3147 p2 = i;
3148 arrange_peers (&p1, &p2, n);
3149 task = ((struct TaskEntry) {
3150 .key = (struct TaskKey) { PHASE_KIND_ALL_TO_ALL_2, p1, p2, -1, -1 },
3151 .step = step,
3152 .start = task_start_reconcile,
3153 .cancel = task_cancel_reconcile,
3154 });
3155 task.cls.setop.input_set = (struct SetKey) { SET_KIND_CURRENT, 0 };
3156 task.cls.setop.output_set = task.cls.setop.input_set;
3157 task.cls.setop.do_not_remove = GNUNET_YES;
3158 put_task (session->taskmap, &task);
3159 }
3160
3161 round += 1;
3162
3163 prev_step = step;
3164 step = NULL;
3165
3166
3167 /* Byzantine union */
3168
3169 /* sequential repetitions of the gradecasts */
3170 for (unsigned int i = 0; i < t + 1; i++)
3171 {
3172 struct Step *step_rep_start;
3173 struct Step *step_rep_end;
3174
3175 /* Every repetition is in a separate round. */
3176 step_rep_start = create_step (session, round, GNUNET_YES);
3177#ifdef GNUNET_EXTRA_LOGGING
3178 GNUNET_asprintf (&step_rep_start->debug_name, "gradecast start rep %u", i);
3179#endif
3180
3181 step_depend_on (step_rep_start, prev_step);
3182
3183 /* gradecast has three rounds */
3184 round += 3;
3185 step_rep_end = create_step (session, round, GNUNET_YES);
3186#ifdef GNUNET_EXTRA_LOGGING
3187 GNUNET_asprintf (&step_rep_end->debug_name, "gradecast end rep %u", i);
3188#endif
3189
3190 /* parallel gradecasts */
3191 for (lead = 0; lead < n; lead++)
3192 construct_task_graph_gradecast (session, i, lead, step_rep_start,
3193 step_rep_end);
3194
3195 task = ((struct TaskEntry) {
3196 .step = step_rep_end,
3197 .key = (struct TaskKey) { PHASE_KIND_APPLY_REP, -1, -1, i, -1 },
3198 .start = task_start_apply_round,
3199 });
3200 put_task (session->taskmap, &task);
3201
3202 prev_step = step_rep_end;
3203 }
3204
3205 /* There is no next gradecast round, thus the final
3206 start step is the overall end step of the gradecasts */
3207 round += 1;
3208 step = create_step (session, round, GNUNET_NO);
3209#ifdef GNUNET_EXTRA_LOGGING
3210 GNUNET_asprintf (&step->debug_name, "finish");
3211#endif
3212 step_depend_on (step, prev_step);
3213
3214 task = ((struct TaskEntry) {
3215 .step = step,
3216 .key = (struct TaskKey) { PHASE_KIND_FINISH, -1, -1, -1, -1 },
3217 .start = task_start_finish,
3218 });
3219 task.cls.finish.input_set = (struct SetKey) { SET_KIND_LAST_GRADECAST };
3220
3221 put_task (session->taskmap, &task);
3222}
3223
3224
3225/**
3226 * Check join message.
3227 *
3228 * @param cls session of client that sent the message
3229 * @param m message sent by the client
3230 * @return #GNUNET_OK if @a m is well-formed
3231 */
3232static int
3233check_client_join (void *cls,
3234 const struct GNUNET_CONSENSUS_JoinMessage *m)
3235{
3236 uint32_t listed_peers = ntohl (m->num_peers);
3237
3238 if ((ntohs (m->header.size) - sizeof(*m)) !=
3239 listed_peers * sizeof(struct GNUNET_PeerIdentity))
3240 {
3241 GNUNET_break (0);
3242 return GNUNET_SYSERR;
3243 }
3244 return GNUNET_OK;
3245}
3246
3247
3248/**
3249 * Called when a client wants to join a consensus session.
3250 *
3251 * @param cls session of client that sent the message
3252 * @param m message sent by the client
3253 */
3254static void
3255handle_client_join (void *cls,
3256 const struct GNUNET_CONSENSUS_JoinMessage *m)
3257{
3258 struct ConsensusSession *session = cls;
3259 struct ConsensusSession *other_session;
3260
3261 initialize_session_peer_list (session,
3262 m);
3263 compute_global_id (session,
3264 &m->session_id);
3265
3266 /* Check if some local client already owns the session.
3267 It is only legal to have a session with an existing global id
3268 if all other sessions with this global id are finished.*/
3269 for (other_session = sessions_head;
3270 NULL != other_session;
3271 other_session = other_session->next)
3272 {
3273 if ( (other_session != session) &&
3274 (0 == GNUNET_CRYPTO_hash_cmp (&session->global_id,
3275 &other_session->global_id)) )
3276 break;
3277 }
3278
3279 session->conclude_deadline
3280 = GNUNET_TIME_absolute_ntoh (m->deadline);
3281 session->conclude_start
3282 = GNUNET_TIME_absolute_ntoh (m->start);
3283 session->local_peer_idx = get_peer_idx (&my_peer,
3284 session);
3285 GNUNET_assert (-1 != session->local_peer_idx);
3286
3287 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3288 "Joining consensus session %s containing %u peers as %u with timeout %s\n",
3289 GNUNET_h2s (&m->session_id),
3290 session->num_peers,
3291 session->local_peer_idx,
3292 GNUNET_STRINGS_relative_time_to_string (
3293 GNUNET_TIME_absolute_get_difference (session->conclude_start,
3294 session->conclude_deadline),
3295 GNUNET_YES));
3296
3297 session->set_listener
3298 = GNUNET_SET_listen (cfg,
3299 GNUNET_SET_OPERATION_UNION,
3300 &session->global_id,
3301 &set_listen_cb,
3302 session);
3303
3304 session->setmap = GNUNET_CONTAINER_multihashmap_create (1,
3305 GNUNET_NO);
3306 session->taskmap = GNUNET_CONTAINER_multihashmap_create (1,
3307 GNUNET_NO);
3308 session->diffmap = GNUNET_CONTAINER_multihashmap_create (1,
3309 GNUNET_NO);
3310 session->rfnmap = GNUNET_CONTAINER_multihashmap_create (1,
3311 GNUNET_NO);
3312
3313 {
3314 struct SetEntry *client_set;
3315
3316 client_set = GNUNET_new (struct SetEntry);
3317 client_set->h = GNUNET_SET_create (cfg,
3318 GNUNET_SET_OPERATION_UNION);
3319 struct SetHandle *sh = GNUNET_new (struct SetHandle);
3320 sh->h = client_set->h;
3321 GNUNET_CONTAINER_DLL_insert (session->set_handles_head,
3322 session->set_handles_tail,
3323 sh);
3324 client_set->key = ((struct SetKey) { SET_KIND_CURRENT, 0, 0 });
3325 put_set (session,
3326 client_set);
3327 }
3328
3329 session->peers_blacklisted = GNUNET_new_array (session->num_peers,
3330 int);
3331
3332 /* Just construct the task graph,
3333 but don't run anything until the client calls conclude. */
3334 construct_task_graph (session);
3335 GNUNET_SERVICE_client_continue (session->client);
3336}
3337
3338
3339/**
3340 * Called when a client performs an insert operation.
3341 *
3342 * @param cls client handle
3343 * @param msg message sent by the client
3344 * @return #GNUNET_OK (always well-formed)
3345 */
3346static int
3347check_client_insert (void *cls,
3348 const struct GNUNET_CONSENSUS_ElementMessage *msg)
3349{
3350 return GNUNET_OK;
3351}
3352
3353
3354/**
3355 * Called when a client performs an insert operation.
3356 *
3357 * @param cls client handle
3358 * @param msg message sent by the client
3359 */
3360static void
3361handle_client_insert (void *cls,
3362 const struct GNUNET_CONSENSUS_ElementMessage *msg)
3363{
3364 struct ConsensusSession *session = cls;
3365 ssize_t element_size;
3366 struct GNUNET_SET_Handle *initial_set;
3367 struct ConsensusElement *ce;
3368
3369 if (GNUNET_YES == session->conclude_started)
3370 {
3371 GNUNET_break (0);
3372 GNUNET_SERVICE_client_drop (session->client);
3373 return;
3374 }
3375 element_size = ntohs (msg->header.size) - sizeof(*msg);
3376 ce = GNUNET_malloc (sizeof(struct ConsensusElement) + element_size);
3377 GNUNET_memcpy (&ce[1],
3378 &msg[1],
3379 element_size);
3380 ce->payload_type = msg->element_type;
3381
3382 {
3383 struct SetKey key = { SET_KIND_CURRENT, 0, 0 };
3384 struct SetEntry *entry;
3385
3386 entry = lookup_set (session,
3387 &key);
3388 GNUNET_assert (NULL != entry);
3389 initial_set = entry->h;
3390 }
3391
3392 session->num_client_insert_pending++;
3393
3394 {
3395 struct GNUNET_SET_Element element = {
3396 .element_type = GNUNET_BLOCK_TYPE_CONSENSUS_ELEMENT,
3397 .size = sizeof(struct ConsensusElement) + element_size,
3398 .data = ce,
3399 };
3400
3401 GNUNET_SET_add_element (initial_set,
3402 &element,
3403 NULL,
3404 NULL);
3405#ifdef GNUNET_EXTRA_LOGGING
3406 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3407 "P%u: element %s added\n",
3408 session->local_peer_idx,
3409 debug_str_element (&element));
3410#endif
3411 }
3412 GNUNET_free (ce);
3413 GNUNET_SERVICE_client_continue (session->client);
3414}
3415
3416
3417/**
3418 * Called when a client performs the conclude operation.
3419 *
3420 * @param cls client handle
3421 * @param message message sent by the client
3422 */
3423static void
3424handle_client_conclude (void *cls,
3425 const struct GNUNET_MessageHeader *message)
3426{
3427 struct ConsensusSession *session = cls;
3428
3429 if (GNUNET_YES == session->conclude_started)
3430 {
3431 /* conclude started twice */
3432 GNUNET_break (0);
3433 GNUNET_SERVICE_client_drop (session->client);
3434 return;
3435 }
3436 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3437 "conclude requested\n");
3438 session->conclude_started = GNUNET_YES;
3439 install_step_timeouts (session);
3440 run_ready_steps (session);
3441 GNUNET_SERVICE_client_continue (session->client);
3442}
3443
3444
3445/**
3446 * Called to clean up, after a shutdown has been requested.
3447 *
3448 * @param cls closure
3449 */
3450static void
3451shutdown_task (void *cls)
3452{
3453 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3454 "shutting down\n");
3455 GNUNET_STATISTICS_destroy (statistics,
3456 GNUNET_NO);
3457 statistics = NULL;
3458}
3459
3460
3461/**
3462 * Start processing consensus requests.
3463 *
3464 * @param cls closure
3465 * @param c configuration to use
3466 * @param service the initialized service
3467 */
3468static void
3469run (void *cls,
3470 const struct GNUNET_CONFIGURATION_Handle *c,
3471 struct GNUNET_SERVICE_Handle *service)
3472{
3473 cfg = c;
3474 if (GNUNET_OK !=
3475 GNUNET_CRYPTO_get_peer_identity (cfg,
3476 &my_peer))
3477 {
3478 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3479 "Could not retrieve host identity\n");
3480 GNUNET_SCHEDULER_shutdown ();
3481 return;
3482 }
3483 statistics = GNUNET_STATISTICS_create ("consensus",
3484 cfg);
3485 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
3486 NULL);
3487}
3488
3489
3490/**
3491 * Callback called when a client connects to the service.
3492 *
3493 * @param cls closure for the service
3494 * @param c the new client that connected to the service
3495 * @param mq the message queue used to send messages to the client
3496 * @return @a c
3497 */
3498static void *
3499client_connect_cb (void *cls,
3500 struct GNUNET_SERVICE_Client *c,
3501 struct GNUNET_MQ_Handle *mq)
3502{
3503 struct ConsensusSession *session = GNUNET_new (struct ConsensusSession);
3504
3505 session->client = c;
3506 session->client_mq = mq;
3507 GNUNET_CONTAINER_DLL_insert (sessions_head,
3508 sessions_tail,
3509 session);
3510 return session;
3511}
3512
3513
3514/**
3515 * Callback called when a client disconnected from the service
3516 *
3517 * @param cls closure for the service
3518 * @param c the client that disconnected
3519 * @param internal_cls should be equal to @a c
3520 */
3521static void
3522client_disconnect_cb (void *cls,
3523 struct GNUNET_SERVICE_Client *c,
3524 void *internal_cls)
3525{
3526 struct ConsensusSession *session = internal_cls;
3527
3528 if (NULL != session->set_listener)
3529 {
3530 GNUNET_SET_listen_cancel (session->set_listener);
3531 session->set_listener = NULL;
3532 }
3533 GNUNET_CONTAINER_DLL_remove (sessions_head,
3534 sessions_tail,
3535 session);
3536 while (session->set_handles_head)
3537 {
3538 struct SetHandle *sh = session->set_handles_head;
3539
3540 session->set_handles_head = sh->next;
3541 GNUNET_SET_destroy (sh->h);
3542 GNUNET_free (sh);
3543 }
3544 GNUNET_free (session);
3545}
3546
3547
3548/**
3549 * Define "main" method using service macro.
3550 */
3551GNUNET_SERVICE_MAIN (
3552 "consensus",
3553 GNUNET_SERVICE_OPTION_NONE,
3554 &run,
3555 &client_connect_cb,
3556 &client_disconnect_cb,
3557 NULL,
3558 GNUNET_MQ_hd_fixed_size (client_conclude,
3559 GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_CONCLUDE,
3560 struct GNUNET_MessageHeader,
3561 NULL),
3562 GNUNET_MQ_hd_var_size (client_insert,
3563 GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_INSERT,
3564 struct GNUNET_CONSENSUS_ElementMessage,
3565 NULL),
3566 GNUNET_MQ_hd_var_size (client_join,
3567 GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_JOIN,
3568 struct GNUNET_CONSENSUS_JoinMessage,
3569 NULL),
3570 GNUNET_MQ_handler_end ());
3571
3572/* end of gnunet-service-consensus.c */
diff --git a/src/contrib/service/consensus/meson.build b/src/contrib/service/consensus/meson.build
new file mode 100644
index 000000000..b66acf2b9
--- /dev/null
+++ b/src/contrib/service/consensus/meson.build
@@ -0,0 +1,67 @@
1libgnunetconsensus_src = ['consensus_api.c']
2
3gnunetserviceconsensus_src = ['gnunet-service-consensus.c']
4
5configure_file(input : 'consensus.conf.in',
6 output : 'consensus.conf',
7 configuration : cdata,
8 install: true,
9 install_dir: pkgcfgdir)
10
11# FIXME needs new seti/setu
12if get_option('monolith')
13 #foreach p : libgnunetconsensus_src + gnunetserviceconsensus_src
14 # gnunet_src += 'consensus/' + p
15 #endforeach
16 subdir_done()
17endif
18
19libgnunetconsensus = shared_library('gnunetconsensus',
20 libgnunetconsensus_src,
21 soversion: '0',
22 version: '0.0.0',
23 dependencies: libgnunetutil_dep,
24 include_directories: [incdir, configuration_inc],
25 install: true,
26 install_dir: get_option('libdir'))
27pkg.generate(libgnunetconsensus, url: 'https://www.gnunet.org',
28 description : 'Provides API for accessing the consensus service')
29libgnunetarm_dep = declare_dependency(link_with : libgnunetarm)
30libgnunetconsensus_dep = declare_dependency(link_with : libgnunetconsensus)
31
32shared_module('gnunet_plugin_block_consensus',
33 ['plugin_block_consensus.c'],
34 dependencies: [libgnunetutil_dep,
35 libgnunetblock_dep],
36 include_directories: [incdir, configuration_inc],
37 install:true,
38 install_dir: get_option('libdir')/'gnunet')
39
40executable ('gnunet-service-consensus',
41 gnunetserviceconsensus_src,
42 dependencies: [libgnunetconsensus_dep,
43 libgnunetutil_dep,
44 libgnunetset_dep,
45 libgnunetstatistics_dep],
46 include_directories: [incdir, configuration_inc],
47 install: true,
48 install_dir: get_option('libdir') / 'gnunet' / 'libexec')
49
50# FIXME do we need evil stuff and ENABLE_MALICIOUS?
51
52testconsensusapi = executable ('test_consensus_api',
53 ['test_consensus_api.c'],
54 dependencies: [libgnunetconsensus_dep,
55 libgnunetutil_dep,
56 libgnunettesting_dep],
57 include_directories: [incdir, configuration_inc],
58 install: false)
59
60configure_file(input : 'test_consensus.conf',
61 output : 'test_consensus.conf',
62 configuration : cdata,
63 install: false)
64
65test('test_consensus_api', testconsensusapi,
66 workdir: meson.current_build_dir(),
67 suite: 'consensus')
diff --git a/src/contrib/service/consensus/plugin_block_consensus.c b/src/contrib/service/consensus/plugin_block_consensus.c
new file mode 100644
index 000000000..241d8fc7b
--- /dev/null
+++ b/src/contrib/service/consensus/plugin_block_consensus.c
@@ -0,0 +1,222 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2017, 2021 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file consensus/plugin_block_consensus.c
23 * @brief consensus block, either nested block or marker
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "consensus_protocol.h"
29#include "gnunet_block_plugin.h"
30#include "gnunet_block_group_lib.h"
31
32
33/**
34 * Our closure.
35 */
36struct BlockContext
37{
38 /**
39 * Configuration to use.
40 */
41 const struct GNUNET_CONFIGURATION_Handle *cfg;
42
43 /**
44 * Lazily initialized block context.
45 */
46 struct GNUNET_BLOCK_Context *bc;
47};
48
49
50
51/**
52 * Function called to validate a query.
53 *
54 * @param cls closure
55 * @param type block type
56 * @param query original query (hash)
57 * @param xquery extrended query data (can be NULL, depending on type)
58 * @param xquery_size number of bytes in @a xquery
59 * @return #GNUNET_OK if the query is fine, #GNUNET_NO if not
60 */
61static enum GNUNET_GenericReturnValue
62block_plugin_consensus_check_query (void *cls,
63 enum GNUNET_BLOCK_Type type,
64 const struct GNUNET_HashCode *query,
65 const void *xquery,
66 size_t xquery_size)
67{
68 /* consensus does not use queries/DHT */
69 GNUNET_break (0);
70 return GNUNET_SYSERR;
71}
72
73
74/**
75 * Function called to validate a block for storage.
76 *
77 * @param cls closure
78 * @param type block type
79 * @param block block data to validate
80 * @param block_size number of bytes in @a block
81 * @return #GNUNET_OK if the block is fine, #GNUNET_NO if not
82 */
83static enum GNUNET_GenericReturnValue
84block_plugin_consensus_check_block (void *cls,
85 enum GNUNET_BLOCK_Type type,
86 const void *block,
87 size_t block_size)
88{
89 struct BlockContext *ctx = cls;
90 const struct ConsensusElement *ce = block;
91
92 if (block_size < sizeof(*ce))
93 {
94 GNUNET_break_op (0);
95 return GNUNET_NO;
96 }
97 if ( (0 != ce->marker) ||
98 (0 == ce->payload_type) )
99 return GNUNET_OK;
100 if (NULL == ctx->bc)
101 ctx->bc = GNUNET_BLOCK_context_create (ctx->cfg);
102 return GNUNET_BLOCK_check_block (ctx->bc,
103 ntohl (ce->payload_type),
104 &ce[1],
105 block_size - sizeof(*ce));
106}
107
108
109/**
110 * Function called to validate a reply to a request. Note that it is assumed
111 * that the reply has already been matched to the key (and signatures checked)
112 * as it would be done with the GetKeyFunction and the
113 * BlockEvaluationFunction.
114 *
115 * @param cls closure
116 * @param type block type
117 * @param group which block group to use for evaluation
118 * @param query original query (hash)
119 * @param xquery extrended query data (can be NULL, depending on type)
120 * @param xquery_size number of bytes in @a xquery
121 * @param reply_block response to validate
122 * @param reply_block_size number of bytes in @a reply_block
123 * @return characterization of result
124 */
125static enum GNUNET_BLOCK_ReplyEvaluationResult
126block_plugin_consensus_check_reply (
127 void *cls,
128 enum GNUNET_BLOCK_Type type,
129 struct GNUNET_BLOCK_Group *group,
130 const struct GNUNET_HashCode *query,
131 const void *xquery,
132 size_t xquery_size,
133 const void *reply_block,
134 size_t reply_block_size)
135{
136 struct BlockContext *ctx = cls;
137 const struct ConsensusElement *ce = reply_block;
138
139 GNUNET_assert (reply_block_size >= sizeof(struct ConsensusElement));
140 if ( (0 != ce->marker) ||
141 (0 == ce->payload_type) )
142 return GNUNET_BLOCK_REPLY_OK_MORE;
143 if (NULL == ctx->bc)
144 ctx->bc = GNUNET_BLOCK_context_create (ctx->cfg);
145 return GNUNET_BLOCK_check_reply (ctx->bc,
146 ntohl (ce->payload_type),
147 group,
148 query,
149 xquery,
150 xquery_size,
151 &ce[1],
152 reply_block_size - sizeof(*ce));
153}
154
155
156/**
157 * Function called to obtain the key for a block.
158 *
159 * @param cls closure
160 * @param type block type
161 * @param block block to get the key for
162 * @param block_size number of bytes in block
163 * @param key set to the key (query) for the given block
164 * @return #GNUNET_OK on success, #GNUNET_SYSERR if type not supported
165 * (or if extracting a key from a block of this type does not work)
166 */
167static enum GNUNET_GenericReturnValue
168block_plugin_consensus_get_key (void *cls,
169 enum GNUNET_BLOCK_Type type,
170 const void *block,
171 size_t block_size,
172 struct GNUNET_HashCode *key)
173{
174 return GNUNET_SYSERR;
175}
176
177
178/**
179 * Entry point for the plugin.
180 */
181void *
182libgnunet_plugin_block_consensus_init (void *cls)
183{
184 static const enum GNUNET_BLOCK_Type types[] = {
185 GNUNET_BLOCK_TYPE_CONSENSUS_ELEMENT,
186 GNUNET_BLOCK_TYPE_ANY /* end of list */
187 };
188 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
189 struct BlockContext *ctx;
190 struct GNUNET_BLOCK_PluginFunctions *api;
191
192 ctx = GNUNET_new (struct BlockContext);
193 ctx->cfg = cfg;
194 api = GNUNET_new (struct GNUNET_BLOCK_PluginFunctions);
195 api->cls = ctx;
196 api->get_key = &block_plugin_consensus_get_key;
197 api->check_query = &block_plugin_consensus_check_query;
198 api->check_block = &block_plugin_consensus_check_block;
199 api->check_reply = &block_plugin_consensus_check_reply;
200 api->types = types;
201 return api;
202}
203
204
205/**
206 * Exit point from the plugin.
207 */
208void *
209libgnunet_plugin_block_consensus_done (void *cls)
210{
211 struct GNUNET_BLOCK_PluginFunctions *api = cls;
212 struct BlockContext *ctx = api->cls;
213
214 if (NULL != ctx->bc)
215 GNUNET_BLOCK_context_destroy (ctx->bc);
216 GNUNET_free (ctx);
217 GNUNET_free (api);
218 return NULL;
219}
220
221
222/* end of plugin_block_consensus.c */
diff --git a/src/contrib/service/consensus/test_consensus.conf b/src/contrib/service/consensus/test_consensus.conf
new file mode 100644
index 000000000..df7fb6861
--- /dev/null
+++ b/src/contrib/service/consensus/test_consensus.conf
@@ -0,0 +1,87 @@
1[PATHS]
2GNUNET_TEST_HOME = $GNUNET_TMP/test-consensus/
3
4[consensus]
5#OPTIONS = -L INFO
6BINARY = gnunet-service-consensus
7
8#PREFIX = valgrind
9
10#EVIL_SPEC = 0;cram-all;noreplace;5
11#EVIL_SPEC = 0;cram;5/1;cram;5
12#EVIL_SPEC = 0;cram;5/1;cram;5/2;cram;5
13#EVIL_SPEC = 0;cram;5/1;cram;5/2;cram;5/3;cram;5
14
15
16[arm]
17RESOURCE_DIAGNOSTICS = resource.log.${PEERID:-master}
18
19[core]
20IMMEDIATE_START = YES
21
22[revocation]
23IMMEDIATE_START = NO
24
25[fs]
26IMMEDIATE_START = NO
27
28[gns]
29IMMEDIATE_START = NO
30
31[zonemaster]
32IMMEDIATE_START = NO
33
34[hostlist]
35IMMEDIATE_START = NO
36
37[cadet]
38#PREFIX = valgrind
39
40[transport]
41PLUGINS = unix
42OPTIONS = -LERROR
43
44[set]
45#OPTIONS = -L INFO
46#PREFIX = valgrind --leak-check=full
47#PREFIX = valgrind
48
49[testbed]
50OVERLAY_TOPOLOGY = CLIQUE
51MAX_PARALLEL_OPERATIONS = 1000
52MAX_PARALLEL_TOPOLOGY_CONFIG_OPERATIONS = 100
53OPERATION_TIMEOUT = 60 s
54MAX_OPEN_FDS = 4096
55
56[hostlist]
57START_ON_DEMAND = NO
58
59[fs]
60START_ON_DEMAND = NO
61
62[revocation]
63START_ON_DEMAND = NO
64
65[nat]
66# Use addresses from the local network interfaces (including loopback, but also others)
67USE_LOCALADDR = YES
68
69# Disable IPv6 support
70DISABLEV6 = NO
71
72# Do we use addresses from localhost address ranges? (::1, 127.0.0.0/8)
73RETURN_LOCAL_ADDRESSES = YES
74
75[nse]
76WORKBITS=0
77
78[ats]
79WAN_QUOTA_IN = unlimited
80WAN_QUOTA_OUT = unlimited
81
82[core]
83USE_EPHEMERAL_KEYS = NO
84
85[rps]
86START_ON_DEMAND = NO
87IMMEDIATE_START = NO
diff --git a/src/contrib/service/consensus/test_consensus_api.c b/src/contrib/service/consensus/test_consensus_api.c
new file mode 100644
index 000000000..235a67484
--- /dev/null
+++ b/src/contrib/service/consensus/test_consensus_api.c
@@ -0,0 +1,120 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file consensus/test_consensus_api.c
23 * @brief testcase for consensus_api.c
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_consensus_service.h"
28#include "gnunet_testing_lib.h"
29
30
31static struct GNUNET_CONSENSUS_Handle *consensus;
32
33static struct GNUNET_HashCode session_id;
34
35static unsigned int elements_received;
36
37
38static void
39conclude_done (void *cls)
40{
41 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "conclude over\n");
42 if (2 != elements_received)
43 GNUNET_assert (0);
44 GNUNET_SCHEDULER_shutdown ();
45}
46
47
48static void
49on_new_element (void *cls,
50 const struct GNUNET_SET_Element *element)
51{
52 elements_received++;
53}
54
55
56static void
57insert_done (void *cls, int success)
58{
59 /* make sure cb is only called once */
60 static int called = GNUNET_NO;
61
62 GNUNET_assert (GNUNET_NO == called);
63 called = GNUNET_YES;
64 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "insert done\n");
65 GNUNET_CONSENSUS_conclude (consensus, &conclude_done, NULL);
66}
67
68
69/**
70 * Signature of the main function of a task.
71 *
72 * @param cls closure
73 */
74static void
75on_shutdown (void *cls)
76{
77 if (NULL != consensus)
78 {
79 GNUNET_CONSENSUS_destroy (consensus);
80 consensus = NULL;
81 }
82}
83
84
85static void
86run (void *cls,
87 const struct GNUNET_CONFIGURATION_Handle *cfg,
88 struct GNUNET_TESTING_Peer *peer)
89{
90 char *str = "foo";
91
92 struct GNUNET_SET_Element el1 = { 4, 0, "foo" };
93 struct GNUNET_SET_Element el2 = { 5, 0, "quux" };
94
95 GNUNET_log_setup ("test_consensus_api",
96 "INFO",
97 NULL);
98 GNUNET_SCHEDULER_add_shutdown (&on_shutdown, NULL);
99
100 GNUNET_CRYPTO_hash (str, strlen (str), &session_id);
101 consensus = GNUNET_CONSENSUS_create (cfg, 0, NULL, &session_id,
102 GNUNET_TIME_relative_to_absolute (
103 GNUNET_TIME_UNIT_SECONDS),
104 GNUNET_TIME_relative_to_absolute (
105 GNUNET_TIME_UNIT_MINUTES),
106 on_new_element, &consensus);
107 GNUNET_assert (consensus != NULL);
108
109 GNUNET_CONSENSUS_insert (consensus, &el1, NULL, &consensus);
110 GNUNET_CONSENSUS_insert (consensus, &el2, &insert_done, &consensus);
111}
112
113
114int
115main (int argc, char **argv)
116{
117 return GNUNET_TESTING_peer_run ("test_consensus_api",
118 "test_consensus.conf",
119 &run, NULL);
120}
diff --git a/src/contrib/service/secretsharing/.gitignore b/src/contrib/service/secretsharing/.gitignore
new file mode 100644
index 000000000..fe9db53a4
--- /dev/null
+++ b/src/contrib/service/secretsharing/.gitignore
@@ -0,0 +1,3 @@
1gnunet-service-secretsharing
2gnunet-secretsharing-profiler
3test_secretsharing_api
diff --git a/src/contrib/service/secretsharing/Makefile.am b/src/contrib/service/secretsharing/Makefile.am
new file mode 100644
index 000000000..afc42309c
--- /dev/null
+++ b/src/contrib/service/secretsharing/Makefile.am
@@ -0,0 +1,74 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4pkgcfgdir= $(pkgdatadir)/config.d/
5
6libexecdir= $(pkglibdir)/libexec/
7
8pkgcfg_DATA = \
9 secretsharing.conf
10
11if USE_COVERAGE
12 AM_CFLAGS = -fprofile-arcs -ftest-coverage
13endif
14
15
16libexec_PROGRAMS = \
17 gnunet-service-secretsharing
18
19#noinst_PROGRAMS = \
20# gnunet-secretsharing-profiler
21
22lib_LTLIBRARIES = \
23 libgnunetsecretsharing.la
24
25
26# TNG
27#gnunet_secretsharing_profiler_SOURCES = \
28# gnunet-secretsharing-profiler.c
29#gnunet_secretsharing_profiler_LDADD = \
30# libgnunetsecretsharing.la \
31# $(top_builddir)/src/service/testing/libgnunettesting.la \
32# $(top_builddir)/src/testbed/libgnunettestbed.la \
33# $(top_builddir)/src/lib/util/libgnunetutil.la \
34# $(GN_LIBINTL)
35
36gnunet_service_secretsharing_SOURCES = \
37 gnunet-service-secretsharing.c \
38 secretsharing_common.c \
39 secretsharing_protocol.h
40gnunet_service_secretsharing_CFLAGS = $(AM_CFLAGS)
41gnunet_service_secretsharing_LDADD = \
42 $(top_builddir)/src/lib/util/libgnunetutil.la \
43 $(top_builddir)/src/contrib/service/consensus/libgnunetconsensus.la \
44 $(LIBGCRYPT_LIBS) \
45 $(GN_LIBINTL)
46
47libgnunetsecretsharing_la_SOURCES = \
48 secretsharing_api.c \
49 secretsharing_common.c \
50 secretsharing.h
51libgnunetsecretsharing_la_LIBADD = \
52 $(top_builddir)/src/lib/util/libgnunetutil.la \
53 $(LIBGCRYPT_LIBS) \
54 $(LTLIBINTL)
55libgnunetsecretsharing_la_LDFLAGS = \
56 $(GN_LIB_LDFLAGS)
57
58check_PROGRAMS = \
59 # test_secretsharing_api
60
61if ENABLE_TEST_RUN
62AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
63TESTS = $(check_PROGRAMS)
64endif
65
66test_secretsharing_api_SOURCES = \
67 test_secretsharing_api.c
68test_secretsharing_api_LDADD = \
69 libgnunetsecretsharing.la \
70 $(top_builddir)/src/service/testing/libgnunettesting.la \
71 $(top_builddir)/src/lib/util/libgnunetutil.la
72
73EXTRA_DIST = \
74 test_secretsharing.conf
diff --git a/src/contrib/service/secretsharing/gnunet-secretsharing-profiler.c b/src/contrib/service/secretsharing/gnunet-secretsharing-profiler.c
new file mode 100644
index 000000000..432d6da89
--- /dev/null
+++ b/src/contrib/service/secretsharing/gnunet-secretsharing-profiler.c
@@ -0,0 +1,662 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file secretsharing/gnunet-secretsharing-profiler.c
23 * @brief profiling tool for distributed key generation and decryption
24 * @author Florian Dold
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_secretsharing_service.h"
29#include "gnunet_testbed_service.h"
30
31/**
32 * How many peers should participate in the key generation?
33 */
34static unsigned int num_peers = 3;
35
36/**
37 * What should the threshold for then key be?
38 */
39static unsigned int threshold = 2;
40
41/**
42 * Should we try to decrypt a value after the key generation?
43 */
44static int decrypt = GNUNET_NO;
45
46/**
47 * When would we like to see the operation finished?
48 */
49static struct GNUNET_TIME_Relative timeout;
50
51/**
52 * When should dkg communication start?
53 */
54static struct GNUNET_TIME_Relative delay;
55
56/**
57 * Handles for secretsharing sessions.
58 */
59static struct GNUNET_SECRETSHARING_Session **session_handles;
60
61static struct GNUNET_SECRETSHARING_DecryptionHandle **decrypt_handles;
62
63/**
64 * Shares we got from the distributed key generation.
65 */
66static struct GNUNET_SECRETSHARING_Share **shares;
67
68static struct GNUNET_SECRETSHARING_PublicKey common_pubkey;
69
70
71static unsigned int num_connected_sessions;
72
73static unsigned int num_connected_decrypt;
74
75/**
76 * Handles to the running peers.
77 * When peers[i] is NULL, the i-th peer has stopped.
78 */
79static struct GNUNET_TESTBED_Peer **peers;
80
81static struct GNUNET_PeerIdentity *peer_ids;
82
83static unsigned int num_retrieved_peer_ids;
84
85static unsigned int num_generated;
86
87static unsigned int num_decrypted;
88
89static struct GNUNET_HashCode session_id;
90
91static unsigned int verbose;
92
93static struct GNUNET_SECRETSHARING_Plaintext reference_plaintext;
94
95static struct GNUNET_SECRETSHARING_Ciphertext ciphertext;
96
97static struct GNUNET_TIME_Absolute dkg_start;
98
99static struct GNUNET_TIME_Absolute dkg_deadline;
100
101
102static struct GNUNET_TIME_Absolute decrypt_start;
103
104static struct GNUNET_TIME_Absolute decrypt_deadline;
105
106/**
107 * Connect operations, one for every peer.
108 */
109static struct GNUNET_TESTBED_Operation **connect_ops;
110
111/**
112 * Are we performing a shutdown right now?
113 */
114static int in_shutdown;
115
116
117/**
118 * Signature of the event handler function called by the
119 * respective event controller.
120 *
121 * @param cls closure
122 * @param event information about the event
123 */
124static void
125controller_cb (void *cls,
126 const struct GNUNET_TESTBED_EventInformation *event)
127{
128 GNUNET_assert (0);
129}
130
131
132/**
133 * Callback to be called when a service connect operation is completed
134 *
135 * @param cls the callback closure from functions generating an operation
136 * @param op the operation that has been finished
137 * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
138 * @param emsg error message in case the operation has failed; will be NULL if
139 * operation has executed successfully.
140 */
141static void
142session_connect_complete (void *cls,
143 struct GNUNET_TESTBED_Operation *op,
144 void *ca_result,
145 const char *emsg)
146{
147 if (NULL != emsg)
148 {
149 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
150 "testbed connect emsg: %s\n",
151 emsg);
152 GNUNET_assert (0);
153 }
154
155 num_connected_sessions++;
156
157 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
158 "dkg: session connect complete\n");
159
160 if (num_connected_sessions == num_peers)
161 {
162 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
163 "dkg: all peers connected\n");
164 }
165}
166
167
168/**
169 * Callback to be called when a service connect operation is completed
170 *
171 * @param cls the callback closure from functions generating an operation
172 * @param op the operation that has been finished
173 * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
174 * @param emsg error message in case the operation has failed; will be NULL if
175 * operation has executed successfully.
176 */
177static void
178decrypt_connect_complete (void *cls,
179 struct GNUNET_TESTBED_Operation *op,
180 void *ca_result,
181 const char *emsg)
182{
183 if (NULL != emsg)
184 {
185 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
186 "testbed connect emsg: %s\n",
187 emsg);
188 GNUNET_assert (0);
189 }
190
191 num_connected_decrypt++;
192
193 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
194 "decrypt: session connect complete\n");
195
196 if (num_connected_decrypt == num_peers)
197 {
198 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
199 "decrypt: all peers connected\n");
200 }
201}
202
203
204/**
205 * Called when a decryption has succeeded.
206 *
207 * @param cls Plaintext
208 * @param plaintext Plaintext
209 */
210static void
211decrypt_cb (void *cls,
212 const struct GNUNET_SECRETSHARING_Plaintext *plaintext)
213{
214 struct GNUNET_SECRETSHARING_DecryptionHandle **dhp = cls;
215 unsigned int n = dhp - decrypt_handles;
216
217 num_decrypted++;
218
219 *dhp = NULL;
220
221 // we should still be connected if this is called
222 GNUNET_assert (NULL != connect_ops[n]);
223
224 GNUNET_TESTBED_operation_done (connect_ops[n]);
225
226 if (NULL == plaintext)
227 {
228 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "decrypt failed for peer %u\n", n);
229 return;
230 }
231 else if (0 == GNUNET_memcmp (&reference_plaintext, plaintext))
232 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
233 "decrypt got correct result for peer %u\n", n);
234 else
235 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
236 "decrypt got wrong result for peer %u\n", n);
237
238 if (num_decrypted == num_peers)
239 {
240 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "every peer decrypted\n");
241 GNUNET_SCHEDULER_shutdown ();
242 }
243
244 *dhp = NULL;
245}
246
247
248/**
249 * Adapter function called to establish a connection to
250 * a service.
251 *
252 * @param cls closure
253 * @param cfg configuration of the peer to connect to; will be available until
254 * GNUNET_TESTBED_operation_done() is called on the operation returned
255 * from GNUNET_TESTBED_service_connect()
256 * @return service handle to return in 'op_result', NULL on error
257 */
258static void *
259decrypt_connect_adapter (void *cls,
260 const struct GNUNET_CONFIGURATION_Handle *cfg)
261{
262 struct GNUNET_SECRETSHARING_DecryptionHandle **hp = cls;
263 unsigned int n = hp - decrypt_handles;
264
265 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
266 "decrypt connect adapter, %d peers\n",
267 num_peers);
268 *hp = GNUNET_SECRETSHARING_decrypt (cfg, shares[n], &ciphertext,
269 decrypt_start, decrypt_deadline,
270 decrypt_cb,
271 hp);
272
273 return *hp;
274}
275
276
277/**
278 * Adapter function called to destroy a connection to
279 * a service.
280 *
281 * @param cls closure
282 * @param op_result service handle returned from the connect adapter
283 */
284static void
285decrypt_disconnect_adapter (void *cls, void *op_result)
286{
287 struct GNUNET_SECRETSHARING_DecryptionHandle **dh = cls;
288 unsigned int n = dh - decrypt_handles;
289
290 GNUNET_assert (*dh == decrypt_handles[n]);
291
292 if (NULL != *dh)
293 {
294 GNUNET_SECRETSHARING_decrypt_cancel (*dh);
295 *dh = NULL;
296 }
297
298 GNUNET_assert (NULL != connect_ops[n]);
299 connect_ops[n] = NULL;
300}
301
302
303static void
304secret_ready_cb (void *cls,
305 struct GNUNET_SECRETSHARING_Share *my_share,
306 struct GNUNET_SECRETSHARING_PublicKey *public_key,
307 unsigned int num_ready_peers,
308 const struct GNUNET_PeerIdentity *ready_peers)
309{
310 struct GNUNET_SECRETSHARING_Session **sp = cls;
311 unsigned int n = sp - session_handles;
312 char pubkey_str[1024];
313 char *ret;
314
315 num_generated++;
316 *sp = NULL;
317 shares[n] = my_share;
318 if (NULL == my_share)
319 {
320 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "key generation failed for peer #%u\n",
321 n);
322 }
323 else
324 {
325 ret = GNUNET_STRINGS_data_to_string (public_key, sizeof *public_key,
326 pubkey_str, 1024);
327 GNUNET_assert (NULL != ret);
328 *ret = '\0';
329 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
330 "key generation successful for peer #%u, pubkey %s\n", n,
331 pubkey_str);
332
333 /* we're the first to get the key -> store it */
334 if (num_generated == 1)
335 {
336 common_pubkey = *public_key;
337 }
338 else if (0 != GNUNET_memcmp (public_key, &common_pubkey))
339 {
340 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
341 "generated public keys do not match\n");
342 GNUNET_SCHEDULER_shutdown ();
343 return;
344 }
345 }
346
347 // we should still be connected
348 GNUNET_assert (NULL != connect_ops[n]);
349
350 // disconnect from the service, will call the disconnect callback
351 GNUNET_TESTBED_operation_done (connect_ops[n]);
352}
353
354
355/**
356 * Adapter function called to establish a connection to
357 * a service.
358 *
359 * @param cls closure
360 * @param cfg configuration of the peer to connect to; will be available until
361 * GNUNET_TESTBED_operation_done() is called on the operation returned
362 * from GNUNET_TESTBED_service_connect()
363 * @return service handle to return in 'op_result', NULL on error
364 */
365static void *
366session_connect_adapter (void *cls,
367 const struct GNUNET_CONFIGURATION_Handle *cfg)
368{
369 struct GNUNET_SECRETSHARING_Session **sp = cls;
370
371 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
372 "connect adapter, %d peers\n",
373 num_peers);
374 *sp = GNUNET_SECRETSHARING_create_session (cfg,
375 num_peers,
376 peer_ids,
377 &session_id,
378 dkg_start,
379 dkg_deadline,
380 threshold,
381 &secret_ready_cb, sp);
382 return *sp;
383}
384
385
386/**
387 * Adapter function called to destroy a connection to
388 * a service.
389 *
390 * @param cls closure
391 * @param op_result service handle returned from the connect adapter
392 */
393static void
394session_disconnect_adapter (void *cls, void *op_result)
395{
396 struct GNUNET_SECRETSHARING_Session **sp = cls;
397 unsigned int n = (sp - session_handles);
398
399 GNUNET_assert (*sp == session_handles[n]);
400
401 if (NULL != *sp)
402 {
403 GNUNET_SECRETSHARING_session_destroy (*sp);
404 *sp = NULL;
405 }
406
407 GNUNET_assert (NULL != connect_ops[n]);
408 connect_ops[n] = NULL;
409
410 if (GNUNET_YES == in_shutdown)
411 return;
412
413 // all peers received their secret
414 if (num_generated == num_peers)
415 {
416 int i;
417
418 // only do decryption if requested by the user
419 if (GNUNET_NO == decrypt)
420 {
421 GNUNET_SCHEDULER_shutdown ();
422 return;
423 }
424
425 decrypt_start = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
426 delay);
427 decrypt_deadline = GNUNET_TIME_absolute_add (decrypt_start, timeout);
428
429 // compute g^42 as the plaintext which we will decrypt and then
430 // cooperatively decrypt
431 GNUNET_SECRETSHARING_plaintext_generate_i (&reference_plaintext, 42);
432 GNUNET_SECRETSHARING_encrypt (&common_pubkey, &reference_plaintext,
433 &ciphertext);
434
435 for (i = 0; i < num_peers; i++)
436 connect_ops[i] =
437 GNUNET_TESTBED_service_connect (NULL, peers[i], "secretsharing",
438 &decrypt_connect_complete, NULL,
439 &decrypt_connect_adapter,
440 &decrypt_disconnect_adapter,
441 &decrypt_handles[i]);
442 }
443}
444
445
446/**
447 * Callback to be called when the requested peer information is available
448 *
449 * @param cb_cls the closure from GNUNET_TESTBED_peer_get_information()
450 * @param op the operation this callback corresponds to
451 * @param pinfo the result; will be NULL if the operation has failed
452 * @param emsg error message if the operation has failed; will be NULL if the
453 * operation is successful
454 */
455static void
456peer_info_cb (void *cb_cls,
457 struct GNUNET_TESTBED_Operation *op,
458 const struct GNUNET_TESTBED_PeerInformation *pinfo,
459 const char *emsg)
460{
461 struct GNUNET_PeerIdentity *p;
462 int i;
463
464 GNUNET_assert (NULL == emsg);
465
466 p = (struct GNUNET_PeerIdentity *) cb_cls;
467
468 if (pinfo->pit == GNUNET_TESTBED_PIT_IDENTITY)
469 {
470 *p = *pinfo->result.id;
471 num_retrieved_peer_ids++;
472 if (num_retrieved_peer_ids == num_peers)
473 for (i = 0; i < num_peers; i++)
474 connect_ops[i] =
475 GNUNET_TESTBED_service_connect (NULL, peers[i], "secretsharing",
476 session_connect_complete, NULL,
477 session_connect_adapter,
478 session_disconnect_adapter,
479 &session_handles[i]);
480 }
481 else
482 {
483 GNUNET_assert (0);
484 }
485
486 GNUNET_TESTBED_operation_done (op);
487}
488
489
490/**
491 * Signature of the main function of a task.
492 *
493 * @param cls closure
494 */
495static void
496handle_shutdown (void *cls)
497{
498 in_shutdown = GNUNET_YES;
499
500 if (NULL != connect_ops)
501 {
502 unsigned int i;
503 for (i = 0; i < num_peers; i++)
504 if (NULL != connect_ops[i])
505 {
506 // the disconnect callback will set the op to NULL
507 GNUNET_TESTBED_operation_done (connect_ops[i]);
508 }
509 GNUNET_free (connect_ops);
510 }
511
512 // killing the testbed operation will take care of remaining
513 // service handles in the disconnect callback
514}
515
516
517/**
518 * Signature of a main function for a testcase.
519 *
520 * @param cls closure
521 * @param h the run handle
522 * @param num_peers number of peers in 'peers'
523 * @param started_peers handle to peers run in the testbed. NULL upon timeout (see
524 * GNUNET_TESTBED_test_run()).
525 * @param links_succeeded the number of overlay link connection attempts that
526 * succeeded
527 * @param links_failed the number of overlay link connection attempts that
528 * failed
529 */
530static void
531test_master (void *cls,
532 struct GNUNET_TESTBED_RunHandle *h,
533 unsigned int num_peers,
534 struct GNUNET_TESTBED_Peer **started_peers,
535 unsigned int links_succeeded,
536 unsigned int links_failed)
537{
538 int i;
539
540 GNUNET_log_setup ("gnunet-secretsharing-profiler", "INFO", NULL);
541
542 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "test master\n");
543
544 GNUNET_SCHEDULER_add_shutdown (&handle_shutdown, NULL);
545
546 peers = started_peers;
547
548 peer_ids = GNUNET_malloc (num_peers * sizeof(struct GNUNET_PeerIdentity));
549
550 session_handles = GNUNET_new_array (num_peers, struct
551 GNUNET_SECRETSHARING_Session *);
552 decrypt_handles = GNUNET_new_array (num_peers, struct
553 GNUNET_SECRETSHARING_DecryptionHandle *);
554 connect_ops = GNUNET_new_array (num_peers, struct GNUNET_TESTBED_Operation *);
555 shares = GNUNET_new_array (num_peers, struct GNUNET_SECRETSHARING_Share *);
556
557 for (i = 0; i < num_peers; i++)
558 {
559 // we do not store the returned operation, as peer_info_cb
560 // will receive it as a parameter and call GNUNET_TESTBED_operation_done.
561 GNUNET_TESTBED_peer_get_information (peers[i],
562 GNUNET_TESTBED_PIT_IDENTITY,
563 peer_info_cb,
564 &peer_ids[i]);
565 }
566}
567
568
569static void
570run (void *cls, char *const *args, const char *cfgfile,
571 const struct GNUNET_CONFIGURATION_Handle *cfg)
572{
573 static char *session_str = "gnunet-secretsharing/test";
574 char *topology;
575 int topology_cmp_result;
576
577 dkg_start = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), delay);
578 dkg_deadline = GNUNET_TIME_absolute_add (dkg_start, timeout);
579
580 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "testbed",
581 "OVERLAY_TOPOLOGY",
582 &topology))
583 {
584 fprintf (stderr,
585 "'OVERLAY_TOPOLOGY' not found in 'testbed' config section, "
586 "seems like you passed the wrong configuration file\n");
587 return;
588 }
589
590 topology_cmp_result = strcasecmp (topology, "NONE");
591 GNUNET_free (topology);
592
593 if (0 == topology_cmp_result)
594 {
595 fprintf (stderr,
596 "'OVERLAY_TOPOLOGY' set to 'NONE', "
597 "seems like you passed the wrong configuration file\n");
598 return;
599 }
600
601 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
602 "running gnunet-secretsharing-profiler\n");
603
604 GNUNET_CRYPTO_hash (session_str, strlen (session_str), &session_id);
605
606 (void) GNUNET_TESTBED_test_run ("gnunet-secretsharing-profiler",
607 cfgfile,
608 num_peers,
609 0,
610 controller_cb,
611 NULL,
612 test_master,
613 NULL);
614}
615
616
617int
618main (int argc, char **argv)
619{
620 struct GNUNET_GETOPT_CommandLineOption options[] = {
621 GNUNET_GETOPT_option_uint ('n',
622 "num-peers",
623 NULL,
624 gettext_noop ("number of peers in consensus"),
625 &num_peers),
626
627 GNUNET_GETOPT_option_relative_time ('D',
628 "delay",
629 NULL,
630 gettext_noop ("dkg start delay"),
631 &delay),
632
633 GNUNET_GETOPT_option_relative_time ('t',
634 "timeout",
635 NULL,
636 gettext_noop ("dkg timeout"),
637 &timeout),
638
639 GNUNET_GETOPT_option_uint ('k',
640 "threshold",
641 NULL,
642 gettext_noop ("threshold"),
643 &threshold),
644
645 GNUNET_GETOPT_option_flag ('d',
646 "descrypt",
647 gettext_noop ("also profile decryption"),
648 &decrypt),
649
650
651 GNUNET_GETOPT_option_verbose (&verbose),
652
653 GNUNET_GETOPT_OPTION_END
654 };
655
656 delay = GNUNET_TIME_UNIT_ZERO;
657 timeout = GNUNET_TIME_UNIT_MINUTES;
658 GNUNET_PROGRAM_run2 (argc, argv, "gnunet-secretsharing-profiler",
659 "help",
660 options, &run, NULL, GNUNET_YES);
661 return 0;
662}
diff --git a/src/contrib/service/secretsharing/gnunet-service-secretsharing.c b/src/contrib/service/secretsharing/gnunet-service-secretsharing.c
new file mode 100644
index 000000000..84338bd11
--- /dev/null
+++ b/src/contrib/service/secretsharing/gnunet-service-secretsharing.c
@@ -0,0 +1,2414 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file secretsharing/gnunet-service-secretsharing.c
23 * @brief secret sharing service
24 * @author Florian Dold
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_time_lib.h"
29#include "gnunet_signatures.h"
30#include "gnunet_consensus_service.h"
31#include "secretsharing.h"
32#include "secretsharing_protocol.h"
33#include <gcrypt.h>
34
35
36#define EXTRA_CHECKS 1
37
38
39/**
40 * Info about a peer in a key generation session.
41 */
42struct KeygenPeerInfo
43{
44 /**
45 * Peer identity of the peer.
46 */
47 struct GNUNET_PeerIdentity peer;
48
49 /**
50 * The peer's paillier public key.
51 * Freshly generated for each keygen session.
52 */
53 struct GNUNET_CRYPTO_PaillierPublicKey paillier_public_key;
54
55 /**
56 * The peer's commitment to its presecret.
57 */
58 gcry_mpi_t presecret_commitment;
59
60 /**
61 * Commitment to the preshare that is
62 * intended for our peer.
63 */
64 gcry_mpi_t preshare_commitment;
65
66 /**
67 * Sigma (exponentiated share) for this peer.
68 */
69 gcry_mpi_t sigma;
70
71 /**
72 * Did we successfully receive the round1 element
73 * of the peer?
74 */
75 int round1_valid;
76
77 /**
78 * Did we successfully receive the round2 element
79 * of the peer?
80 */
81 int round2_valid;
82};
83
84
85/**
86 * Information about a peer in a decrypt session.
87 */
88struct DecryptPeerInfo
89{
90 /**
91 * Identity of the peer.
92 */
93 struct GNUNET_PeerIdentity peer;
94
95 /**
96 * Original index in the key generation round.
97 * Necessary for computing the lagrange coefficients.
98 */
99 unsigned int original_index;
100
101 /**
102 * Set to the partial decryption of
103 * this peer, or NULL if we did not
104 * receive a partial decryption from this
105 * peer or the zero knowledge proof failed.
106 */
107 gcry_mpi_t partial_decryption;
108};
109
110
111/**
112 * State we keep per client.
113 */
114struct ClientState;
115
116
117/**
118 * Session to establish a threshold-shared secret.
119 */
120struct KeygenSession
121{
122 /**
123 * Current consensus, used for both DKG rounds.
124 */
125 struct GNUNET_CONSENSUS_Handle *consensus;
126
127 /**
128 * Which client is this for?
129 */
130 struct ClientState *cs;
131
132 /**
133 * Randomly generated coefficients of the polynomial for sharing our
134 * pre-secret, where 'preshares[0]' is our pre-secret. Contains 'threshold'
135 * elements, thus represents a polynomial of degree 'threshold-1', which can
136 * be interpolated with 'threshold' data points.
137 *
138 * The pre-secret-shares 'i=1,...,num_peers' are given by evaluating this
139 * polyomial at 'i' for share i.
140 */
141 gcry_mpi_t *presecret_polynomial;
142
143 /**
144 * Minimum number of shares required to restore the secret.
145 * Also the number of coefficients for the polynomial representing
146 * the sharing. Obviously, the polynomial then has degree threshold-1.
147 */
148 unsigned int threshold;
149
150 /**
151 * Total number of peers.
152 */
153 unsigned int num_peers;
154
155 /**
156 * Index of the local peer.
157 */
158 unsigned int local_peer;
159
160 /**
161 * Information about all participating peers.
162 * Array of size 'num_peers'.
163 */
164 struct KeygenPeerInfo *info;
165
166 /**
167 * List of all peers involved in the secret sharing session.
168 */
169 struct GNUNET_PeerIdentity *peers;
170
171 /**
172 * Identifier for this session.
173 */
174 struct GNUNET_HashCode session_id;
175
176 /**
177 * Paillier private key of our peer.
178 */
179 struct GNUNET_CRYPTO_PaillierPrivateKey paillier_private_key;
180
181 /**
182 * When would we like the key to be established?
183 */
184 struct GNUNET_TIME_Absolute deadline;
185
186 /**
187 * When does the DKG start? Necessary to compute fractions of the
188 * operation's desired time interval.
189 */
190 struct GNUNET_TIME_Absolute start_time;
191
192 /**
193 * Index of the local peer in the ordered list
194 * of peers in the session.
195 */
196 unsigned int local_peer_idx;
197
198 /**
199 * Share of our peer. Once preshares from other peers are received, they
200 * will be added to 'my'share.
201 */
202 gcry_mpi_t my_share;
203
204 /**
205 * Public key, will be updated when a round2 element arrives.
206 */
207 gcry_mpi_t public_key;
208};
209
210
211/**
212 * Session to cooperatively decrypt a value.
213 */
214struct DecryptSession
215{
216 /**
217 * Handle to the consensus over partial decryptions.
218 */
219 struct GNUNET_CONSENSUS_Handle *consensus;
220
221 /**
222 * Which client is this for?
223 */
224 struct ClientState *cs;
225
226 /**
227 * When should we start communicating for decryption?
228 */
229 struct GNUNET_TIME_Absolute start;
230
231 /**
232 * When would we like the ciphertext to be
233 * decrypted?
234 */
235 struct GNUNET_TIME_Absolute deadline;
236
237 /**
238 * Ciphertext we want to decrypt.
239 */
240 struct GNUNET_SECRETSHARING_Ciphertext ciphertext;
241
242 /**
243 * Share of the local peer.
244 * Contains other important information, such as
245 * the list of other peers.
246 */
247 struct GNUNET_SECRETSHARING_Share *share;
248
249 /**
250 * State information about other peers.
251 */
252 struct DecryptPeerInfo *info;
253};
254
255
256/**
257 * State we keep per client.
258 */
259struct ClientState
260{
261 /**
262 * Decrypt session of the client, if any.
263 */
264 struct DecryptSession *decrypt_session;
265
266 /**
267 * Keygen session of the client, if any.
268 */
269 struct KeygenSession *keygen_session;
270
271 /**
272 * Client this is about.
273 */
274 struct GNUNET_SERVICE_Client *client;
275
276 /**
277 * MQ to talk to @a client.
278 */
279 struct GNUNET_MQ_Handle *mq;
280};
281
282
283/**
284 * The ElGamal prime field order as libgcrypt mpi.
285 * Initialized in #init_crypto_constants.
286 */
287static gcry_mpi_t elgamal_q;
288
289/**
290 * Modulus of the prime field used for ElGamal.
291 * Initialized in #init_crypto_constants.
292 */
293static gcry_mpi_t elgamal_p;
294
295/**
296 * Generator for prime field of order 'elgamal_q'.
297 * Initialized in #init_crypto_constants.
298 */
299static gcry_mpi_t elgamal_g;
300
301/**
302 * Peer that runs this service.
303 */
304static struct GNUNET_PeerIdentity my_peer;
305
306/**
307 * Peer that runs this service.
308 */
309static struct GNUNET_CRYPTO_EddsaPrivateKey *my_peer_private_key;
310
311/**
312 * Configuration of this service.
313 */
314static const struct GNUNET_CONFIGURATION_Handle *cfg;
315
316
317/**
318 * Get the peer info belonging to a peer identity in a keygen session.
319 *
320 * @param ks The keygen session.
321 * @param peer The peer identity.
322 * @return The Keygen peer info, or NULL if the peer could not be found.
323 */
324static struct KeygenPeerInfo *
325get_keygen_peer_info (const struct KeygenSession *ks,
326 const struct GNUNET_PeerIdentity *peer)
327{
328 unsigned int i;
329
330 for (i = 0; i < ks->num_peers; i++)
331 if (0 == GNUNET_memcmp (peer, &ks->info[i].peer))
332 return &ks->info[i];
333 return NULL;
334}
335
336
337/**
338 * Get the peer info belonging to a peer identity in a decrypt session.
339 *
340 * @param ds The decrypt session.
341 * @param peer The peer identity.
342 * @return The decrypt peer info, or NULL if the peer could not be found.
343 */
344static struct DecryptPeerInfo *
345get_decrypt_peer_info (const struct DecryptSession *ds,
346 const struct GNUNET_PeerIdentity *peer)
347{
348 unsigned int i;
349
350 for (i = 0; i < ds->share->num_peers; i++)
351 if (0 == GNUNET_memcmp (peer, &ds->info[i].peer))
352 return &ds->info[i];
353 return NULL;
354}
355
356
357/**
358 * Interpolate between two points in time.
359 *
360 * @param start start time
361 * @param end end time
362 * @param num numerator of the scale factor
363 * @param denum denumerator of the scale factor
364 */
365static struct GNUNET_TIME_Absolute
366time_between (struct GNUNET_TIME_Absolute start,
367 struct GNUNET_TIME_Absolute end,
368 int num, int denum)
369{
370 struct GNUNET_TIME_Absolute result;
371 uint64_t diff;
372
373 GNUNET_assert (start.abs_value_us <= end.abs_value_us);
374 diff = end.abs_value_us - start.abs_value_us;
375 result.abs_value_us = start.abs_value_us + ((diff * num) / denum);
376
377 return result;
378}
379
380
381/**
382 * Compare two peer identities. Intended to be used with qsort or bsearch.
383 *
384 * @param p1 Some peer identity.
385 * @param p2 Some peer identity.
386 * @return 1 if p1 > p2, -1 if p1 < p2 and 0 if p1 == p2.
387 */
388static int
389peer_id_cmp (const void *p1, const void *p2)
390{
391 return memcmp (p1,
392 p2,
393 sizeof(struct GNUNET_PeerIdentity));
394}
395
396
397/**
398 * Get the index of a peer in an array of peers
399 *
400 * @param haystack Array of peers.
401 * @param n Size of @a haystack.
402 * @param needle Peer to find
403 * @return Index of @a needle in @a haystack, or -1 if peer
404 * is not in the list.
405 */
406static int
407peer_find (const struct GNUNET_PeerIdentity *haystack, unsigned int n,
408 const struct GNUNET_PeerIdentity *needle)
409{
410 unsigned int i;
411
412 for (i = 0; i < n; i++)
413 if (0 == GNUNET_memcmp (&haystack[i],
414 needle))
415 return i;
416 return -1;
417}
418
419
420/**
421 * Normalize the given list of peers, by including the local peer
422 * (if it is missing) and sorting the peers by their identity.
423 *
424 * @param listed Peers in the unnormalized list.
425 * @param num_listed Peers in the un-normalized list.
426 * @param[out] num_normalized Number of peers in the normalized list.
427 * @param[out] my_peer_idx Index of the local peer in the normalized list.
428 * @return Normalized list, must be free'd by the caller.
429 */
430static struct GNUNET_PeerIdentity *
431normalize_peers (struct GNUNET_PeerIdentity *listed,
432 unsigned int num_listed,
433 unsigned int *num_normalized,
434 unsigned int *my_peer_idx)
435{
436 unsigned int local_peer_in_list;
437 /* number of peers in the normalized list */
438 unsigned int n;
439 struct GNUNET_PeerIdentity *normalized;
440
441 local_peer_in_list = GNUNET_YES;
442 n = num_listed;
443 if (peer_find (listed, num_listed, &my_peer) < 0)
444 {
445 local_peer_in_list = GNUNET_NO;
446 n += 1;
447 }
448
449 normalized = GNUNET_new_array (n,
450 struct GNUNET_PeerIdentity);
451
452 if (GNUNET_NO == local_peer_in_list)
453 normalized[n - 1] = my_peer;
454
455 GNUNET_memcpy (normalized,
456 listed,
457 num_listed * sizeof(struct GNUNET_PeerIdentity));
458 qsort (normalized,
459 n,
460 sizeof(struct GNUNET_PeerIdentity),
461 &peer_id_cmp);
462
463 if (NULL != my_peer_idx)
464 *my_peer_idx = peer_find (normalized, n, &my_peer);
465 if (NULL != num_normalized)
466 *num_normalized = n;
467
468 return normalized;
469}
470
471
472/**
473 * Get a the j-th lagrange coefficient for a set of indices.
474 *
475 * @param[out] coeff the lagrange coefficient
476 * @param j lagrange coefficient we want to compute
477 * @param indices indices
478 * @param num number of indices in @a indices
479 */
480static void
481compute_lagrange_coefficient (gcry_mpi_t coeff, unsigned int j,
482 unsigned int *indices,
483 unsigned int num)
484{
485 unsigned int i;
486 /* numerator */
487 gcry_mpi_t n;
488 /* denominator */
489 gcry_mpi_t d;
490 /* temp value for l-j */
491 gcry_mpi_t tmp;
492
493 GNUNET_assert (0 != coeff);
494
495 GNUNET_assert (0 != (n = gcry_mpi_new (0)));
496 GNUNET_assert (0 != (d = gcry_mpi_new (0)));
497 GNUNET_assert (0 != (tmp = gcry_mpi_new (0)));
498
499 gcry_mpi_set_ui (n, 1);
500 gcry_mpi_set_ui (d, 1);
501
502 for (i = 0; i < num; i++)
503 {
504 unsigned int l = indices[i];
505 if (l == j)
506 continue;
507 gcry_mpi_mul_ui (n, n, l + 1);
508 // d <- d * (l-j)
509 gcry_mpi_set_ui (tmp, l + 1);
510 gcry_mpi_sub_ui (tmp, tmp, j + 1);
511 gcry_mpi_mul (d, d, tmp);
512 }
513
514 // gcry_mpi_invm does not like negative numbers ...
515 gcry_mpi_mod (d, d, elgamal_q);
516
517 GNUNET_assert (gcry_mpi_cmp_ui (d, 0) > 0);
518
519 // now we do the actual division, with everything mod q, as we
520 // are not operating on elements from <g>, but on exponents
521 GNUNET_assert (0 != gcry_mpi_invm (d, d, elgamal_q));
522
523 gcry_mpi_mulm (coeff, n, d, elgamal_q);
524
525 gcry_mpi_release (n);
526 gcry_mpi_release (d);
527 gcry_mpi_release (tmp);
528}
529
530
531/**
532 * Destroy a decrypt session, removing it from
533 * the linked list of decrypt sessions.
534 *
535 * @param ds decrypt session to destroy
536 */
537static void
538decrypt_session_destroy (struct DecryptSession *ds)
539{
540 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
541 "destroying decrypt session\n");
542 if (NULL != ds->cs)
543 {
544 ds->cs->decrypt_session = NULL;
545 ds->cs = NULL;
546 }
547 if (NULL != ds->consensus)
548 {
549 GNUNET_CONSENSUS_destroy (ds->consensus);
550 ds->consensus = NULL;
551 }
552
553 if (NULL != ds->info)
554 {
555 for (unsigned int i = 0; i < ds->share->num_peers; i++)
556 {
557 if (NULL != ds->info[i].partial_decryption)
558 {
559 gcry_mpi_release (ds->info[i].partial_decryption);
560 ds->info[i].partial_decryption = NULL;
561 }
562 }
563 GNUNET_free (ds->info);
564 ds->info = NULL;
565 }
566 if (NULL != ds->share)
567 {
568 GNUNET_SECRETSHARING_share_destroy (ds->share);
569 ds->share = NULL;
570 }
571
572 GNUNET_free (ds);
573}
574
575
576static void
577keygen_info_destroy (struct KeygenPeerInfo *info)
578{
579 if (NULL != info->sigma)
580 {
581 gcry_mpi_release (info->sigma);
582 info->sigma = NULL;
583 }
584 if (NULL != info->presecret_commitment)
585 {
586 gcry_mpi_release (info->presecret_commitment);
587 info->presecret_commitment = NULL;
588 }
589 if (NULL != info->preshare_commitment)
590 {
591 gcry_mpi_release (info->preshare_commitment);
592 info->preshare_commitment = NULL;
593 }
594}
595
596
597static void
598keygen_session_destroy (struct KeygenSession *ks)
599{
600 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
601 "destroying keygen session\n");
602
603 if (NULL != ks->cs)
604 {
605 ks->cs->keygen_session = NULL;
606 ks->cs = NULL;
607 }
608 if (NULL != ks->info)
609 {
610 for (unsigned int i = 0; i < ks->num_peers; i++)
611 keygen_info_destroy (&ks->info[i]);
612 GNUNET_free (ks->info);
613 ks->info = NULL;
614 }
615
616 if (NULL != ks->consensus)
617 {
618 GNUNET_CONSENSUS_destroy (ks->consensus);
619 ks->consensus = NULL;
620 }
621
622 if (NULL != ks->presecret_polynomial)
623 {
624 for (unsigned int i = 0; i < ks->threshold; i++)
625 {
626 GNUNET_assert (NULL != ks->presecret_polynomial[i]);
627 gcry_mpi_release (ks->presecret_polynomial[i]);
628 ks->presecret_polynomial[i] = NULL;
629 }
630 GNUNET_free (ks->presecret_polynomial);
631 ks->presecret_polynomial = NULL;
632 }
633 if (NULL != ks->my_share)
634 {
635 gcry_mpi_release (ks->my_share);
636 ks->my_share = NULL;
637 }
638 if (NULL != ks->public_key)
639 {
640 gcry_mpi_release (ks->public_key);
641 ks->public_key = NULL;
642 }
643 if (NULL != ks->peers)
644 {
645 GNUNET_free (ks->peers);
646 ks->peers = NULL;
647 }
648 GNUNET_free (ks);
649}
650
651
652/**
653 * Task run during shutdown.
654 *
655 * @param cls unused
656 */
657static void
658cleanup_task (void *cls)
659{
660 /* Nothing to do! */
661}
662
663
664/**
665 * Generate the random coefficients of our pre-secret polynomial
666 *
667 * @param ks the session
668 */
669static void
670generate_presecret_polynomial (struct KeygenSession *ks)
671{
672 int i;
673 gcry_mpi_t v;
674
675 GNUNET_assert (NULL == ks->presecret_polynomial);
676 ks->presecret_polynomial = GNUNET_new_array (ks->threshold,
677 gcry_mpi_t);
678 for (i = 0; i < ks->threshold; i++)
679 {
680 v = ks->presecret_polynomial[i] = gcry_mpi_new (
681 GNUNET_SECRETSHARING_ELGAMAL_BITS);
682 GNUNET_assert (NULL != v);
683 // Randomize v such that 0 < v < elgamal_q.
684 // The '- 1' is necessary as bitlength(q) = bitlength(p) - 1.
685 do
686 {
687 gcry_mpi_randomize (v, GNUNET_SECRETSHARING_ELGAMAL_BITS - 1,
688 GCRY_WEAK_RANDOM);
689 }
690 while ((gcry_mpi_cmp_ui (v, 0) == 0) || (gcry_mpi_cmp (v, elgamal_q) >= 0));
691 }
692}
693
694
695/**
696 * Consensus element handler for round one.
697 * We should get one ephemeral key for each peer.
698 *
699 * @param cls Closure (keygen session).
700 * @param element The element from consensus, or
701 * NULL if consensus failed.
702 */
703static void
704keygen_round1_new_element (void *cls,
705 const struct GNUNET_SET_Element *element)
706{
707 const struct GNUNET_SECRETSHARING_KeygenCommitData *d;
708 struct KeygenSession *ks = cls;
709 struct KeygenPeerInfo *info;
710
711 if (NULL == element)
712 {
713 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "round1 consensus failed\n");
714 return;
715 }
716
717 /* elements have fixed size */
718 if (element->size != sizeof(struct GNUNET_SECRETSHARING_KeygenCommitData))
719 {
720 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
721 "keygen commit data with wrong size (%u) in consensus, %u expected\n",
722 (unsigned int) element->size,
723 (unsigned int) sizeof(struct
724 GNUNET_SECRETSHARING_KeygenCommitData));
725 return;
726 }
727
728 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "got round1 element\n");
729
730 d = element->data;
731 info = get_keygen_peer_info (ks, &d->peer);
732
733 if (NULL == info)
734 {
735 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
736 "keygen commit data with wrong peer identity (%s) in consensus\n",
737 GNUNET_i2s (&d->peer));
738 return;
739 }
740
741 /* Check that the right amount of data has been signed. */
742 if (d->purpose.size !=
743 htonl (element->size - offsetof (struct
744 GNUNET_SECRETSHARING_KeygenCommitData,
745 purpose)))
746 {
747 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
748 "keygen commit data with wrong signature purpose size in consensus\n");
749 return;
750 }
751
752 if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify_ (
753 GNUNET_SIGNATURE_PURPOSE_SECRETSHARING_DKG1,
754 &d->purpose, &d->signature,
755 &d->peer.public_key))
756 {
757 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
758 "keygen commit data with invalid signature in consensus\n");
759 return;
760 }
761 info->paillier_public_key = d->pubkey;
762 GNUNET_CRYPTO_mpi_scan_unsigned (&info->presecret_commitment, &d->commitment,
763 512 / 8);
764 info->round1_valid = GNUNET_YES;
765}
766
767
768/**
769 * Evaluate the polynomial with coefficients @a coeff at @a x.
770 * The i-th element in @a coeff corresponds to the coefficient of x^i.
771 *
772 * @param[out] z result of the evaluation
773 * @param coeff array of coefficients
774 * @param num_coeff number of coefficients
775 * @param x where to evaluate the polynomial
776 * @param m what group are we operating in?
777 */
778static void
779horner_eval (gcry_mpi_t z, gcry_mpi_t *coeff, unsigned int num_coeff, gcry_mpi_t
780 x, gcry_mpi_t m)
781{
782 unsigned int i;
783
784 gcry_mpi_set_ui (z, 0);
785 for (i = 0; i < num_coeff; i++)
786 {
787 // z <- zx + c
788 gcry_mpi_mul (z, z, x);
789 gcry_mpi_addm (z, z, coeff[num_coeff - i - 1], m);
790 }
791}
792
793
794static void
795keygen_round2_conclude (void *cls)
796{
797 struct KeygenSession *ks = cls;
798 struct GNUNET_SECRETSHARING_SecretReadyMessage *m;
799 struct GNUNET_MQ_Envelope *ev;
800 size_t share_size;
801 unsigned int i;
802 unsigned int j;
803 struct GNUNET_SECRETSHARING_Share *share;
804
805 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "round2 conclude\n");
806
807 GNUNET_CONSENSUS_destroy (ks->consensus);
808 ks->consensus = NULL;
809
810 share = GNUNET_new (struct GNUNET_SECRETSHARING_Share);
811
812 share->num_peers = 0;
813
814 for (i = 0; i < ks->num_peers; i++)
815 if (GNUNET_YES == ks->info[i].round2_valid)
816 share->num_peers++;
817
818 share->peers = GNUNET_new_array (share->num_peers,
819 struct GNUNET_PeerIdentity);
820 share->sigmas =
821 GNUNET_new_array (share->num_peers,
822 struct GNUNET_SECRETSHARING_FieldElement);
823 share->original_indices = GNUNET_new_array (share->num_peers,
824 uint16_t);
825
826 /* maybe we're not even in the list of peers? */
827 share->my_peer = share->num_peers;
828
829 j = 0; /* running index of valid peers */
830 for (i = 0; i < ks->num_peers; i++)
831 {
832 if (GNUNET_YES == ks->info[i].round2_valid)
833 {
834 share->peers[j] = ks->info[i].peer;
835 GNUNET_CRYPTO_mpi_print_unsigned (&share->sigmas[j],
836 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8,
837 ks->info[i].sigma);
838 share->original_indices[i] = j;
839 if (0 == GNUNET_memcmp (&share->peers[i], &my_peer))
840 share->my_peer = j;
841 j += 1;
842 }
843 }
844
845 if (share->my_peer == share->num_peers)
846 {
847 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "P%u: peer identity not in share\n",
848 ks->local_peer_idx);
849 }
850
851 GNUNET_CRYPTO_mpi_print_unsigned (&share->my_share,
852 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8,
853 ks->my_share);
854 GNUNET_CRYPTO_mpi_print_unsigned (&share->public_key,
855 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8,
856 ks->public_key);
857
858 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "keygen completed with %u peers\n",
859 share->num_peers);
860
861 /* Write the share. If 0 peers completed the dkg, an empty
862 * share will be sent. */
863
864 GNUNET_assert (GNUNET_OK == GNUNET_SECRETSHARING_share_write (share, NULL, 0,
865 &share_size));
866
867 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "writing share of size %u\n",
868 (unsigned int) share_size);
869
870 ev = GNUNET_MQ_msg_extra (m, share_size,
871 GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_SECRET_READY);
872
873 GNUNET_assert (GNUNET_OK == GNUNET_SECRETSHARING_share_write (share, &m[1],
874 share_size,
875 NULL));
876
877 GNUNET_SECRETSHARING_share_destroy (share);
878 share = NULL;
879
880 GNUNET_MQ_send (ks->cs->mq,
881 ev);
882}
883
884
885static void
886restore_fair (const struct GNUNET_CRYPTO_PaillierPublicKey *ppub,
887 const struct GNUNET_SECRETSHARING_FairEncryption *fe,
888 gcry_mpi_t x, gcry_mpi_t xres)
889{
890 gcry_mpi_t a_1;
891 gcry_mpi_t a_2;
892 gcry_mpi_t b_1;
893 gcry_mpi_t b_2;
894 gcry_mpi_t big_a;
895 gcry_mpi_t big_b;
896 gcry_mpi_t big_t;
897 gcry_mpi_t n;
898 gcry_mpi_t t_1;
899 gcry_mpi_t t_2;
900 gcry_mpi_t t;
901 gcry_mpi_t r;
902 gcry_mpi_t v;
903
904
905 GNUNET_assert (NULL != (n = gcry_mpi_new (0)));
906 GNUNET_assert (NULL != (t = gcry_mpi_new (0)));
907 GNUNET_assert (NULL != (t_1 = gcry_mpi_new (0)));
908 GNUNET_assert (NULL != (t_2 = gcry_mpi_new (0)));
909 GNUNET_assert (NULL != (r = gcry_mpi_new (0)));
910 GNUNET_assert (NULL != (big_t = gcry_mpi_new (0)));
911 GNUNET_assert (NULL != (v = gcry_mpi_new (0)));
912 GNUNET_assert (NULL != (big_a = gcry_mpi_new (0)));
913 GNUNET_assert (NULL != (big_b = gcry_mpi_new (0)));
914
915 // a = (N,0)^T
916 GNUNET_CRYPTO_mpi_scan_unsigned (&a_1,
917 ppub,
918 sizeof(struct
919 GNUNET_CRYPTO_PaillierPublicKey));
920 GNUNET_assert (NULL != (a_2 = gcry_mpi_new (0)));
921 gcry_mpi_set_ui (a_2, 0);
922 // b = (x,1)^T
923 GNUNET_assert (NULL != (b_1 = gcry_mpi_new (0)));
924 gcry_mpi_set (b_1, x);
925 GNUNET_assert (NULL != (b_2 = gcry_mpi_new (0)));
926 gcry_mpi_set_ui (b_2, 1);
927
928 // A = a DOT a
929 gcry_mpi_mul (t, a_1, a_1);
930 gcry_mpi_mul (big_a, a_2, a_2);
931 gcry_mpi_add (big_a, big_a, t);
932
933 // B = b DOT b
934 gcry_mpi_mul (t, b_1, b_1);
935 gcry_mpi_mul (big_b, b_2, b_2);
936 gcry_mpi_add (big_b, big_b, t);
937
938 while (1)
939 {
940 // n = a DOT b
941 gcry_mpi_mul (t, a_1, b_1);
942 gcry_mpi_mul (n, a_2, b_2);
943 gcry_mpi_add (n, n, t);
944
945 // r = nearest(n/B)
946 gcry_mpi_div (r, NULL, n, big_b, 0);
947
948 // T := A - 2rn + rrB
949 gcry_mpi_mul (v, r, n);
950 gcry_mpi_mul_ui (v, v, 2);
951 gcry_mpi_sub (big_t, big_a, v);
952 gcry_mpi_mul (v, r, r);
953 gcry_mpi_mul (v, v, big_b);
954 gcry_mpi_add (big_t, big_t, v);
955
956 if (gcry_mpi_cmp (big_t, big_b) >= 0)
957 {
958 break;
959 }
960
961 // t = a - rb
962 gcry_mpi_mul (v, r, b_1);
963 gcry_mpi_sub (t_1, a_1, v);
964 gcry_mpi_mul (v, r, b_2);
965 gcry_mpi_sub (t_2, a_2, v);
966
967 // a = b
968 gcry_mpi_set (a_1, b_1);
969 gcry_mpi_set (a_2, b_2);
970 // b = t
971 gcry_mpi_set (b_1, t_1);
972 gcry_mpi_set (b_2, t_2);
973
974 gcry_mpi_set (big_a, big_b);
975 gcry_mpi_set (big_b, big_t);
976 }
977
978 gcry_mpi_set (xres, b_2);
979 gcry_mpi_invm (xres, xres, elgamal_q);
980 gcry_mpi_mulm (xres, xres, b_1, elgamal_q);
981
982 gcry_mpi_release (a_1);
983 gcry_mpi_release (a_2);
984 gcry_mpi_release (b_1);
985 gcry_mpi_release (b_2);
986 gcry_mpi_release (big_a);
987 gcry_mpi_release (big_b);
988 gcry_mpi_release (big_t);
989 gcry_mpi_release (n);
990 gcry_mpi_release (t_1);
991 gcry_mpi_release (t_2);
992 gcry_mpi_release (t);
993 gcry_mpi_release (r);
994 gcry_mpi_release (v);
995}
996
997
998static void
999get_fair_encryption_challenge (const struct
1000 GNUNET_SECRETSHARING_FairEncryption *fe,
1001 gcry_mpi_t *e)
1002{
1003 struct
1004 {
1005 struct GNUNET_CRYPTO_PaillierCiphertext c;
1006 char h[GNUNET_SECRETSHARING_ELGAMAL_BITS / 8];
1007 char t1[GNUNET_SECRETSHARING_ELGAMAL_BITS / 8];
1008 char t2[GNUNET_CRYPTO_PAILLIER_BITS * 2 / 8];
1009 } hash_data;
1010 struct GNUNET_HashCode e_hash;
1011
1012 memset (&hash_data,
1013 0,
1014 sizeof(hash_data));
1015 GNUNET_memcpy (&hash_data.c, &fe->c, sizeof(struct
1016 GNUNET_CRYPTO_PaillierCiphertext));
1017 GNUNET_memcpy (&hash_data.h, &fe->h, GNUNET_SECRETSHARING_ELGAMAL_BITS / 8);
1018 GNUNET_memcpy (&hash_data.t1, &fe->t1, GNUNET_SECRETSHARING_ELGAMAL_BITS / 8);
1019 GNUNET_memcpy (&hash_data.t2, &fe->t2, GNUNET_CRYPTO_PAILLIER_BITS * 2 / 8);
1020 GNUNET_CRYPTO_hash (&hash_data,
1021 sizeof(hash_data),
1022 &e_hash);
1023 /* This allocates "e" */
1024 GNUNET_CRYPTO_mpi_scan_unsigned (e,
1025 &e_hash,
1026 sizeof(struct GNUNET_HashCode));
1027 gcry_mpi_mod (*e, *e, elgamal_q);
1028}
1029
1030
1031static int
1032verify_fair (const struct GNUNET_CRYPTO_PaillierPublicKey *ppub,
1033 const struct GNUNET_SECRETSHARING_FairEncryption *fe)
1034{
1035 gcry_mpi_t n;
1036 gcry_mpi_t n_sq;
1037 gcry_mpi_t z;
1038 gcry_mpi_t t1;
1039 gcry_mpi_t t2;
1040 gcry_mpi_t e;
1041 gcry_mpi_t w;
1042 gcry_mpi_t tmp1;
1043 gcry_mpi_t tmp2;
1044 gcry_mpi_t y;
1045 gcry_mpi_t big_y;
1046 int res;
1047
1048 GNUNET_assert (NULL != (n_sq = gcry_mpi_new (0)));
1049 GNUNET_assert (NULL != (tmp1 = gcry_mpi_new (0)));
1050 GNUNET_assert (NULL != (tmp2 = gcry_mpi_new (0)));
1051
1052 get_fair_encryption_challenge (fe,
1053 &e /* this allocates e */);
1054
1055 GNUNET_CRYPTO_mpi_scan_unsigned (&n,
1056 ppub,
1057 sizeof(struct
1058 GNUNET_CRYPTO_PaillierPublicKey));
1059 GNUNET_CRYPTO_mpi_scan_unsigned (&t1, fe->t1, GNUNET_CRYPTO_PAILLIER_BITS
1060 / 8);
1061 GNUNET_CRYPTO_mpi_scan_unsigned (&z, fe->z,
1062 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8);
1063 GNUNET_CRYPTO_mpi_scan_unsigned (&y, fe->h,
1064 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8);
1065 GNUNET_CRYPTO_mpi_scan_unsigned (&w, fe->w, GNUNET_CRYPTO_PAILLIER_BITS / 8);
1066 GNUNET_CRYPTO_mpi_scan_unsigned (&big_y, fe->c.bits,
1067 GNUNET_CRYPTO_PAILLIER_BITS * 2 / 8);
1068 GNUNET_CRYPTO_mpi_scan_unsigned (&t2, fe->t2, GNUNET_CRYPTO_PAILLIER_BITS
1069 * 2 / 8);
1070 gcry_mpi_mul (n_sq, n, n);
1071
1072 // tmp1 = g^z
1073 gcry_mpi_powm (tmp1, elgamal_g, z, elgamal_p);
1074 // tmp2 = y^{-e}
1075 gcry_mpi_powm (tmp1, y, e, elgamal_p);
1076 gcry_mpi_invm (tmp1, tmp1, elgamal_p);
1077 // tmp1 = tmp1 * tmp2
1078 gcry_mpi_mulm (tmp1, tmp1, tmp2, elgamal_p);
1079
1080 if (0 == gcry_mpi_cmp (t1, tmp1))
1081 {
1082 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "fair encryption invalid (t1)\n");
1083 res = GNUNET_NO;
1084 goto cleanup;
1085 }
1086
1087 gcry_mpi_powm (big_y, big_y, e, n_sq);
1088 gcry_mpi_invm (big_y, big_y, n_sq);
1089
1090 gcry_mpi_add_ui (tmp1, n, 1);
1091 gcry_mpi_powm (tmp1, tmp1, z, n_sq);
1092
1093 gcry_mpi_powm (tmp2, w, n, n_sq);
1094
1095 gcry_mpi_mulm (tmp1, tmp1, tmp2, n_sq);
1096 gcry_mpi_mulm (tmp1, tmp1, big_y, n_sq);
1097
1098
1099 if (0 == gcry_mpi_cmp (t2, tmp1))
1100 {
1101 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "fair encryption invalid (t2)\n");
1102 res = GNUNET_NO;
1103 goto cleanup;
1104 }
1105
1106 res = GNUNET_YES;
1107
1108cleanup:
1109
1110 gcry_mpi_release (n);
1111 gcry_mpi_release (n_sq);
1112 gcry_mpi_release (z);
1113 gcry_mpi_release (t1);
1114 gcry_mpi_release (t2);
1115 gcry_mpi_release (e);
1116 gcry_mpi_release (w);
1117 gcry_mpi_release (tmp1);
1118 gcry_mpi_release (tmp2);
1119 gcry_mpi_release (y);
1120 gcry_mpi_release (big_y);
1121 return res;
1122}
1123
1124
1125/**
1126 * Create a fair Paillier encryption of then given ciphertext.
1127 *
1128 * @param v the ciphertext
1129 * @param[out] fe the fair encryption
1130 */
1131static void
1132encrypt_fair (gcry_mpi_t v,
1133 const struct GNUNET_CRYPTO_PaillierPublicKey *ppub,
1134 struct GNUNET_SECRETSHARING_FairEncryption *fe)
1135{
1136 gcry_mpi_t r;
1137 gcry_mpi_t s;
1138 gcry_mpi_t t1;
1139 gcry_mpi_t t2;
1140 gcry_mpi_t z;
1141 gcry_mpi_t w;
1142 gcry_mpi_t n;
1143 gcry_mpi_t e;
1144 gcry_mpi_t n_sq;
1145 gcry_mpi_t u;
1146 gcry_mpi_t Y;
1147 gcry_mpi_t G;
1148 gcry_mpi_t h;
1149
1150 GNUNET_assert (NULL != (r = gcry_mpi_new (0)));
1151 GNUNET_assert (NULL != (s = gcry_mpi_new (0)));
1152 GNUNET_assert (NULL != (t1 = gcry_mpi_new (0)));
1153 GNUNET_assert (NULL != (t2 = gcry_mpi_new (0)));
1154 GNUNET_assert (NULL != (z = gcry_mpi_new (0)));
1155 GNUNET_assert (NULL != (w = gcry_mpi_new (0)));
1156 GNUNET_assert (NULL != (n_sq = gcry_mpi_new (0)));
1157 GNUNET_assert (NULL != (u = gcry_mpi_new (0)));
1158 GNUNET_assert (NULL != (Y = gcry_mpi_new (0)));
1159 GNUNET_assert (NULL != (G = gcry_mpi_new (0)));
1160 GNUNET_assert (NULL != (h = gcry_mpi_new (0)));
1161
1162 GNUNET_CRYPTO_mpi_scan_unsigned (&n,
1163 ppub,
1164 sizeof(struct
1165 GNUNET_CRYPTO_PaillierPublicKey));
1166 gcry_mpi_mul (n_sq, n, n);
1167 gcry_mpi_add_ui (G, n, 1);
1168
1169 do
1170 {
1171 gcry_mpi_randomize (u, GNUNET_CRYPTO_PAILLIER_BITS, GCRY_WEAK_RANDOM);
1172 }
1173 while (gcry_mpi_cmp (u, n) >= 0);
1174
1175 gcry_mpi_powm (t1, G, v, n_sq);
1176 gcry_mpi_powm (t2, u, n, n_sq);
1177 gcry_mpi_mulm (Y, t1, t2, n_sq);
1178
1179 GNUNET_CRYPTO_mpi_print_unsigned (fe->c.bits,
1180 sizeof fe->c.bits,
1181 Y);
1182
1183
1184 gcry_mpi_randomize (r, 2048, GCRY_WEAK_RANDOM);
1185 do
1186 {
1187 gcry_mpi_randomize (s, GNUNET_CRYPTO_PAILLIER_BITS, GCRY_WEAK_RANDOM);
1188 }
1189 while (gcry_mpi_cmp (s, n) >= 0);
1190
1191 // compute t1
1192 gcry_mpi_mulm (t1, elgamal_g, r, elgamal_p);
1193 // compute t2 (use z and w as temp)
1194 gcry_mpi_powm (z, G, r, n_sq);
1195 gcry_mpi_powm (w, s, n, n_sq);
1196 gcry_mpi_mulm (t2, z, w, n_sq);
1197
1198
1199 gcry_mpi_powm (h, elgamal_g, v, elgamal_p);
1200
1201 GNUNET_CRYPTO_mpi_print_unsigned (fe->h,
1202 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8,
1203 h);
1204
1205 GNUNET_CRYPTO_mpi_print_unsigned (fe->t1,
1206 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8,
1207 t1);
1208
1209 GNUNET_CRYPTO_mpi_print_unsigned (fe->t2,
1210 GNUNET_CRYPTO_PAILLIER_BITS * 2 / 8,
1211 t2);
1212
1213 get_fair_encryption_challenge (fe,
1214 &e /* This allocates "e" */);
1215
1216 // compute z
1217 gcry_mpi_mul (z, e, v);
1218 gcry_mpi_addm (z, z, r, elgamal_q);
1219 // compute w
1220 gcry_mpi_powm (w, u, e, n);
1221 gcry_mpi_mulm (w, w, s, n);
1222
1223 GNUNET_CRYPTO_mpi_print_unsigned (fe->z,
1224 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8,
1225 z);
1226
1227 GNUNET_CRYPTO_mpi_print_unsigned (fe->w,
1228 GNUNET_CRYPTO_PAILLIER_BITS / 8,
1229 w);
1230
1231 gcry_mpi_release (n);
1232 gcry_mpi_release (r);
1233 gcry_mpi_release (s);
1234 gcry_mpi_release (t1);
1235 gcry_mpi_release (t2);
1236 gcry_mpi_release (z);
1237 gcry_mpi_release (w);
1238 gcry_mpi_release (e);
1239 gcry_mpi_release (n_sq);
1240 gcry_mpi_release (u);
1241 gcry_mpi_release (Y);
1242 gcry_mpi_release (G);
1243 gcry_mpi_release (h);
1244}
1245
1246
1247/**
1248 * Insert round 2 element in the consensus, consisting of
1249 * (1) The exponentiated pre-share polynomial coefficients A_{i,l}=g^{a_{i,l}}
1250 * (2) The exponentiated pre-shares y_{i,j}=g^{s_{i,j}}
1251 * (3) The encrypted pre-shares Y_{i,j}
1252 * (4) The zero knowledge proof for fairness of
1253 * the encryption
1254 *
1255 * @param ks session to use
1256 */
1257static void
1258insert_round2_element (struct KeygenSession *ks)
1259{
1260 struct GNUNET_SET_Element *element;
1261 struct GNUNET_SECRETSHARING_KeygenRevealData *d;
1262 unsigned char *pos;
1263 unsigned char *last_pos;
1264 size_t element_size;
1265 unsigned int i;
1266 gcry_mpi_t idx;
1267 gcry_mpi_t v;
1268
1269 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "P%u: Inserting round2 element\n",
1270 ks->local_peer_idx);
1271
1272 GNUNET_assert (NULL != (v = gcry_mpi_new (
1273 GNUNET_SECRETSHARING_ELGAMAL_BITS)));
1274 GNUNET_assert (NULL != (idx = gcry_mpi_new (
1275 GNUNET_SECRETSHARING_ELGAMAL_BITS)));
1276
1277 element_size = (sizeof(struct GNUNET_SECRETSHARING_KeygenRevealData)
1278 + sizeof(struct GNUNET_SECRETSHARING_FairEncryption)
1279 * ks->num_peers
1280 + GNUNET_SECRETSHARING_ELGAMAL_BITS / 8 * ks->threshold);
1281
1282 element = GNUNET_malloc (sizeof(struct GNUNET_SET_Element) + element_size);
1283 element->size = element_size;
1284 element->data = (void *) &element[1];
1285
1286 d = (void *) element->data;
1287 d->peer = my_peer;
1288
1289 // start inserting vector elements
1290 // after the fixed part of the element's data
1291 pos = (void *) &d[1];
1292 last_pos = pos + element_size;
1293
1294 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "P%u: computed exp preshares\n",
1295 ks->local_peer_idx);
1296
1297 // encrypted pre-shares
1298 // and fair encryption proof
1299 {
1300 for (i = 0; i < ks->num_peers; i++)
1301 {
1302 ptrdiff_t remaining = last_pos - pos;
1303 struct GNUNET_SECRETSHARING_FairEncryption *fe = (void *) pos;
1304
1305 GNUNET_assert (remaining > 0);
1306 memset (fe, 0, sizeof *fe);
1307 if (GNUNET_YES == ks->info[i].round1_valid)
1308 {
1309 gcry_mpi_set_ui (idx, i + 1);
1310 // evaluate the polynomial
1311 horner_eval (v, ks->presecret_polynomial, ks->threshold, idx,
1312 elgamal_q);
1313 // encrypt the result
1314 encrypt_fair (v, &ks->info[i].paillier_public_key, fe);
1315 }
1316 pos += sizeof *fe;
1317 }
1318 }
1319
1320 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "P%u: computed enc preshares\n",
1321 ks->local_peer_idx);
1322
1323 // exponentiated coefficients
1324 for (i = 0; i < ks->threshold; i++)
1325 {
1326 ptrdiff_t remaining = last_pos - pos;
1327 GNUNET_assert (remaining > 0);
1328 gcry_mpi_powm (v, elgamal_g, ks->presecret_polynomial[i], elgamal_p);
1329 GNUNET_CRYPTO_mpi_print_unsigned (pos, GNUNET_SECRETSHARING_ELGAMAL_BITS
1330 / 8, v);
1331 pos += GNUNET_SECRETSHARING_ELGAMAL_BITS / 8;
1332 }
1333
1334 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "P%u: computed exp coefficients\n",
1335 ks->local_peer_idx);
1336
1337
1338 d->purpose.size = htonl (element_size - offsetof (struct
1339 GNUNET_SECRETSHARING_KeygenRevealData,
1340 purpose));
1341 d->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_SECRETSHARING_DKG2);
1342 GNUNET_assert (GNUNET_OK ==
1343 GNUNET_CRYPTO_eddsa_sign_ (my_peer_private_key,
1344 &d->purpose,
1345 &d->signature));
1346
1347 GNUNET_CONSENSUS_insert (ks->consensus, element, NULL, NULL);
1348 GNUNET_free (element); /* FIXME: maybe stack-allocate instead? */
1349
1350 gcry_mpi_release (v);
1351 gcry_mpi_release (idx);
1352}
1353
1354
1355static gcry_mpi_t
1356keygen_reveal_get_exp_coeff (struct KeygenSession *ks,
1357 const struct
1358 GNUNET_SECRETSHARING_KeygenRevealData *d,
1359 unsigned int idx)
1360{
1361 unsigned char *pos;
1362 gcry_mpi_t exp_coeff;
1363
1364 GNUNET_assert (idx < ks->threshold);
1365
1366 pos = (void *) &d[1];
1367 // skip encrypted pre-shares
1368 pos += sizeof(struct GNUNET_SECRETSHARING_FairEncryption) * ks->num_peers;
1369 // skip exp. coeffs we are not interested in
1370 pos += GNUNET_SECRETSHARING_ELGAMAL_BITS / 8 * idx;
1371 // the first exponentiated coefficient is the public key share
1372 GNUNET_CRYPTO_mpi_scan_unsigned (&exp_coeff, pos,
1373 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8);
1374 return exp_coeff;
1375}
1376
1377
1378static struct GNUNET_SECRETSHARING_FairEncryption *
1379keygen_reveal_get_enc_preshare (struct KeygenSession *ks,
1380 const struct
1381 GNUNET_SECRETSHARING_KeygenRevealData *d,
1382 unsigned int idx)
1383{
1384 unsigned char *pos;
1385
1386 GNUNET_assert (idx < ks->num_peers);
1387
1388 pos = (void *) &d[1];
1389 // skip encrypted pre-shares we're not interested in
1390 pos += sizeof(struct GNUNET_SECRETSHARING_FairEncryption) * idx;
1391 return (struct GNUNET_SECRETSHARING_FairEncryption *) pos;
1392}
1393
1394
1395static gcry_mpi_t
1396keygen_reveal_get_exp_preshare (struct KeygenSession *ks,
1397 const struct
1398 GNUNET_SECRETSHARING_KeygenRevealData *d,
1399 unsigned int idx)
1400{
1401 gcry_mpi_t exp_preshare;
1402 struct GNUNET_SECRETSHARING_FairEncryption *fe;
1403
1404 GNUNET_assert (idx < ks->num_peers);
1405 fe = keygen_reveal_get_enc_preshare (ks, d, idx);
1406 GNUNET_CRYPTO_mpi_scan_unsigned (&exp_preshare, fe->h,
1407 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8);
1408 return exp_preshare;
1409}
1410
1411
1412static void
1413keygen_round2_new_element (void *cls,
1414 const struct GNUNET_SET_Element *element)
1415{
1416 struct KeygenSession *ks = cls;
1417 const struct GNUNET_SECRETSHARING_KeygenRevealData *d;
1418 struct KeygenPeerInfo *info;
1419 size_t expected_element_size;
1420 unsigned int j;
1421 int cmp_result;
1422 gcry_mpi_t tmp;
1423 gcry_mpi_t public_key_share;
1424 gcry_mpi_t preshare;
1425
1426 if (NULL == element)
1427 {
1428 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "round2 consensus failed\n");
1429 return;
1430 }
1431
1432 expected_element_size = (sizeof(struct GNUNET_SECRETSHARING_KeygenRevealData)
1433 + sizeof(struct
1434 GNUNET_SECRETSHARING_FairEncryption)
1435 * ks->num_peers
1436 + GNUNET_SECRETSHARING_ELGAMAL_BITS / 8
1437 * ks->threshold);
1438
1439 if (element->size != expected_element_size)
1440 {
1441 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1442 "keygen round2 data with wrong size (%u) in consensus, %u expected\n",
1443 (unsigned int) element->size,
1444 (unsigned int) expected_element_size);
1445 return;
1446 }
1447
1448 d = (const void *) element->data;
1449
1450 info = get_keygen_peer_info (ks, &d->peer);
1451
1452 if (NULL == info)
1453 {
1454 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1455 "keygen commit data with wrong peer identity (%s) in consensus\n",
1456 GNUNET_i2s (&d->peer));
1457 return;
1458 }
1459
1460 if (GNUNET_NO == info->round1_valid)
1461 {
1462 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1463 "ignoring round2 element from peer with invalid round1 element (%s)\n",
1464 GNUNET_i2s (&d->peer));
1465 return;
1466 }
1467
1468 if (GNUNET_YES == info->round2_valid)
1469 {
1470 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1471 "ignoring duplicate round2 element (%s)\n",
1472 GNUNET_i2s (&d->peer));
1473 return;
1474 }
1475
1476 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "got round2 element\n");
1477
1478 if (ntohl (d->purpose.size) !=
1479 element->size - offsetof (struct GNUNET_SECRETSHARING_KeygenRevealData,
1480 purpose))
1481 {
1482 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1483 "keygen reveal data with wrong signature purpose size in consensus\n");
1484 return;
1485 }
1486
1487 if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify_ (
1488 GNUNET_SIGNATURE_PURPOSE_SECRETSHARING_DKG2,
1489 &d->purpose, &d->signature,
1490 &d->peer.public_key))
1491 {
1492 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1493 "keygen reveal data with invalid signature in consensus\n");
1494 return;
1495 }
1496
1497 public_key_share = keygen_reveal_get_exp_coeff (ks, d, 0);
1498 info->preshare_commitment = keygen_reveal_get_exp_preshare (ks, d,
1499 ks->local_peer_idx);
1500
1501 if (NULL == ks->public_key)
1502 {
1503 GNUNET_assert (NULL != (ks->public_key = gcry_mpi_new (0)));
1504 gcry_mpi_set_ui (ks->public_key, 1);
1505 }
1506 gcry_mpi_mulm (ks->public_key, ks->public_key, public_key_share, elgamal_p);
1507
1508 gcry_mpi_release (public_key_share);
1509 public_key_share = NULL;
1510
1511 {
1512 struct GNUNET_SECRETSHARING_FairEncryption *fe =
1513 keygen_reveal_get_enc_preshare (ks, d, ks->local_peer_idx);
1514 GNUNET_assert (NULL != (preshare = gcry_mpi_new (0)));
1515 GNUNET_CRYPTO_paillier_decrypt (&ks->paillier_private_key,
1516 &ks->info[ks->local_peer_idx].
1517 paillier_public_key,
1518 &fe->c,
1519 preshare);
1520
1521 // FIXME: not doing the restoration is less expensive
1522 restore_fair (&ks->info[ks->local_peer_idx].paillier_public_key,
1523 fe,
1524 preshare,
1525 preshare);
1526 }
1527
1528 GNUNET_assert (NULL != (tmp = gcry_mpi_new (0)));
1529 gcry_mpi_powm (tmp, elgamal_g, preshare, elgamal_p);
1530
1531 cmp_result = gcry_mpi_cmp (tmp, info->preshare_commitment);
1532 gcry_mpi_release (tmp);
1533 tmp = NULL;
1534 if (0 != cmp_result)
1535 {
1536 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1537 "P%u: Got invalid presecret from P%u\n",
1538 (unsigned int) ks->local_peer_idx, (unsigned int) (info
1539 - ks->info));
1540 return;
1541 }
1542
1543 if (NULL == ks->my_share)
1544 {
1545 GNUNET_assert (NULL != (ks->my_share = gcry_mpi_new (0)));
1546 }
1547 gcry_mpi_addm (ks->my_share, ks->my_share, preshare, elgamal_q);
1548
1549 for (j = 0; j < ks->num_peers; j++)
1550 {
1551 gcry_mpi_t presigma;
1552 if (NULL == ks->info[j].sigma)
1553 {
1554 GNUNET_assert (NULL != (ks->info[j].sigma = gcry_mpi_new (0)));
1555 gcry_mpi_set_ui (ks->info[j].sigma, 1);
1556 }
1557 presigma = keygen_reveal_get_exp_preshare (ks, d, j);
1558 gcry_mpi_mulm (ks->info[j].sigma, ks->info[j].sigma, presigma, elgamal_p);
1559 gcry_mpi_release (presigma);
1560 }
1561
1562 gcry_mpi_t prod;
1563 GNUNET_assert (NULL != (prod = gcry_mpi_new (0)));
1564 gcry_mpi_t j_to_k;
1565 GNUNET_assert (NULL != (j_to_k = gcry_mpi_new (0)));
1566 // validate that the polynomial sharing matches the additive sharing
1567 for (j = 0; j < ks->num_peers; j++)
1568 {
1569 unsigned int k;
1570 int cmp_result;
1571 gcry_mpi_t exp_preshare;
1572 gcry_mpi_set_ui (prod, 1);
1573 for (k = 0; k < ks->threshold; k++)
1574 {
1575 // Using pow(double,double) is a bit sketchy.
1576 // We count players from 1, but shares from 0.
1577 gcry_mpi_t tmp;
1578 gcry_mpi_set_ui (j_to_k, (unsigned int) pow (j + 1, k));
1579 tmp = keygen_reveal_get_exp_coeff (ks, d, k);
1580 gcry_mpi_powm (tmp, tmp, j_to_k, elgamal_p);
1581 gcry_mpi_mulm (prod, prod, tmp, elgamal_p);
1582 gcry_mpi_release (tmp);
1583 }
1584 exp_preshare = keygen_reveal_get_exp_preshare (ks, d, j);
1585 gcry_mpi_mod (exp_preshare, exp_preshare, elgamal_p);
1586 cmp_result = gcry_mpi_cmp (prod, exp_preshare);
1587 gcry_mpi_release (exp_preshare);
1588 exp_preshare = NULL;
1589 if (0 != cmp_result)
1590 {
1591 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1592 "P%u: reveal data from P%u incorrect\n",
1593 ks->local_peer_idx, j);
1594 /* no need for further verification, round2 stays invalid ... */
1595 return;
1596 }
1597 }
1598
1599 // TODO: verify proof of fair encryption (once implemented)
1600 for (j = 0; j < ks->num_peers; j++)
1601 {
1602 struct GNUNET_SECRETSHARING_FairEncryption *fe =
1603 keygen_reveal_get_enc_preshare (ks, d, j);
1604 if (GNUNET_YES != verify_fair (&ks->info[j].paillier_public_key, fe))
1605 {
1606 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1607 "P%u: reveal data from P%u incorrect (fair encryption)\n",
1608 ks->local_peer_idx, j);
1609 return;
1610 }
1611 }
1612
1613 info->round2_valid = GNUNET_YES;
1614
1615 gcry_mpi_release (preshare);
1616 gcry_mpi_release (prod);
1617 gcry_mpi_release (j_to_k);
1618}
1619
1620
1621/**
1622 * Called when the first consensus round has concluded.
1623 * Will initiate the second round.
1624 *
1625 * @param cls closure
1626 */
1627static void
1628keygen_round1_conclude (void *cls)
1629{
1630 struct KeygenSession *ks = cls;
1631
1632 GNUNET_CONSENSUS_destroy (ks->consensus);
1633
1634 ks->consensus = GNUNET_CONSENSUS_create (cfg, ks->num_peers, ks->peers,
1635 &ks->session_id,
1636 time_between (ks->start_time,
1637 ks->deadline, 1, 2),
1638 ks->deadline,
1639 keygen_round2_new_element, ks);
1640
1641 insert_round2_element (ks);
1642
1643 GNUNET_CONSENSUS_conclude (ks->consensus,
1644 keygen_round2_conclude,
1645 ks);
1646}
1647
1648
1649/**
1650 * Insert the ephemeral key and the presecret commitment
1651 * of this peer in the consensus of the given session.
1652 *
1653 * @param ks session to use
1654 */
1655static void
1656insert_round1_element (struct KeygenSession *ks)
1657{
1658 struct GNUNET_SET_Element *element;
1659 struct GNUNET_SECRETSHARING_KeygenCommitData *d;
1660 // g^a_{i,0}
1661 gcry_mpi_t v;
1662 // big-endian representation of 'v'
1663 unsigned char v_data[GNUNET_SECRETSHARING_ELGAMAL_BITS / 8];
1664
1665 element = GNUNET_malloc (sizeof *element + sizeof *d);
1666 d = (void *) &element[1];
1667 element->data = d;
1668 element->size = sizeof *d;
1669
1670 d->peer = my_peer;
1671
1672 GNUNET_assert (0 != (v = gcry_mpi_new (GNUNET_SECRETSHARING_ELGAMAL_BITS)));
1673
1674 gcry_mpi_powm (v, elgamal_g, ks->presecret_polynomial[0], elgamal_p);
1675
1676 GNUNET_CRYPTO_mpi_print_unsigned (v_data, GNUNET_SECRETSHARING_ELGAMAL_BITS
1677 / 8, v);
1678
1679 GNUNET_CRYPTO_hash (v_data, GNUNET_SECRETSHARING_ELGAMAL_BITS / 8,
1680 &d->commitment);
1681
1682 d->pubkey = ks->info[ks->local_peer_idx].paillier_public_key;
1683
1684 d->purpose.size = htonl ((sizeof *d) - offsetof (struct
1685 GNUNET_SECRETSHARING_KeygenCommitData,
1686 purpose));
1687 d->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_SECRETSHARING_DKG1);
1688 GNUNET_assert (GNUNET_OK ==
1689 GNUNET_CRYPTO_eddsa_sign_ (my_peer_private_key,
1690 &d->purpose,
1691 &d->signature));
1692
1693 GNUNET_CONSENSUS_insert (ks->consensus, element, NULL, NULL);
1694
1695 gcry_mpi_release (v);
1696 GNUNET_free (element);
1697}
1698
1699
1700/**
1701 * Check that @a msg is well-formed.
1702 *
1703 * @param cls identification of the client
1704 * @param msg the actual message
1705 * @return #GNUNET_OK if @a msg is well-formed
1706 */
1707static int
1708check_client_keygen (void *cls,
1709 const struct GNUNET_SECRETSHARING_CreateMessage *msg)
1710{
1711 unsigned int num_peers = ntohs (msg->num_peers);
1712
1713 if (ntohs (msg->header.size) - sizeof(*msg) !=
1714 num_peers * sizeof(struct GNUNET_PeerIdentity))
1715 {
1716 GNUNET_break (0);
1717 return GNUNET_SYSERR;
1718 }
1719 return GNUNET_OK;
1720}
1721
1722
1723/**
1724 * Functions with this signature are called whenever a message is
1725 * received.
1726 *
1727 * @param cls identification of the client
1728 * @param msg the actual message
1729 */
1730static void
1731handle_client_keygen (void *cls,
1732 const struct GNUNET_SECRETSHARING_CreateMessage *msg)
1733{
1734 struct ClientState *cs = cls;
1735 struct KeygenSession *ks;
1736
1737 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1738 "client requested key generation\n");
1739 if (NULL != cs->keygen_session)
1740 {
1741 GNUNET_break (0);
1742 GNUNET_SERVICE_client_drop (cs->client);
1743 return;
1744 }
1745 ks = GNUNET_new (struct KeygenSession);
1746 ks->cs = cs;
1747 cs->keygen_session = ks;
1748 ks->deadline = GNUNET_TIME_absolute_ntoh (msg->deadline);
1749 ks->threshold = ntohs (msg->threshold);
1750 ks->num_peers = ntohs (msg->num_peers);
1751
1752 ks->peers = normalize_peers ((struct GNUNET_PeerIdentity *) &msg[1],
1753 ks->num_peers,
1754 &ks->num_peers,
1755 &ks->local_peer_idx);
1756
1757
1758 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1759 "first round of consensus with %u peers\n",
1760 ks->num_peers);
1761 ks->consensus = GNUNET_CONSENSUS_create (cfg,
1762 ks->num_peers,
1763 ks->peers,
1764 &msg->session_id,
1765 GNUNET_TIME_absolute_ntoh (
1766 msg->start),
1767 GNUNET_TIME_absolute_ntoh (
1768 msg->deadline),
1769 keygen_round1_new_element,
1770 ks);
1771
1772 ks->info = GNUNET_new_array (ks->num_peers,
1773 struct KeygenPeerInfo);
1774
1775 for (unsigned int i = 0; i < ks->num_peers; i++)
1776 ks->info[i].peer = ks->peers[i];
1777
1778 GNUNET_CRYPTO_paillier_create (
1779 &ks->info[ks->local_peer_idx].paillier_public_key,
1780 &ks->paillier_private_key);
1781
1782 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1783 "P%u: Generated paillier key pair\n",
1784 ks->local_peer_idx);
1785 generate_presecret_polynomial (ks);
1786 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1787 "P%u: Generated presecret polynomial\n",
1788 ks->local_peer_idx);
1789 insert_round1_element (ks);
1790 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1791 "P%u: Concluding for round 1\n",
1792 ks->local_peer_idx);
1793 GNUNET_CONSENSUS_conclude (ks->consensus,
1794 keygen_round1_conclude,
1795 ks);
1796 GNUNET_SERVICE_client_continue (cs->client);
1797 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1798 "P%u: Waiting for round 1 elements ...\n",
1799 ks->local_peer_idx);
1800}
1801
1802
1803/**
1804 * Called when the partial decryption consensus concludes.
1805 */
1806static void
1807decrypt_conclude (void *cls)
1808{
1809 struct DecryptSession *ds = cls;
1810 struct GNUNET_SECRETSHARING_DecryptResponseMessage *msg;
1811 struct GNUNET_MQ_Envelope *ev;
1812 gcry_mpi_t lagrange;
1813 gcry_mpi_t m;
1814 gcry_mpi_t tmp;
1815 gcry_mpi_t c_2;
1816 gcry_mpi_t prod;
1817 unsigned int *indices;
1818 unsigned int num;
1819 unsigned int i;
1820 unsigned int j;
1821
1822 GNUNET_CONSENSUS_destroy (ds->consensus);
1823 ds->consensus = NULL;
1824
1825 GNUNET_assert (0 != (lagrange = gcry_mpi_new (0)));
1826 GNUNET_assert (0 != (m = gcry_mpi_new (0)));
1827 GNUNET_assert (0 != (tmp = gcry_mpi_new (0)));
1828 GNUNET_assert (0 != (prod = gcry_mpi_new (0)));
1829
1830 num = 0;
1831 for (i = 0; i < ds->share->num_peers; i++)
1832 if (NULL != ds->info[i].partial_decryption)
1833 num++;
1834
1835 indices = GNUNET_new_array (num,
1836 unsigned int);
1837 j = 0;
1838 for (i = 0; i < ds->share->num_peers; i++)
1839 if (NULL != ds->info[i].partial_decryption)
1840 indices[j++] = ds->info[i].original_index;
1841
1842 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1843 "P%u: decrypt conclude, with %u peers\n",
1844 ds->share->my_peer,
1845 num);
1846
1847 gcry_mpi_set_ui (prod, 1);
1848 for (i = 0; i < num; i++)
1849 {
1850 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1851 "P%u: index of %u: %u\n",
1852 ds->share->my_peer, i, indices[i]);
1853 compute_lagrange_coefficient (lagrange, indices[i], indices, num);
1854 // w_i^{\lambda_i}
1855 gcry_mpi_powm (tmp, ds->info[indices[i]].partial_decryption, lagrange,
1856 elgamal_p);
1857
1858 // product of all exponentiated partiel decryptions ...
1859 gcry_mpi_mulm (prod, prod, tmp, elgamal_p);
1860 }
1861
1862 GNUNET_CRYPTO_mpi_scan_unsigned (&c_2, ds->ciphertext.c2_bits,
1863 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8);
1864
1865 GNUNET_assert (0 != gcry_mpi_invm (prod, prod, elgamal_p));
1866 gcry_mpi_mulm (m, c_2, prod, elgamal_p);
1867 ev = GNUNET_MQ_msg (msg,
1868 GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT_DONE);
1869 GNUNET_CRYPTO_mpi_print_unsigned (&msg->plaintext,
1870 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, m);
1871 msg->success = htonl (1);
1872 GNUNET_MQ_send (ds->cs->mq,
1873 ev);
1874
1875 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "sent decrypt done to client\n");
1876
1877 GNUNET_free (indices);
1878
1879 gcry_mpi_release (lagrange);
1880 gcry_mpi_release (m);
1881 gcry_mpi_release (tmp);
1882 gcry_mpi_release (prod);
1883 gcry_mpi_release (c_2);
1884
1885 // FIXME: what if not enough peers participated?
1886}
1887
1888
1889/**
1890 * Get a string representation of an MPI.
1891 * The caller must free the returned string.
1892 *
1893 * @param mpi mpi to convert to a string
1894 * @return string representation of @a mpi, must be free'd by the caller
1895 */
1896static char *
1897mpi_to_str (gcry_mpi_t mpi)
1898{
1899 unsigned char *buf;
1900
1901 GNUNET_assert (0 == gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buf, NULL, mpi));
1902 return (char *) buf;
1903}
1904
1905
1906/**
1907 * Called when a new partial decryption arrives.
1908 */
1909static void
1910decrypt_new_element (void *cls,
1911 const struct GNUNET_SET_Element *element)
1912{
1913 struct DecryptSession *session = cls;
1914 const struct GNUNET_SECRETSHARING_DecryptData *d;
1915 struct DecryptPeerInfo *info;
1916 struct GNUNET_HashCode challenge_hash;
1917
1918 /* nizk response */
1919 gcry_mpi_t r;
1920 /* nizk challenge */
1921 gcry_mpi_t challenge;
1922 /* nizk commit1, g^\beta */
1923 gcry_mpi_t commit1;
1924 /* nizk commit2, c_1^\beta */
1925 gcry_mpi_t commit2;
1926 /* homomorphic commitment to the peer's share,
1927 * public key share */
1928 gcry_mpi_t sigma;
1929 /* partial decryption we received */
1930 gcry_mpi_t w;
1931 /* ciphertext component #1 */
1932 gcry_mpi_t c1;
1933 /* temporary variable (for comparison) #1 */
1934 gcry_mpi_t tmp1;
1935 /* temporary variable (for comparison) #2 */
1936 gcry_mpi_t tmp2;
1937
1938 if (NULL == element)
1939 {
1940 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "decryption failed\n");
1941 /* FIXME: destroy */
1942 return;
1943 }
1944
1945 if (element->size != sizeof *d)
1946 {
1947 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1948 "element of wrong size in decrypt consensus\n");
1949 return;
1950 }
1951
1952 d = element->data;
1953
1954 info = get_decrypt_peer_info (session, &d->peer);
1955
1956 if (NULL == info)
1957 {
1958 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1959 "decrypt element from invalid peer (%s)\n",
1960 GNUNET_i2s (&d->peer));
1961 return;
1962 }
1963
1964 if (NULL != info->partial_decryption)
1965 {
1966 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "decrypt element duplicate\n");
1967 return;
1968 }
1969
1970 if (0 != GNUNET_memcmp (&d->ciphertext, &session->ciphertext))
1971 {
1972 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1973 "P%u: got decrypt element with non-matching ciphertext from P%u\n",
1974 (unsigned int) session->share->my_peer, (unsigned int) (info
1975 -
1976 session
1977 ->info));
1978
1979 return;
1980 }
1981
1982
1983 GNUNET_CRYPTO_hash (offsetof (struct GNUNET_SECRETSHARING_DecryptData,
1984 ciphertext) + (char *) d,
1985 offsetof (struct GNUNET_SECRETSHARING_DecryptData,
1986 nizk_response)
1987 - offsetof (struct GNUNET_SECRETSHARING_DecryptData,
1988 ciphertext),
1989 &challenge_hash);
1990
1991 GNUNET_CRYPTO_mpi_scan_unsigned (&challenge, &challenge_hash,
1992 sizeof(struct GNUNET_HashCode));
1993
1994 GNUNET_CRYPTO_mpi_scan_unsigned (&sigma, &session->share->sigmas[info
1995 - session->
1996 info],
1997 sizeof(struct
1998 GNUNET_SECRETSHARING_FieldElement));
1999
2000 GNUNET_CRYPTO_mpi_scan_unsigned (&c1, session->ciphertext.c1_bits,
2001 sizeof(struct
2002 GNUNET_SECRETSHARING_FieldElement));
2003
2004 GNUNET_CRYPTO_mpi_scan_unsigned (&commit1, &d->nizk_commit1,
2005 sizeof(struct
2006 GNUNET_SECRETSHARING_FieldElement));
2007
2008 GNUNET_CRYPTO_mpi_scan_unsigned (&commit2, &d->nizk_commit2,
2009 sizeof(struct
2010 GNUNET_SECRETSHARING_FieldElement));
2011
2012 GNUNET_CRYPTO_mpi_scan_unsigned (&r, &d->nizk_response,
2013 sizeof(struct
2014 GNUNET_SECRETSHARING_FieldElement));
2015
2016 GNUNET_CRYPTO_mpi_scan_unsigned (&w, &d->partial_decryption,
2017 sizeof(struct
2018 GNUNET_SECRETSHARING_FieldElement));
2019
2020 GNUNET_assert (NULL != (tmp1 = gcry_mpi_new (0)));
2021 GNUNET_assert (NULL != (tmp2 = gcry_mpi_new (0)));
2022
2023 // tmp1 = g^r
2024 gcry_mpi_powm (tmp1, elgamal_g, r, elgamal_p);
2025
2026 // tmp2 = g^\beta * \sigma^challenge
2027 gcry_mpi_powm (tmp2, sigma, challenge, elgamal_p);
2028 gcry_mpi_mulm (tmp2, tmp2, commit1, elgamal_p);
2029
2030 if (0 != gcry_mpi_cmp (tmp1, tmp2))
2031 {
2032 char *tmp1_str;
2033 char *tmp2_str;
2034
2035 tmp1_str = mpi_to_str (tmp1);
2036 tmp2_str = mpi_to_str (tmp2);
2037 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2038 "P%u: Received invalid partial decryption from P%u (eqn 1), expected %s got %s\n",
2039 session->share->my_peer,
2040 (unsigned int) (info - session->info),
2041 tmp1_str,
2042 tmp2_str);
2043 GNUNET_free (tmp1_str);
2044 GNUNET_free (tmp2_str);
2045 goto cleanup;
2046 }
2047
2048
2049 gcry_mpi_powm (tmp1, c1, r, elgamal_p);
2050
2051 gcry_mpi_powm (tmp2, w, challenge, elgamal_p);
2052 gcry_mpi_mulm (tmp2, tmp2, commit2, elgamal_p);
2053
2054
2055 if (0 != gcry_mpi_cmp (tmp1, tmp2))
2056 {
2057 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2058 "P%u: Received invalid partial decryption from P%u (eqn 2)\n",
2059 session->share->my_peer,
2060 (unsigned int) (info - session->info));
2061 goto cleanup;
2062 }
2063
2064
2065 GNUNET_CRYPTO_mpi_scan_unsigned (&info->partial_decryption,
2066 &d->partial_decryption,
2067 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8);
2068cleanup:
2069 gcry_mpi_release (tmp1);
2070 gcry_mpi_release (tmp2);
2071 gcry_mpi_release (sigma);
2072 gcry_mpi_release (commit1);
2073 gcry_mpi_release (commit2);
2074 gcry_mpi_release (r);
2075 gcry_mpi_release (w);
2076 gcry_mpi_release (challenge);
2077 gcry_mpi_release (c1);
2078}
2079
2080
2081static void
2082insert_decrypt_element (struct DecryptSession *ds)
2083{
2084 struct GNUNET_SECRETSHARING_DecryptData d;
2085 struct GNUNET_SET_Element element;
2086 /* our share */
2087 gcry_mpi_t s;
2088 /* partial decryption with our share */
2089 gcry_mpi_t w;
2090 /* first component of the elgamal ciphertext */
2091 gcry_mpi_t c1;
2092 /* nonce for dlog zkp */
2093 gcry_mpi_t beta;
2094 gcry_mpi_t tmp;
2095 gcry_mpi_t challenge;
2096 gcry_mpi_t sigma;
2097 struct GNUNET_HashCode challenge_hash;
2098
2099 /* make vagrind happy until we implement the real deal ... */
2100 memset (&d, 0, sizeof d);
2101
2102 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "P%u: Inserting decrypt element\n",
2103 ds->share->my_peer);
2104
2105 GNUNET_assert (ds->share->my_peer < ds->share->num_peers);
2106
2107 GNUNET_CRYPTO_mpi_scan_unsigned (&c1, &ds->ciphertext.c1_bits,
2108 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8);
2109 GNUNET_CRYPTO_mpi_scan_unsigned (&s, &ds->share->my_share,
2110 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8);
2111 GNUNET_CRYPTO_mpi_scan_unsigned (&sigma,
2112 &ds->share->sigmas[ds->share->my_peer],
2113 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8);
2114
2115 GNUNET_assert (NULL != (w = gcry_mpi_new (0)));
2116 GNUNET_assert (NULL != (beta = gcry_mpi_new (0)));
2117 GNUNET_assert (NULL != (tmp = gcry_mpi_new (0)));
2118
2119 // FIXME: unnecessary, remove once crypto works
2120 gcry_mpi_powm (tmp, elgamal_g, s, elgamal_p);
2121 if (0 != gcry_mpi_cmp (tmp, sigma))
2122 {
2123 char *sigma_str = mpi_to_str (sigma);
2124 char *tmp_str = mpi_to_str (tmp);
2125 char *s_str = mpi_to_str (s);
2126 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2127 "Share of P%u is invalid, ref sigma %s, "
2128 "computed sigma %s, s %s\n",
2129 ds->share->my_peer,
2130 sigma_str, tmp_str, s_str);
2131 GNUNET_free (sigma_str);
2132 GNUNET_free (tmp_str);
2133 GNUNET_free (s_str);
2134 }
2135
2136 gcry_mpi_powm (w, c1, s, elgamal_p);
2137
2138 element.data = (void *) &d;
2139 element.size = sizeof(struct GNUNET_SECRETSHARING_DecryptData);
2140 element.element_type = 0;
2141
2142 d.ciphertext = ds->ciphertext;
2143 d.peer = my_peer;
2144 GNUNET_CRYPTO_mpi_print_unsigned (&d.partial_decryption,
2145 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, w);
2146
2147 // create the zero knowledge proof
2148 // randomly choose beta such that 0 < beta < q
2149 do
2150 {
2151 gcry_mpi_randomize (beta, GNUNET_SECRETSHARING_ELGAMAL_BITS - 1,
2152 GCRY_WEAK_RANDOM);
2153 }
2154 while ((gcry_mpi_cmp_ui (beta, 0) == 0) || (gcry_mpi_cmp (beta, elgamal_q) >=
2155 0));
2156 // tmp = g^beta
2157 gcry_mpi_powm (tmp, elgamal_g, beta, elgamal_p);
2158 GNUNET_CRYPTO_mpi_print_unsigned (&d.nizk_commit1,
2159 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, tmp);
2160 // tmp = (c_1)^beta
2161 gcry_mpi_powm (tmp, c1, beta, elgamal_p);
2162 GNUNET_CRYPTO_mpi_print_unsigned (&d.nizk_commit2,
2163 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, tmp);
2164
2165 // the challenge is the hash of everything up to the response
2166 GNUNET_CRYPTO_hash (offsetof (struct GNUNET_SECRETSHARING_DecryptData,
2167 ciphertext) + (char *) &d,
2168 offsetof (struct GNUNET_SECRETSHARING_DecryptData,
2169 nizk_response)
2170 - offsetof (struct GNUNET_SECRETSHARING_DecryptData,
2171 ciphertext),
2172 &challenge_hash);
2173
2174 GNUNET_CRYPTO_mpi_scan_unsigned (&challenge, &challenge_hash,
2175 sizeof(struct GNUNET_HashCode));
2176
2177 // compute the response in tmp,
2178 // tmp = (c * s + beta) mod q
2179 gcry_mpi_mulm (tmp, challenge, s, elgamal_q);
2180 gcry_mpi_addm (tmp, tmp, beta, elgamal_q);
2181
2182 GNUNET_CRYPTO_mpi_print_unsigned (&d.nizk_response,
2183 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, tmp);
2184
2185 d.purpose.size = htonl (element.size - offsetof (struct
2186 GNUNET_SECRETSHARING_DecryptData,
2187 purpose));
2188 d.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_SECRETSHARING_DECRYPTION);
2189
2190 GNUNET_assert (GNUNET_OK ==
2191 GNUNET_CRYPTO_eddsa_sign_ (my_peer_private_key,
2192 &d.purpose,
2193 &d.signature));
2194
2195 GNUNET_CONSENSUS_insert (ds->consensus, &element, NULL, NULL);
2196 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2197 "P%u: Inserting decrypt element done!\n",
2198 ds->share->my_peer);
2199
2200 gcry_mpi_release (s);
2201 gcry_mpi_release (w);
2202 gcry_mpi_release (c1);
2203 gcry_mpi_release (beta);
2204 gcry_mpi_release (tmp);
2205 gcry_mpi_release (challenge);
2206 gcry_mpi_release (sigma);
2207}
2208
2209
2210/**
2211 * Check that @a msg is well-formed.
2212 *
2213 * @param cls identification of the client
2214 * @param msg the actual message
2215 * @return #GNUNET_OK (check deferred a bit)
2216 */
2217static int
2218check_client_decrypt (void *cls,
2219 const struct
2220 GNUNET_SECRETSHARING_DecryptRequestMessage *msg)
2221{
2222 /* we check later, it's complicated */
2223 return GNUNET_OK;
2224}
2225
2226
2227/**
2228 * Functions with this signature are called whenever a message is
2229 * received.
2230 *
2231 * @param cls identification of the client
2232 * @param msg the actual message
2233 */
2234static void
2235handle_client_decrypt (void *cls,
2236 const struct
2237 GNUNET_SECRETSHARING_DecryptRequestMessage *msg)
2238{
2239 struct ClientState *cs = cls;
2240 struct DecryptSession *ds;
2241 struct GNUNET_HashCode session_id;
2242
2243 if (NULL != cs->decrypt_session)
2244 {
2245 GNUNET_break (0);
2246 GNUNET_SERVICE_client_drop (cs->client);
2247 return;
2248 }
2249 ds = GNUNET_new (struct DecryptSession);
2250 cs->decrypt_session = ds;
2251 ds->cs = cs;
2252 ds->start = GNUNET_TIME_absolute_ntoh (msg->start);
2253 ds->deadline = GNUNET_TIME_absolute_ntoh (msg->deadline);
2254 ds->ciphertext = msg->ciphertext;
2255
2256 ds->share = GNUNET_SECRETSHARING_share_read (&msg[1],
2257 ntohs (msg->header.size)
2258 - sizeof(*msg),
2259 NULL);
2260 if (NULL == ds->share)
2261 {
2262 GNUNET_break (0);
2263 GNUNET_SERVICE_client_drop (cs->client);
2264 return;
2265 }
2266
2267 /* FIXME: this is probably sufficient, but kdf/hash with all values would be nicer ... */
2268 GNUNET_CRYPTO_hash (&msg->ciphertext,
2269 sizeof(struct GNUNET_SECRETSHARING_Ciphertext),
2270 &session_id);
2271 ds->consensus = GNUNET_CONSENSUS_create (cfg,
2272 ds->share->num_peers,
2273 ds->share->peers,
2274 &session_id,
2275 ds->start,
2276 ds->deadline,
2277 &decrypt_new_element,
2278 ds);
2279
2280
2281 ds->info = GNUNET_new_array (ds->share->num_peers,
2282 struct DecryptPeerInfo);
2283 for (unsigned int i = 0; i < ds->share->num_peers; i++)
2284 {
2285 ds->info[i].peer = ds->share->peers[i];
2286 ds->info[i].original_index = ds->share->original_indices[i];
2287 }
2288 insert_decrypt_element (ds);
2289 GNUNET_CONSENSUS_conclude (ds->consensus,
2290 decrypt_conclude,
2291 ds);
2292 GNUNET_SERVICE_client_continue (cs->client);
2293 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2294 "decrypting with %u peers\n",
2295 ds->share->num_peers);
2296}
2297
2298
2299static void
2300init_crypto_constants (void)
2301{
2302 GNUNET_assert (0 == gcry_mpi_scan (&elgamal_q, GCRYMPI_FMT_HEX,
2303 GNUNET_SECRETSHARING_ELGAMAL_Q_HEX, 0,
2304 NULL));
2305 GNUNET_assert (0 == gcry_mpi_scan (&elgamal_p, GCRYMPI_FMT_HEX,
2306 GNUNET_SECRETSHARING_ELGAMAL_P_HEX, 0,
2307 NULL));
2308 GNUNET_assert (0 == gcry_mpi_scan (&elgamal_g, GCRYMPI_FMT_HEX,
2309 GNUNET_SECRETSHARING_ELGAMAL_G_HEX, 0,
2310 NULL));
2311}
2312
2313
2314/**
2315 * Initialize secretsharing service.
2316 *
2317 * @param cls closure
2318 * @param c configuration to use
2319 * @param service the initialized service
2320 */
2321static void
2322run (void *cls,
2323 const struct GNUNET_CONFIGURATION_Handle *c,
2324 struct GNUNET_SERVICE_Handle *service)
2325{
2326 cfg = c;
2327 my_peer_private_key = GNUNET_CRYPTO_eddsa_key_create_from_configuration (c);
2328 if (NULL == my_peer_private_key)
2329 {
2330 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2331 "could not access host private key\n");
2332 GNUNET_break (0);
2333 GNUNET_SCHEDULER_shutdown ();
2334 return;
2335 }
2336 init_crypto_constants ();
2337 if (GNUNET_OK !=
2338 GNUNET_CRYPTO_get_peer_identity (cfg,
2339 &my_peer))
2340 {
2341 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2342 "could not retrieve host identity\n");
2343 GNUNET_break (0);
2344 GNUNET_SCHEDULER_shutdown ();
2345 return;
2346 }
2347 GNUNET_SCHEDULER_add_shutdown (&cleanup_task,
2348 NULL);
2349}
2350
2351
2352/**
2353 * Callback called when a client connects to the service.
2354 *
2355 * @param cls closure for the service
2356 * @param c the new client that connected to the service
2357 * @param mq the message queue used to send messages to the client
2358 * @return @a c
2359 */
2360static void *
2361client_connect_cb (void *cls,
2362 struct GNUNET_SERVICE_Client *c,
2363 struct GNUNET_MQ_Handle *mq)
2364{
2365 struct ClientState *cs = GNUNET_new (struct ClientState);;
2366
2367 cs->client = c;
2368 cs->mq = mq;
2369 return cs;
2370}
2371
2372
2373/**
2374 * Callback called when a client disconnected from the service
2375 *
2376 * @param cls closure for the service
2377 * @param c the client that disconnected
2378 * @param internal_cls should be equal to @a c
2379 */
2380static void
2381client_disconnect_cb (void *cls,
2382 struct GNUNET_SERVICE_Client *c,
2383 void *internal_cls)
2384{
2385 struct ClientState *cs = internal_cls;
2386
2387 if (NULL != cs->keygen_session)
2388 keygen_session_destroy (cs->keygen_session);
2389
2390 if (NULL != cs->decrypt_session)
2391 decrypt_session_destroy (cs->decrypt_session);
2392 GNUNET_free (cs);
2393}
2394
2395
2396/**
2397 * Define "main" method using service macro.
2398 */
2399GNUNET_SERVICE_MAIN
2400 ("secretsharing",
2401 GNUNET_SERVICE_OPTION_NONE,
2402 &run,
2403 &client_connect_cb,
2404 &client_disconnect_cb,
2405 NULL,
2406 GNUNET_MQ_hd_var_size (client_keygen,
2407 GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_GENERATE,
2408 struct GNUNET_SECRETSHARING_CreateMessage,
2409 NULL),
2410 GNUNET_MQ_hd_var_size (client_decrypt,
2411 GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT,
2412 struct GNUNET_SECRETSHARING_DecryptRequestMessage,
2413 NULL),
2414 GNUNET_MQ_handler_end ());
diff --git a/src/contrib/service/secretsharing/meson.build b/src/contrib/service/secretsharing/meson.build
new file mode 100644
index 000000000..bfd66dddb
--- /dev/null
+++ b/src/contrib/service/secretsharing/meson.build
@@ -0,0 +1,46 @@
1libgnunetsecretsharing_src = ['secretsharing_api.c', 'secretsharing_common.c']
2
3gnunetservicesecretsharing_src = ['gnunet-service-secretsharing.c', 'secretsharing_common.c']
4
5configure_file(input : 'secretsharing.conf.in',
6 output : 'secretsharing.conf',
7 configuration : cdata,
8 install: true,
9 install_dir: pkgcfgdir)
10
11# FIXME needs new seti/setu
12if get_option('monolith')
13 #foreach p : libgnunetsecretsharing_src + gnunetservicesecretsharing_src
14 # gnunet_src += 'secretsharing/' + p
15 #endforeach
16 subdir_done()
17endif
18
19libgnunetsecretsharing = library('gnunetsecretsharing',
20 libgnunetsecretsharing_src,
21 soversion: '0',
22 version: '0.0.0',
23 dependencies: [libgnunetutil_dep,
24 libgnunetstatistics_dep,
25 gcrypt_dep,
26 libgnunetdatacache_dep],
27 include_directories: [incdir, configuration_inc],
28 install: true,
29 install_dir: get_option('libdir'))
30libgnunetsecretsharing_dep = declare_dependency(link_with : libgnunetsecretsharing)
31pkg.generate(libgnunetsecretsharing, url: 'https://www.gnunet.org',
32 description : 'Provides API for the secretsharing service')
33
34executable ('gnunet-service-secretsharing',
35 gnunetservicesecretsharing_src,
36 dependencies: [libgnunetsecretsharing_dep,
37 libgnunetutil_dep,
38 gcrypt_dep,
39 m_dep,
40 libgnunetconsensus_dep,
41 libgnunetstatistics_dep,
42 libgnunetdatacache_dep],
43 include_directories: [incdir, configuration_inc],
44 install: true,
45 install_dir: get_option('libdir')/'gnunet'/'libexec')
46
diff --git a/src/contrib/service/secretsharing/secretsharing.conf.in b/src/contrib/service/secretsharing/secretsharing.conf.in
new file mode 100644
index 000000000..df8566abd
--- /dev/null
+++ b/src/contrib/service/secretsharing/secretsharing.conf.in
@@ -0,0 +1,20 @@
1[secretsharing]
2START_ON_DEMAND = NO
3@JAVAPORT@PORT = 2114
4HOSTNAME = localhost
5BINARY = gnunet-service-secretsharing
6ACCEPT_FROM = 127.0.0.1;
7ACCEPT_FROM6 = ::1;
8UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-secretsharing.sock
9UNIX_MATCH_UID = YES
10UNIX_MATCH_GID = YES
11# PREFIX = valgrind --leak-check=yes
12# DISABLE_SOCKET_FORWARDING = NO
13# USERNAME =
14# MAXBUF =
15# TIMEOUT =
16# DISABLEV6 =
17# BINDTO =
18# REJECT_FROM =
19# REJECT_FROM6 =
20# PREFIX =
diff --git a/src/contrib/service/secretsharing/secretsharing.h b/src/contrib/service/secretsharing/secretsharing.h
new file mode 100644
index 000000000..6e104ebfa
--- /dev/null
+++ b/src/contrib/service/secretsharing/secretsharing.h
@@ -0,0 +1,227 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @author Florian Dold
23 * @file secretsharing/secretsharing.h
24 * @brief messages used for the secretsharing api
25 */
26#ifndef SECRETSHARING_H
27#define SECRETSHARING_H
28
29#include "platform.h"
30#include "gnunet_util_lib.h"
31#include "gnunet_time_lib.h"
32#include "gnunet_common.h"
33#include "gnunet_secretsharing_service.h"
34
35
36GNUNET_NETWORK_STRUCT_BEGIN
37
38struct GNUNET_SECRETSHARING_FieldElement
39{
40 /**
41 * Value of an element in &lt;elgamal_g&gt;.
42 */
43 unsigned char bits[GNUNET_SECRETSHARING_ELGAMAL_BITS / 8];
44};
45
46
47struct GNUNET_SECRETSHARING_CreateMessage
48{
49 /**
50 * Type: GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_GENERATE
51 */
52 struct GNUNET_MessageHeader header;
53
54 /**
55 * Session ID, will be used for consensus.
56 */
57 struct GNUNET_HashCode session_id GNUNET_PACKED;
58
59 /**
60 * Start time for communication with the other peers.
61 */
62 struct GNUNET_TIME_AbsoluteNBO start;
63
64 /**
65 * Deadline for the establishment of the crypto system.
66 */
67 struct GNUNET_TIME_AbsoluteNBO deadline;
68
69 /**
70 * Minimum number of cooperating peers to decrypt a
71 * value.
72 */
73 uint16_t threshold GNUNET_PACKED;
74
75 /**
76 * Number of peers at the end of this message.
77 */
78 uint16_t num_peers GNUNET_PACKED;
79
80 /* struct GNUNET_PeerIdentity[num_peers]; */
81};
82
83
84struct GNUNET_SECRETSHARING_ShareHeaderNBO
85{
86 /**
87 * Threshold for the key this share belongs to.
88 */
89 uint16_t threshold;
90
91 /**
92 * Peers that have the share.
93 */
94 uint16_t num_peers;
95
96 /**
97 * Index of our peer in the list.
98 */
99 uint16_t my_peer;
100
101 /**
102 * Public key. Must correspond to the product of
103 * the homomorphic share commitments.
104 */
105 struct GNUNET_SECRETSHARING_PublicKey public_key;
106
107 /**
108 * Share of 'my_peer'
109 */
110 struct GNUNET_SECRETSHARING_FieldElement my_share;
111};
112
113
114/**
115 * Notify the client that then threshold secret has been
116 * established.
117 */
118struct GNUNET_SECRETSHARING_SecretReadyMessage
119{
120 /**
121 * Type: GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_SECRET_READY
122 */
123 struct GNUNET_MessageHeader header;
124
125 /* rest: the serialized share */
126};
127
128
129struct GNUNET_SECRETSHARING_DecryptRequestMessage
130{
131 /**
132 * Type: GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT_REQUEST
133 */
134 struct GNUNET_MessageHeader header;
135
136 /**
137 * Until when should the decryption start?
138 */
139 struct GNUNET_TIME_AbsoluteNBO start;
140
141 /**
142 * Until when should the decryption be finished?
143 */
144 struct GNUNET_TIME_AbsoluteNBO deadline;
145
146 /**
147 * Ciphertext we want to decrypt.
148 */
149 struct GNUNET_SECRETSHARING_Ciphertext ciphertext;
150
151 /* the share with payload */
152};
153
154
155struct GNUNET_SECRETSHARING_DecryptResponseMessage
156{
157 /**
158 * Type: #GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT_DONE
159 */
160 struct GNUNET_MessageHeader header;
161
162 /**
163 * Zero if decryption failed, non-zero if decryption succeeded.
164 * If the decryption failed, plaintext is also zero.
165 */
166 uint32_t success GNUNET_PACKED;
167
168 /**
169 * Decrypted plaintext.
170 */
171 struct GNUNET_SECRETSHARING_FieldElement plaintext;
172};
173
174
175GNUNET_NETWORK_STRUCT_END
176
177
178/**
179 * A share, with all values in in host byte order.
180 */
181struct GNUNET_SECRETSHARING_Share
182{
183 /**
184 * Threshold for the key this share belongs to.
185 */
186 uint16_t threshold;
187
188 /**
189 * Peers that have the share.
190 */
191 uint16_t num_peers;
192
193 /**
194 * Index of our peer in the list.
195 */
196 uint16_t my_peer;
197
198 /**
199 * Public key. Computed from the
200 * exponentiated coefficients.
201 */
202 struct GNUNET_SECRETSHARING_PublicKey public_key;
203
204 /**
205 * Share of 'my_peer'
206 */
207 struct GNUNET_SECRETSHARING_FieldElement my_share;
208
209 /**
210 * Peer identities (includes 'my_peer')
211 */
212 struct GNUNET_PeerIdentity *peers;
213
214 /*
215 * For each peer, store elgamal_g to the peer's
216 * share.
217 */
218 struct GNUNET_SECRETSHARING_FieldElement *sigmas;
219
220 /*
221 * Original indices of peers from the DKG round.
222 */
223 uint16_t *original_indices;
224};
225
226
227#endif
diff --git a/src/contrib/service/secretsharing/secretsharing_api.c b/src/contrib/service/secretsharing/secretsharing_api.c
new file mode 100644
index 000000000..595af751f
--- /dev/null
+++ b/src/contrib/service/secretsharing/secretsharing_api.c
@@ -0,0 +1,492 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file secretsharing/secretsharing_api.c
23 * @brief
24 * @author Florian Dold
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_secretsharing_service.h"
29#include "secretsharing.h"
30#include <gcrypt.h>
31
32
33#define LOG(kind, ...) GNUNET_log_from (kind, "secretsharing-api", __VA_ARGS__)
34
35/**
36 * Session that will eventually establish a shared secred between
37 * the involved peers and allow encryption and cooperative decryption.
38 */
39struct GNUNET_SECRETSHARING_Session
40{
41 /**
42 * Message queue for @e client.
43 */
44 struct GNUNET_MQ_Handle *mq;
45
46 /**
47 * Called when the secret sharing is done.
48 */
49 GNUNET_SECRETSHARING_SecretReadyCallback secret_ready_cb;
50
51 /**
52 * Closure for @e secret_ready_cb.
53 */
54 void *secret_ready_cls;
55};
56
57
58/**
59 * Handle to cancel a cooperative decryption operation.
60 */
61struct GNUNET_SECRETSHARING_DecryptionHandle
62{
63 /**
64 * Message queue for @e client.
65 */
66 struct GNUNET_MQ_Handle *mq;
67
68 /**
69 * Called when the secret sharing is done.
70 */
71 GNUNET_SECRETSHARING_DecryptCallback decrypt_cb;
72
73 /**
74 * Closure for @e decrypt_cb.
75 */
76 void *decrypt_cls;
77};
78
79
80/**
81 * The ElGamal prime field order as libgcrypt mpi.
82 * Initialized in #init_crypto_constants.
83 */
84static gcry_mpi_t elgamal_q;
85
86/**
87 * Modulus of the prime field used for ElGamal.
88 * Initialized in #init_crypto_constants.
89 */
90static gcry_mpi_t elgamal_p;
91
92/**
93 * Generator for prime field of order 'elgamal_q'.
94 * Initialized in #init_crypto_constants.
95 */
96static gcry_mpi_t elgamal_g;
97
98
99/**
100 * Function to initialize #elgamal_q, #elgamal_p and #elgamal_g.
101 */
102static void
103ensure_elgamal_initialized (void)
104{
105 if (NULL != elgamal_q)
106 return; /* looks like crypto is already initialized */
107
108 GNUNET_assert (0 == gcry_mpi_scan (&elgamal_q, GCRYMPI_FMT_HEX,
109 GNUNET_SECRETSHARING_ELGAMAL_Q_HEX, 0,
110 NULL));
111 GNUNET_assert (0 == gcry_mpi_scan (&elgamal_p, GCRYMPI_FMT_HEX,
112 GNUNET_SECRETSHARING_ELGAMAL_P_HEX, 0,
113 NULL));
114 GNUNET_assert (0 == gcry_mpi_scan (&elgamal_g, GCRYMPI_FMT_HEX,
115 GNUNET_SECRETSHARING_ELGAMAL_G_HEX, 0,
116 NULL));
117}
118
119
120/**
121 * Callback invoked when there is an error communicating with
122 * the service. Notifies the application about the error.
123 *
124 * @param cls the `struct GNUNET_SECRETSHARING_Session`
125 * @param error error code
126 */
127static void
128handle_session_client_error (void *cls,
129 enum GNUNET_MQ_Error error)
130{
131 struct GNUNET_SECRETSHARING_Session *s = cls;
132
133 s->secret_ready_cb (s->secret_ready_cls, NULL, NULL, 0, NULL);
134 GNUNET_SECRETSHARING_session_destroy (s);
135}
136
137
138/**
139 * Callback invoked when there is an error communicating with
140 * the service. Notifies the application about the error.
141 *
142 * @param cls the `struct GNUNET_SECRETSHARING_DecryptionHandle`
143 * @param error error code
144 */
145static void
146handle_decrypt_client_error (void *cls,
147 enum GNUNET_MQ_Error error)
148{
149 struct GNUNET_SECRETSHARING_DecryptionHandle *dh = cls;
150
151 dh->decrypt_cb (dh->decrypt_cls, NULL);
152 GNUNET_SECRETSHARING_decrypt_cancel (dh);
153}
154
155
156/**
157 * Handler invoked with the final result message from
158 * secret sharing. Decodes the message and passes the
159 * result to the application.
160 *
161 * @param cls the `struct GNUNET_SECRETSHARING_Session`
162 * @param m message with the result
163 */
164static int
165check_secret_ready (void *cls,
166 const struct GNUNET_SECRETSHARING_SecretReadyMessage *m)
167{
168 /* FIXME: actually check m is well-formed here! */
169 return GNUNET_OK;
170}
171
172
173/**
174 * Handler invoked with the final result message from
175 * secret sharing. Decodes the message and passes the
176 * result to the application.
177 *
178 * @param cls the `struct GNUNET_SECRETSHARING_Session`
179 * @param m message with the result
180 */
181static void
182handle_secret_ready (void *cls,
183 const struct GNUNET_SECRETSHARING_SecretReadyMessage *m)
184{
185 struct GNUNET_SECRETSHARING_Session *s = cls;
186 struct GNUNET_SECRETSHARING_Share *share;
187 size_t share_size;
188
189 LOG (GNUNET_ERROR_TYPE_DEBUG,
190 "Got secret ready message of size %u\n",
191 ntohs (m->header.size));
192 share_size = ntohs (m->header.size) - sizeof(struct
193 GNUNET_SECRETSHARING_SecretReadyMessage);
194
195 share = GNUNET_SECRETSHARING_share_read (&m[1],
196 share_size,
197 NULL);
198 GNUNET_assert (NULL != share); // FIXME: this can fail!
199 // should have been checked in #check_secret_ready!
200 // FIXME: below we never check &m[1] is valid!
201 // FIXME: do we leak 'share' here?
202 s->secret_ready_cb (s->secret_ready_cls,
203 share, /* FIXME */
204 &share->public_key,
205 share->num_peers,
206 (const struct GNUNET_PeerIdentity *) &m[1]);
207
208 GNUNET_SECRETSHARING_session_destroy (s);
209}
210
211
212/**
213 * Destroy a secret sharing session.
214 * The secret ready callback will not be called.
215 *
216 * @param s session to destroy
217 */
218void
219GNUNET_SECRETSHARING_session_destroy (struct GNUNET_SECRETSHARING_Session *s)
220{
221 GNUNET_MQ_destroy (s->mq);
222 s->mq = NULL;
223 GNUNET_free (s);
224}
225
226
227/**
228 * Create a session that will eventually establish a shared secret
229 * with the other peers.
230 *
231 * @param cfg configuration to use
232 * @param num_peers number of peers in @a peers
233 * @param peers array of peers that we will share secrets with, can optionally contain the local peer
234 * @param session_id unique session id
235 * @param start When should all peers be available for sharing the secret?
236 * Random number generation can take place before the start time.
237 * @param deadline point in time where the session must be established; taken as hint
238 * by underlying consensus sessions
239 * @param threshold minimum number of peers that must cooperate to decrypt a value
240 * @param cb called when the secret has been established
241 * @param cls closure for @a cb
242 */
243struct GNUNET_SECRETSHARING_Session *
244GNUNET_SECRETSHARING_create_session (const struct
245 GNUNET_CONFIGURATION_Handle *cfg,
246 unsigned int num_peers,
247 const struct GNUNET_PeerIdentity *peers,
248 const struct GNUNET_HashCode *session_id,
249 struct GNUNET_TIME_Absolute start,
250 struct GNUNET_TIME_Absolute deadline,
251 unsigned int threshold,
252 GNUNET_SECRETSHARING_SecretReadyCallback cb,
253 void *cls)
254{
255 struct GNUNET_SECRETSHARING_Session *s
256 = GNUNET_new (struct GNUNET_SECRETSHARING_Session);
257 struct GNUNET_MQ_MessageHandler mq_handlers[] = {
258 GNUNET_MQ_hd_var_size (secret_ready,
259 GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_SECRET_READY,
260 struct GNUNET_SECRETSHARING_SecretReadyMessage,
261 s),
262 GNUNET_MQ_handler_end ()
263 };
264 struct GNUNET_MQ_Envelope *ev;
265 struct GNUNET_SECRETSHARING_CreateMessage *msg;
266
267 s->mq = GNUNET_CLIENT_connect (cfg,
268 "secretsharing",
269 mq_handlers,
270 &handle_session_client_error,
271 s);
272 if (NULL == s->mq)
273 {
274 /* secretsharing not configured correctly */
275 GNUNET_break (0);
276 GNUNET_free (s);
277 return NULL;
278 }
279 s->secret_ready_cb = cb;
280 s->secret_ready_cls = cls;
281 ev = GNUNET_MQ_msg_extra (msg,
282 num_peers * sizeof(struct GNUNET_PeerIdentity),
283 GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_GENERATE);
284
285 msg->threshold = htons (threshold);
286 msg->num_peers = htons (num_peers);
287 msg->session_id = *session_id;
288 msg->start = GNUNET_TIME_absolute_hton (start);
289 msg->deadline = GNUNET_TIME_absolute_hton (deadline);
290 GNUNET_memcpy (&msg[1], peers, num_peers * sizeof(struct
291 GNUNET_PeerIdentity));
292
293 GNUNET_MQ_send (s->mq, ev);
294
295 LOG (GNUNET_ERROR_TYPE_DEBUG,
296 "Secretsharing session created with %u peers\n",
297 num_peers);
298 return s;
299}
300
301
302static void
303handle_decrypt_done (void *cls,
304 const struct
305 GNUNET_SECRETSHARING_DecryptResponseMessage *m)
306{
307 struct GNUNET_SECRETSHARING_DecryptionHandle *dh = cls;
308 const struct GNUNET_SECRETSHARING_Plaintext *plaintext;
309
310 if (m->success == 0)
311 plaintext = NULL;
312 else
313 plaintext = (void *) &m->plaintext;
314 dh->decrypt_cb (dh->decrypt_cls, plaintext);
315 GNUNET_SECRETSHARING_decrypt_cancel (dh);
316}
317
318
319struct GNUNET_SECRETSHARING_DecryptionHandle *
320GNUNET_SECRETSHARING_decrypt (const struct GNUNET_CONFIGURATION_Handle *cfg,
321 struct GNUNET_SECRETSHARING_Share *share,
322 const struct
323 GNUNET_SECRETSHARING_Ciphertext *ciphertext,
324 struct GNUNET_TIME_Absolute start,
325 struct GNUNET_TIME_Absolute deadline,
326 GNUNET_SECRETSHARING_DecryptCallback decrypt_cb,
327 void *decrypt_cb_cls)
328{
329 struct GNUNET_SECRETSHARING_DecryptionHandle *s
330 = GNUNET_new (struct GNUNET_SECRETSHARING_DecryptionHandle);
331 struct GNUNET_MQ_MessageHandler mq_handlers[] = {
332 GNUNET_MQ_hd_fixed_size (decrypt_done,
333 GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT_DONE,
334 struct GNUNET_SECRETSHARING_DecryptResponseMessage,
335 s),
336 GNUNET_MQ_handler_end ()
337 };
338 struct GNUNET_MQ_Envelope *ev;
339 struct GNUNET_SECRETSHARING_DecryptRequestMessage *msg;
340 size_t share_size;
341
342 s->decrypt_cb = decrypt_cb;
343 s->decrypt_cls = decrypt_cb_cls;
344 s->mq = GNUNET_CLIENT_connect (cfg,
345 "secretsharing",
346 mq_handlers,
347 &handle_decrypt_client_error,
348 s);
349 if (NULL == s->mq)
350 {
351 GNUNET_free (s);
352 return NULL;
353 }
354 GNUNET_assert (GNUNET_OK ==
355 GNUNET_SECRETSHARING_share_write (share, NULL, 0,
356 &share_size));
357
358 ev = GNUNET_MQ_msg_extra (msg,
359 share_size,
360 GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT);
361
362 GNUNET_assert (GNUNET_OK ==
363 GNUNET_SECRETSHARING_share_write (share,
364 &msg[1],
365 share_size,
366 NULL));
367
368 msg->start = GNUNET_TIME_absolute_hton (start);
369 msg->deadline = GNUNET_TIME_absolute_hton (deadline);
370 msg->ciphertext = *ciphertext;
371
372 GNUNET_MQ_send (s->mq, ev);
373
374 LOG (GNUNET_ERROR_TYPE_DEBUG,
375 "decrypt session created\n");
376 return s;
377}
378
379
380int
381GNUNET_SECRETSHARING_plaintext_generate_i (struct
382 GNUNET_SECRETSHARING_Plaintext *
383 plaintext,
384 int64_t exponent)
385{
386 int negative;
387 gcry_mpi_t x;
388
389 ensure_elgamal_initialized ();
390
391 GNUNET_assert (NULL != (x = gcry_mpi_new (0)));
392
393 negative = GNUNET_NO;
394 if (exponent < 0)
395 {
396 negative = GNUNET_YES;
397 exponent = -exponent;
398 }
399
400 gcry_mpi_set_ui (x, exponent);
401
402 gcry_mpi_powm (x, elgamal_g, x, elgamal_p);
403
404 if (GNUNET_YES == negative)
405 {
406 int res;
407 res = gcry_mpi_invm (x, x, elgamal_p);
408 if (0 == res)
409 return GNUNET_SYSERR;
410 }
411
412 GNUNET_CRYPTO_mpi_print_unsigned (plaintext,
413 sizeof(struct
414 GNUNET_SECRETSHARING_Plaintext),
415 x);
416
417 return GNUNET_OK;
418}
419
420
421int
422GNUNET_SECRETSHARING_encrypt (const struct
423 GNUNET_SECRETSHARING_PublicKey *public_key,
424 const struct
425 GNUNET_SECRETSHARING_Plaintext *plaintext,
426 struct GNUNET_SECRETSHARING_Ciphertext *
427 result_ciphertext)
428{
429 /* pubkey */
430 gcry_mpi_t h;
431 /* nonce */
432 gcry_mpi_t y;
433 /* plaintext message */
434 gcry_mpi_t m;
435 /* temp value */
436 gcry_mpi_t tmp;
437
438 ensure_elgamal_initialized ();
439
440 GNUNET_assert (NULL != (h = gcry_mpi_new (0)));
441 GNUNET_assert (NULL != (y = gcry_mpi_new (0)));
442 GNUNET_assert (NULL != (tmp = gcry_mpi_new (0)));
443
444 GNUNET_CRYPTO_mpi_scan_unsigned (&h, public_key, sizeof *public_key);
445 GNUNET_CRYPTO_mpi_scan_unsigned (&m, plaintext, sizeof *plaintext);
446
447 // Randomize y such that 0 < y < elgamal_q.
448 // The '- 1' is necessary as bitlength(q) = bitlength(p) - 1.
449 do
450 {
451 gcry_mpi_randomize (y, GNUNET_SECRETSHARING_ELGAMAL_BITS - 1,
452 GCRY_WEAK_RANDOM);
453 }
454 while ((gcry_mpi_cmp_ui (y, 0) == 0) || (gcry_mpi_cmp (y, elgamal_q) >= 0));
455
456 // tmp <- g^y
457 gcry_mpi_powm (tmp, elgamal_g, y, elgamal_p);
458 // write tmp to c1
459 GNUNET_CRYPTO_mpi_print_unsigned (&result_ciphertext->c1_bits,
460 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, tmp);
461
462 // tmp <- h^y
463 gcry_mpi_powm (tmp, h, y, elgamal_p);
464 // tmp <- tmp * m
465 gcry_mpi_mulm (tmp, tmp, m, elgamal_p);
466 // write tmp to c2
467 GNUNET_CRYPTO_mpi_print_unsigned (&result_ciphertext->c2_bits,
468 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, tmp);
469
470 return GNUNET_OK;
471}
472
473
474/**
475 * Cancel a decryption.
476 *
477 * The decrypt_cb is not called anymore, but the calling
478 * peer may already have irrevocably contributed its share for the decryption of the value.
479 *
480 * @param dh to cancel
481 */
482void
483GNUNET_SECRETSHARING_decrypt_cancel (struct
484 GNUNET_SECRETSHARING_DecryptionHandle *dh)
485{
486 GNUNET_MQ_destroy (dh->mq);
487 dh->mq = NULL;
488 GNUNET_free (dh);
489}
490
491
492/* end of secretsharing_api.c */
diff --git a/src/contrib/service/secretsharing/secretsharing_common.c b/src/contrib/service/secretsharing/secretsharing_common.c
new file mode 100644
index 000000000..44b96b1c8
--- /dev/null
+++ b/src/contrib/service/secretsharing/secretsharing_common.c
@@ -0,0 +1,159 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21#include "platform.h"
22#include "secretsharing.h"
23
24/**
25 * Read a share from its binary representation.
26 *
27 * @param data Binary representation of the share.
28 * @param len Length of @a data.
29 * @param[out] readlen Number of bytes read,
30 * ignored if NULL.
31 * @return The share, or NULL on error.
32 */
33struct GNUNET_SECRETSHARING_Share *
34GNUNET_SECRETSHARING_share_read (const void *data,
35 size_t len,
36 size_t *readlen)
37{
38 struct GNUNET_SECRETSHARING_Share *share;
39 const struct GNUNET_SECRETSHARING_ShareHeaderNBO *sh = data;
40 const char *p;
41 size_t n;
42 uint16_t payload_size;
43
44 payload_size = ntohs (sh->num_peers)
45 * (sizeof(uint16_t) + sizeof(struct
46 GNUNET_SECRETSHARING_FieldElement)
47 + sizeof(struct GNUNET_PeerIdentity));
48
49 if (NULL != readlen)
50 *readlen = payload_size + sizeof *sh;
51
52 share = GNUNET_new (struct GNUNET_SECRETSHARING_Share);
53
54 share->threshold = ntohs (sh->threshold);
55 share->num_peers = ntohs (sh->num_peers);
56 share->my_peer = ntohs (sh->my_peer);
57
58 share->my_share = sh->my_share;
59 share->public_key = sh->public_key;
60
61 p = (const char *) &sh[1];
62
63 n = share->num_peers * sizeof(struct GNUNET_PeerIdentity);
64 share->peers = GNUNET_new_array (share->num_peers,
65 struct GNUNET_PeerIdentity);
66 GNUNET_memcpy (share->peers, p, n);
67 p += n;
68
69 n = share->num_peers * sizeof(struct GNUNET_SECRETSHARING_FieldElement);
70 share->sigmas = GNUNET_new_array (share->num_peers,
71 struct GNUNET_SECRETSHARING_FieldElement);
72 GNUNET_memcpy (share->sigmas, p, n);
73 p += n;
74
75 n = share->num_peers * sizeof(uint16_t);
76 share->original_indices = GNUNET_new_array (share->num_peers,
77 uint16_t);
78 GNUNET_memcpy (share->original_indices, p, n);
79
80 return share;
81}
82
83
84/**
85 * Convert a share to its binary representation.
86 * Can be called with a NULL @a buf to get the size of the share.
87 *
88 * @param share Share to write.
89 * @param buf Buffer to write to.
90 * @param buflen Number of writable bytes in @a buf.
91 * @param[out] writelen Pointer to store number of bytes written,
92 * ignored if NULL.
93 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure.
94 */
95int
96GNUNET_SECRETSHARING_share_write (const struct
97 GNUNET_SECRETSHARING_Share *share,
98 void *buf, size_t buflen, size_t *writelen)
99{
100 uint16_t payload_size;
101 struct GNUNET_SECRETSHARING_ShareHeaderNBO *sh;
102 char *p;
103 int n;
104
105 payload_size = share->num_peers
106 * (sizeof(uint16_t) + sizeof(struct
107 GNUNET_SECRETSHARING_FieldElement)
108 + sizeof(struct GNUNET_PeerIdentity));
109
110 if (NULL != writelen)
111 *writelen = payload_size + sizeof(struct
112 GNUNET_SECRETSHARING_ShareHeaderNBO);
113
114 /* just a query for the writelen */
115 if (buf == NULL)
116 return GNUNET_OK;
117
118 /* wrong buffer size */
119 if (buflen < payload_size + sizeof(struct
120 GNUNET_SECRETSHARING_ShareHeaderNBO))
121 return GNUNET_SYSERR;
122
123 sh = buf;
124
125 sh->threshold = htons (share->threshold);
126 sh->num_peers = htons (share->num_peers);
127 sh->my_peer = htons (share->my_peer);
128
129 sh->my_share = share->my_share;
130 sh->public_key = share->public_key;
131
132 p = (void *) &sh[1];
133
134 n = share->num_peers * sizeof(struct GNUNET_PeerIdentity);
135 GNUNET_memcpy (p, share->peers, n);
136 p += n;
137
138 n = share->num_peers * sizeof(struct GNUNET_SECRETSHARING_FieldElement);
139 GNUNET_memcpy (p, share->sigmas, n);
140 p += n;
141
142 n = share->num_peers * sizeof(uint16_t);
143 GNUNET_memcpy (p, share->original_indices, n);
144
145 return GNUNET_OK;
146}
147
148
149void
150GNUNET_SECRETSHARING_share_destroy (struct GNUNET_SECRETSHARING_Share *share)
151{
152 GNUNET_free (share->original_indices);
153 share->original_indices = NULL;
154 GNUNET_free (share->sigmas);
155 share->sigmas = NULL;
156 GNUNET_free (share->peers);
157 share->peers = NULL;
158 GNUNET_free (share);
159}
diff --git a/src/contrib/service/secretsharing/secretsharing_protocol.h b/src/contrib/service/secretsharing/secretsharing_protocol.h
new file mode 100644
index 000000000..e6f3ba286
--- /dev/null
+++ b/src/contrib/service/secretsharing/secretsharing_protocol.h
@@ -0,0 +1,147 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21
22/**
23 * @file secretsharing/secretsharing_protocol.h
24 * @brief p2p message definitions for secretsharing
25 * @author Florian Dold
26 */
27
28#ifndef GNUNET_SECRETSHARING_PROTOCOL_H
29#define GNUNET_SECRETSHARING_PROTOCOL_H
30
31#include "platform.h"
32#include "gnunet_common.h"
33#include "gnunet_protocols.h"
34#include "secretsharing.h"
35
36
37GNUNET_NETWORK_STRUCT_BEGIN
38
39
40/**
41 * Consensus element data used in the first round of key generation.
42 */
43struct GNUNET_SECRETSHARING_KeygenCommitData
44{
45 /**
46 * Signature over the rest of the message.
47 */
48 struct GNUNET_CRYPTO_EddsaSignature signature;
49 /**
50 * Signature purpose for signing the keygen commit data.
51 */
52 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
53 /**
54 * Peer that inserts this element.
55 */
56 struct GNUNET_PeerIdentity peer;
57 /**
58 * Ephemeral paillier public key used by 'peer' for
59 * this session.
60 */
61 struct GNUNET_CRYPTO_PaillierPublicKey pubkey;
62 /**
63 * Commitment of 'peer' to its presecret.
64 */
65 struct GNUNET_HashCode commitment GNUNET_PACKED;
66};
67
68
69struct GNUNET_SECRETSHARING_KeygenRevealData
70{
71 /**
72 * Signature over rest of the message.
73 */
74 struct GNUNET_CRYPTO_EddsaSignature signature;
75 /*
76 * Signature purpose for signing the keygen commit data.
77 */
78 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
79 /**
80 * Peer that inserts this element.
81 */
82 struct GNUNET_PeerIdentity peer;
83
84 /* values follow */
85};
86
87
88/**
89 * Data of then element put in consensus
90 * for decrypting a value.
91 */
92struct GNUNET_SECRETSHARING_DecryptData
93{
94 /*
95 * Signature over rest of the message.
96 */
97 struct GNUNET_CRYPTO_EddsaSignature signature;
98 /*
99 * Signature purpose for signing the keygen commit data.
100 */
101 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
102 /**
103 * Ciphertext we want to decrypt.
104 */
105 struct GNUNET_SECRETSHARING_Ciphertext ciphertext;
106 /**
107 * Peer that inserts this element.
108 */
109 struct GNUNET_PeerIdentity peer;
110 /**
111 * Partial decryption, computed as \f$c_1^{s_i}\f$
112 */
113 struct GNUNET_SECRETSHARING_FieldElement partial_decryption;
114 /**
115 * Commitment for the non-interactive zero knowledge proof.
116 * \f$g^\beta\f$, with \f$\beta < q\f$
117 */
118 struct GNUNET_SECRETSHARING_FieldElement nizk_commit1;
119 /**
120 * Commitment for the non-interactive zero knowledge proof.
121 * \f$c_1^\beta\f$, with \f$\beta < q\f$
122 */
123 struct GNUNET_SECRETSHARING_FieldElement nizk_commit2;
124 /**
125 * Response to the challenge computed from the protocol transcript.
126 * \f$r = \beta + challenge \cdot share_i\f$
127 */
128 struct GNUNET_SECRETSHARING_FieldElement nizk_response;
129};
130
131
132struct GNUNET_SECRETSHARING_FairEncryption
133{
134 struct GNUNET_CRYPTO_PaillierCiphertext c;
135 /**
136 * h = g^x, where x is the fairly encrypted secret.
137 */
138 char h[GNUNET_SECRETSHARING_ELGAMAL_BITS / 8];
139 char t1[GNUNET_SECRETSHARING_ELGAMAL_BITS / 8];
140 char t2[GNUNET_CRYPTO_PAILLIER_BITS * 2 / 8];
141 char z[GNUNET_SECRETSHARING_ELGAMAL_BITS / 8];
142 char w[GNUNET_CRYPTO_PAILLIER_BITS / 8];
143};
144
145GNUNET_NETWORK_STRUCT_END
146
147#endif
diff --git a/src/contrib/service/secretsharing/test_secretsharing.conf b/src/contrib/service/secretsharing/test_secretsharing.conf
new file mode 100644
index 000000000..9cbe3ebb1
--- /dev/null
+++ b/src/contrib/service/secretsharing/test_secretsharing.conf
@@ -0,0 +1,40 @@
1[secretsharing]
2START_ON_DEMAND = YES
3#PREFIX = valgrind --leak-check=full
4OPTIONS = -LINFO
5
6[consensus]
7START_ON_DEMAND = YES
8
9[transport]
10OPTIONS = -LERROR
11PLUGINS = unix
12
13[set]
14OPTIONS = -L INFO
15START_ON_DEMAND = YES
16#PREFIX = valgrind --leak-check=full
17
18[testbed]
19OVERLAY_TOPOLOGY = CLIQUE
20
21[hostlist]
22SERVERS =
23
24[nat]
25# Use addresses from the local network interfaces (including loopback, but also others)
26USE_LOCALADDR = YES
27
28# Disable IPv6 support
29DISABLEV6 = NO
30
31# Do we use addresses from localhost address ranges? (::1, 127.0.0.0/8)
32RETURN_LOCAL_ADDRESSES = YES
33
34[nse]
35START_ON_DEMAND = NO
36
37
38[rps]
39START_ON_DEMAND = NO
40IMMEDIATE_START = NO
diff --git a/src/contrib/service/secretsharing/test_secretsharing_api.c b/src/contrib/service/secretsharing/test_secretsharing_api.c
new file mode 100644
index 000000000..470bfddf4
--- /dev/null
+++ b/src/contrib/service/secretsharing/test_secretsharing_api.c
@@ -0,0 +1,103 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file secretsharing/test_secretsharing_api.c
23 * @brief testcase for the secretsharing api
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_testing_lib.h"
28#include "gnunet_secretsharing_service.h"
29
30
31static int success;
32
33static struct GNUNET_SECRETSHARING_Session *keygen;
34
35
36static void
37secret_ready_cb (void *cls,
38 struct GNUNET_SECRETSHARING_Share *my_share,
39 struct GNUNET_SECRETSHARING_PublicKey *public_key,
40 unsigned int num_ready_peers,
41 const struct GNUNET_PeerIdentity *ready_peers)
42{
43 keygen = NULL;
44 if (num_ready_peers == 1)
45 success = 1;
46 // FIXME: check that our share is valid, which we can do as there's only
47 // one peer.
48 GNUNET_SECRETSHARING_share_destroy (my_share);
49 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "secret ready, shutting down\n");
50 GNUNET_SCHEDULER_shutdown ();
51}
52
53
54static void
55handle_shutdown (void *cls)
56{
57 if (NULL != keygen)
58 {
59 GNUNET_SECRETSHARING_session_destroy (keygen);
60 keygen = NULL;
61 }
62}
63
64
65static void
66run (void *cls,
67 const struct GNUNET_CONFIGURATION_Handle *cfg,
68 struct GNUNET_TESTING_Peer *peer)
69{
70 struct GNUNET_HashCode session_id;
71 struct GNUNET_TIME_Absolute start;
72 struct GNUNET_TIME_Absolute deadline;
73
74 GNUNET_SCHEDULER_add_shutdown (&handle_shutdown, NULL);
75
76 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "testing secretsharing api\n");
77
78 GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, &session_id);
79
80 start = GNUNET_TIME_absolute_get ();
81 deadline = GNUNET_TIME_absolute_add (start, GNUNET_TIME_UNIT_SECONDS);
82
83 keygen = GNUNET_SECRETSHARING_create_session (cfg,
84 0, NULL, /* only the local peer */
85 &session_id,
86 start, deadline,
87 1,
88 secret_ready_cb, NULL);
89}
90
91
92int
93main (int argc, char **argv)
94{
95 int ret;
96
97 ret = GNUNET_TESTING_peer_run ("test_secretsharing_api",
98 "test_secretsharing.conf",
99 &run, NULL);
100 if (0 != ret)
101 return ret;
102 return (GNUNET_YES == success) ? 0 : 1;
103}
diff --git a/src/contrib/service/set/.gitignore b/src/contrib/service/set/.gitignore
new file mode 100644
index 000000000..f1c958639
--- /dev/null
+++ b/src/contrib/service/set/.gitignore
@@ -0,0 +1,7 @@
1gnunet-set-profiler
2gnunet-service-set
3gnunet-set-ibf-profiler
4test_set_api
5test_set_intersection_result_full
6test_set_union_copy
7test_set_union_result_symmetric
diff --git a/src/contrib/service/set/Makefile.am b/src/contrib/service/set/Makefile.am
new file mode 100644
index 000000000..88d4d39ad
--- /dev/null
+++ b/src/contrib/service/set/Makefile.am
@@ -0,0 +1,122 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4pkgcfgdir= $(pkgdatadir)/config.d/
5
6libexecdir= $(pkglibdir)/libexec/
7
8plugindir = $(libdir)/gnunet
9
10pkgcfg_DATA = \
11 set.conf
12
13if USE_COVERAGE
14 AM_CFLAGS = -fprofile-arcs -ftest-coverage
15endif
16
17noinst_PROGRAMS = \
18 gnunet-set-ibf-profiler \
19 gnunet-set-profiler
20
21libexec_PROGRAMS = \
22 gnunet-service-set
23
24lib_LTLIBRARIES = \
25 libgnunetset.la
26
27gnunet_set_profiler_SOURCES = \
28 gnunet-set-profiler.c
29gnunet_set_profiler_LDADD = \
30 $(top_builddir)/src/lib/util/libgnunetutil.la \
31 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
32 libgnunetset.la \
33 $(top_builddir)/src/service/testing/libgnunettesting.la \
34 $(GN_LIBINTL)
35
36
37gnunet_set_ibf_profiler_SOURCES = \
38 gnunet-set-ibf-profiler.c \
39 ibf.c
40gnunet_set_ibf_profiler_LDADD = \
41 $(top_builddir)/src/lib/util/libgnunetutil.la \
42 $(GN_LIBINTL)
43
44gnunet_service_set_SOURCES = \
45 gnunet-service-set.c gnunet-service-set.h \
46 gnunet-service-set_union.c gnunet-service-set_union.h \
47 gnunet-service-set_intersection.c gnunet-service-set_intersection.h \
48 ibf.c ibf.h \
49 gnunet-service-set_union_strata_estimator.c gnunet-service-set_union_strata_estimator.h \
50 gnunet-service-set_protocol.h
51gnunet_service_set_LDADD = \
52 $(top_builddir)/src/lib/util/libgnunetutil.la \
53 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
54 $(top_builddir)/src/service/core/libgnunetcore.la \
55 $(top_builddir)/src/service/cadet/libgnunetcadet.la \
56 $(top_builddir)/src/lib/block/libgnunetblock.la \
57 libgnunetset.la \
58 $(GN_LIBINTL)
59
60libgnunetset_la_SOURCES = \
61 set_api.c set.h
62libgnunetset_la_LIBADD = \
63 $(top_builddir)/src/lib/util/libgnunetutil.la \
64 $(LTLIBINTL)
65libgnunetset_la_LDFLAGS = \
66 $(GN_LIB_LDFLAGS)
67
68check_PROGRAMS = \
69 # test_set_api \
70 # test_set_union_result_symmetric \
71 # test_set_intersection_result_full \
72 test_set_union_copy
73
74if ENABLE_TEST_RUN
75AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
76TESTS = $(check_PROGRAMS)
77endif
78
79test_set_api_SOURCES = \
80 test_set_api.c
81test_set_api_LDADD = \
82 $(top_builddir)/src/lib/util/libgnunetutil.la \
83 $(top_builddir)/src/service/testing/libgnunettesting.la \
84 libgnunetset.la
85
86test_set_union_result_symmetric_SOURCES = \
87 test_set_union_result_symmetric.c
88test_set_union_result_symmetric_LDADD = \
89 $(top_builddir)/src/lib/util/libgnunetutil.la \
90 $(top_builddir)/src/service/testing/libgnunettesting.la \
91 libgnunetset.la
92
93test_set_intersection_result_full_SOURCES = \
94 test_set_intersection_result_full.c
95test_set_intersection_result_full_LDADD = \
96 $(top_builddir)/src/lib/util/libgnunetutil.la \
97 $(top_builddir)/src/service/testing/libgnunettesting.la \
98 libgnunetset.la
99
100test_set_union_copy_SOURCES = \
101 test_set_union_copy.c
102test_set_union_copy_LDADD = \
103 $(top_builddir)/src/lib/util/libgnunetutil.la \
104 $(top_builddir)/src/service/testing/libgnunettesting.la \
105 libgnunetset.la
106
107plugin_LTLIBRARIES = \
108 libgnunet_plugin_block_set_test.la
109
110libgnunet_plugin_block_set_test_la_SOURCES = \
111 plugin_block_set_test.c
112libgnunet_plugin_block_set_test_la_LIBADD = \
113 $(top_builddir)/src/lib/block/libgnunetblock.la \
114 $(top_builddir)/src/lib/block/libgnunetblockgroup.la \
115 $(top_builddir)/src/lib/util/libgnunetutil.la \
116 $(LTLIBINTL)
117libgnunet_plugin_block_set_test_la_LDFLAGS = \
118 $(GN_PLUGIN_LDFLAGS)
119
120
121EXTRA_DIST = \
122 test_set.conf
diff --git a/src/contrib/service/set/gnunet-service-set.c b/src/contrib/service/set/gnunet-service-set.c
new file mode 100644
index 000000000..7c522ec34
--- /dev/null
+++ b/src/contrib/service/set/gnunet-service-set.c
@@ -0,0 +1,1983 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013-2017 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file set/gnunet-service-set.c
22 * @brief two-peer set operations
23 * @author Florian Dold
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet-service-set.h"
28#include "gnunet-service-set_union.h"
29#include "gnunet-service-set_intersection.h"
30#include "gnunet-service-set_protocol.h"
31#include "gnunet_statistics_service.h"
32
33/**
34 * How long do we hold on to an incoming channel if there is
35 * no local listener before giving up?
36 */
37#define INCOMING_CHANNEL_TIMEOUT GNUNET_TIME_UNIT_MINUTES
38
39
40/**
41 * Lazy copy requests made by a client.
42 */
43struct LazyCopyRequest
44{
45 /**
46 * Kept in a DLL.
47 */
48 struct LazyCopyRequest *prev;
49
50 /**
51 * Kept in a DLL.
52 */
53 struct LazyCopyRequest *next;
54
55 /**
56 * Which set are we supposed to copy?
57 */
58 struct Set *source_set;
59
60 /**
61 * Cookie identifying the request.
62 */
63 uint32_t cookie;
64};
65
66
67/**
68 * A listener is inhabited by a client, and waits for evaluation
69 * requests from remote peers.
70 */
71struct Listener
72{
73 /**
74 * Listeners are held in a doubly linked list.
75 */
76 struct Listener *next;
77
78 /**
79 * Listeners are held in a doubly linked list.
80 */
81 struct Listener *prev;
82
83 /**
84 * Head of DLL of operations this listener is responsible for.
85 * Once the client has accepted/declined the operation, the
86 * operation is moved to the respective set's operation DLLS.
87 */
88 struct Operation *op_head;
89
90 /**
91 * Tail of DLL of operations this listener is responsible for.
92 * Once the client has accepted/declined the operation, the
93 * operation is moved to the respective set's operation DLLS.
94 */
95 struct Operation *op_tail;
96
97 /**
98 * Client that owns the listener.
99 * Only one client may own a listener.
100 */
101 struct ClientState *cs;
102
103 /**
104 * The port we are listening on with CADET.
105 */
106 struct GNUNET_CADET_Port *open_port;
107
108 /**
109 * Application ID for the operation, used to distinguish
110 * multiple operations of the same type with the same peer.
111 */
112 struct GNUNET_HashCode app_id;
113
114 /**
115 * The type of the operation.
116 */
117 enum GNUNET_SET_OperationType operation;
118};
119
120
121/**
122 * Handle to the cadet service, used to listen for and connect to
123 * remote peers.
124 */
125static struct GNUNET_CADET_Handle *cadet;
126
127/**
128 * DLL of lazy copy requests by this client.
129 */
130static struct LazyCopyRequest *lazy_copy_head;
131
132/**
133 * DLL of lazy copy requests by this client.
134 */
135static struct LazyCopyRequest *lazy_copy_tail;
136
137/**
138 * Generator for unique cookie we set per lazy copy request.
139 */
140static uint32_t lazy_copy_cookie;
141
142/**
143 * Statistics handle.
144 */
145struct GNUNET_STATISTICS_Handle *_GSS_statistics;
146
147/**
148 * Listeners are held in a doubly linked list.
149 */
150static struct Listener *listener_head;
151
152/**
153 * Listeners are held in a doubly linked list.
154 */
155static struct Listener *listener_tail;
156
157/**
158 * Number of active clients.
159 */
160static unsigned int num_clients;
161
162/**
163 * Are we in shutdown? if #GNUNET_YES and the number of clients
164 * drops to zero, disconnect from CADET.
165 */
166static int in_shutdown;
167
168/**
169 * Counter for allocating unique IDs for clients, used to identify
170 * incoming operation requests from remote peers, that the client can
171 * choose to accept or refuse. 0 must not be used (reserved for
172 * uninitialized).
173 */
174static uint32_t suggest_id;
175
176
177/**
178 * Get the incoming socket associated with the given id.
179 *
180 * @param id id to look for
181 * @return the incoming socket associated with the id,
182 * or NULL if there is none
183 */
184static struct Operation *
185get_incoming (uint32_t id)
186{
187 for (struct Listener *listener = listener_head; NULL != listener;
188 listener = listener->next)
189 {
190 for (struct Operation *op = listener->op_head; NULL != op; op = op->next)
191 if (op->suggest_id == id)
192 return op;
193 }
194 return NULL;
195}
196
197
198/**
199 * Destroy an incoming request from a remote peer
200 *
201 * @param op remote request to destroy
202 */
203static void
204incoming_destroy (struct Operation *op)
205{
206 struct Listener *listener;
207
208 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
209 "Destroying incoming operation %p\n",
210 op);
211 if (NULL != (listener = op->listener))
212 {
213 GNUNET_CONTAINER_DLL_remove (listener->op_head, listener->op_tail, op);
214 op->listener = NULL;
215 }
216 if (NULL != op->timeout_task)
217 {
218 GNUNET_SCHEDULER_cancel (op->timeout_task);
219 op->timeout_task = NULL;
220 }
221 _GSS_operation_destroy2 (op);
222}
223
224
225/**
226 * Context for the #garbage_collect_cb().
227 */
228struct GarbageContext
229{
230 /**
231 * Map for which we are garbage collecting removed elements.
232 */
233 struct GNUNET_CONTAINER_MultiHashMap *map;
234
235 /**
236 * Lowest generation for which an operation is still pending.
237 */
238 unsigned int min_op_generation;
239
240 /**
241 * Largest generation for which an operation is still pending.
242 */
243 unsigned int max_op_generation;
244};
245
246
247/**
248 * Function invoked to check if an element can be removed from
249 * the set's history because it is no longer needed.
250 *
251 * @param cls the `struct GarbageContext *`
252 * @param key key of the element in the map
253 * @param value the `struct ElementEntry *`
254 * @return #GNUNET_OK (continue to iterate)
255 */
256static int
257garbage_collect_cb (void *cls, const struct GNUNET_HashCode *key, void *value)
258{
259 // struct GarbageContext *gc = cls;
260 // struct ElementEntry *ee = value;
261
262 // if (GNUNET_YES != ee->removed)
263 // return GNUNET_OK;
264 // if ( (gc->max_op_generation < ee->generation_added) ||
265 // (ee->generation_removed > gc->min_op_generation) )
266 // {
267 // GNUNET_assert (GNUNET_YES ==
268 // GNUNET_CONTAINER_multihashmap_remove (gc->map,
269 // key,
270 // ee));
271 // GNUNET_free (ee);
272 // }
273 return GNUNET_OK;
274}
275
276
277/**
278 * Collect and destroy elements that are not needed anymore, because
279 * their lifetime (as determined by their generation) does not overlap
280 * with any active set operation.
281 *
282 * @param set set to garbage collect
283 */
284static void
285collect_generation_garbage (struct Set *set)
286{
287 struct GarbageContext gc;
288
289 gc.min_op_generation = UINT_MAX;
290 gc.max_op_generation = 0;
291 for (struct Operation *op = set->ops_head; NULL != op; op = op->next)
292 {
293 gc.min_op_generation =
294 GNUNET_MIN (gc.min_op_generation, op->generation_created);
295 gc.max_op_generation =
296 GNUNET_MAX (gc.max_op_generation, op->generation_created);
297 }
298 gc.map = set->content->elements;
299 GNUNET_CONTAINER_multihashmap_iterate (set->content->elements,
300 &garbage_collect_cb,
301 &gc);
302}
303
304
305/**
306 * Is @a generation in the range of exclusions?
307 *
308 * @param generation generation to query
309 * @param excluded array of generations where the element is excluded
310 * @param excluded_size length of the @a excluded array
311 * @return #GNUNET_YES if @a generation is in any of the ranges
312 */
313static int
314is_excluded_generation (unsigned int generation,
315 struct GenerationRange *excluded,
316 unsigned int excluded_size)
317{
318 for (unsigned int i = 0; i < excluded_size; i++)
319 if ((generation >= excluded[i].start) && (generation < excluded[i].end))
320 return GNUNET_YES;
321 return GNUNET_NO;
322}
323
324
325/**
326 * Is element @a ee part of the set during @a query_generation?
327 *
328 * @param ee element to test
329 * @param query_generation generation to query
330 * @param excluded array of generations where the element is excluded
331 * @param excluded_size length of the @a excluded array
332 * @return #GNUNET_YES if the element is in the set, #GNUNET_NO if not
333 */
334static int
335is_element_of_generation (struct ElementEntry *ee,
336 unsigned int query_generation,
337 struct GenerationRange *excluded,
338 unsigned int excluded_size)
339{
340 struct MutationEvent *mut;
341 int is_present;
342
343 GNUNET_assert (NULL != ee->mutations);
344 if (GNUNET_YES ==
345 is_excluded_generation (query_generation, excluded, excluded_size))
346 {
347 GNUNET_break (0);
348 return GNUNET_NO;
349 }
350
351 is_present = GNUNET_NO;
352
353 /* Could be made faster with binary search, but lists
354 are small, so why bother. */
355 for (unsigned int i = 0; i < ee->mutations_size; i++)
356 {
357 mut = &ee->mutations[i];
358
359 if (mut->generation > query_generation)
360 {
361 /* The mutation doesn't apply to our generation
362 anymore. We can'b break here, since mutations aren't
363 sorted by generation. */
364 continue;
365 }
366
367 if (GNUNET_YES ==
368 is_excluded_generation (mut->generation, excluded, excluded_size))
369 {
370 /* The generation is excluded (because it belongs to another
371 fork via a lazy copy) and thus mutations aren't considered
372 for membership testing. */
373 continue;
374 }
375
376 /* This would be an inconsistency in how we manage mutations. */
377 if ((GNUNET_YES == is_present) && (GNUNET_YES == mut->added))
378 GNUNET_assert (0);
379 /* Likewise. */
380 if ((GNUNET_NO == is_present) && (GNUNET_NO == mut->added))
381 GNUNET_assert (0);
382
383 is_present = mut->added;
384 }
385
386 return is_present;
387}
388
389
390/**
391 * Is element @a ee part of the set used by @a op?
392 *
393 * @param ee element to test
394 * @param op operation the defines the set and its generation
395 * @return #GNUNET_YES if the element is in the set, #GNUNET_NO if not
396 */
397int
398_GSS_is_element_of_operation (struct ElementEntry *ee, struct Operation *op)
399{
400 return is_element_of_generation (ee,
401 op->generation_created,
402 op->set->excluded_generations,
403 op->set->excluded_generations_size);
404}
405
406
407/**
408 * Destroy the given operation. Used for any operation where both
409 * peers were known and that thus actually had a vt and channel. Must
410 * not be used for operations where 'listener' is still set and we do
411 * not know the other peer.
412 *
413 * Call the implementation-specific cancel function of the operation.
414 * Disconnects from the remote peer. Does not disconnect the client,
415 * as there may be multiple operations per set.
416 *
417 * @param op operation to destroy
418 * @param gc #GNUNET_YES to perform garbage collection on the set
419 */
420void
421_GSS_operation_destroy (struct Operation *op, int gc)
422{
423 struct Set *set = op->set;
424 struct GNUNET_CADET_Channel *channel;
425
426 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Destroying operation %p\n", op);
427 GNUNET_assert (NULL == op->listener);
428 if (NULL != op->state)
429 {
430 set->vt->cancel (op);
431 op->state = NULL;
432 }
433 if (NULL != set)
434 {
435 GNUNET_CONTAINER_DLL_remove (set->ops_head, set->ops_tail, op);
436 op->set = NULL;
437 }
438 if (NULL != op->context_msg)
439 {
440 GNUNET_free (op->context_msg);
441 op->context_msg = NULL;
442 }
443 if (NULL != (channel = op->channel))
444 {
445 /* This will free op; called conditionally as this helper function
446 is also called from within the channel disconnect handler. */
447 op->channel = NULL;
448 GNUNET_CADET_channel_destroy (channel);
449 }
450 if ((NULL != set) && (GNUNET_YES == gc))
451 collect_generation_garbage (set);
452 /* We rely on the channel end handler to free 'op'. When 'op->channel' was NULL,
453 * there was a channel end handler that will free 'op' on the call stack. */
454}
455
456
457/**
458 * Callback called when a client connects to the service.
459 *
460 * @param cls closure for the service
461 * @param c the new client that connected to the service
462 * @param mq the message queue used to send messages to the client
463 * @return @a `struct ClientState`
464 */
465static void *
466client_connect_cb (void *cls,
467 struct GNUNET_SERVICE_Client *c,
468 struct GNUNET_MQ_Handle *mq)
469{
470 struct ClientState *cs;
471
472 num_clients++;
473 cs = GNUNET_new (struct ClientState);
474 cs->client = c;
475 cs->mq = mq;
476 return cs;
477}
478
479
480/**
481 * Iterator over hash map entries to free element entries.
482 *
483 * @param cls closure
484 * @param key current key code
485 * @param value a `struct ElementEntry *` to be free'd
486 * @return #GNUNET_YES (continue to iterate)
487 */
488static int
489destroy_elements_iterator (void *cls,
490 const struct GNUNET_HashCode *key,
491 void *value)
492{
493 struct ElementEntry *ee = value;
494
495 GNUNET_free (ee->mutations);
496 GNUNET_free (ee);
497 return GNUNET_YES;
498}
499
500
501/**
502 * Clean up after a client has disconnected
503 *
504 * @param cls closure, unused
505 * @param client the client to clean up after
506 * @param internal_cls the `struct ClientState`
507 */
508static void
509client_disconnect_cb (void *cls,
510 struct GNUNET_SERVICE_Client *client,
511 void *internal_cls)
512{
513 struct ClientState *cs = internal_cls;
514 struct Operation *op;
515 struct Listener *listener;
516 struct Set *set;
517
518 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client disconnected, cleaning up\n");
519 if (NULL != (set = cs->set))
520 {
521 struct SetContent *content = set->content;
522 struct PendingMutation *pm;
523 struct PendingMutation *pm_current;
524 struct LazyCopyRequest *lcr;
525
526 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Destroying client's set\n");
527 /* Destroy pending set operations */
528 while (NULL != set->ops_head)
529 _GSS_operation_destroy (set->ops_head, GNUNET_NO);
530
531 /* Destroy operation-specific state */
532 GNUNET_assert (NULL != set->state);
533 set->vt->destroy_set (set->state);
534 set->state = NULL;
535
536 /* Clean up ongoing iterations */
537 if (NULL != set->iter)
538 {
539 GNUNET_CONTAINER_multihashmap_iterator_destroy (set->iter);
540 set->iter = NULL;
541 set->iteration_id++;
542 }
543
544 /* discard any pending mutations that reference this set */
545 pm = content->pending_mutations_head;
546 while (NULL != pm)
547 {
548 pm_current = pm;
549 pm = pm->next;
550 if (pm_current->set == set)
551 {
552 GNUNET_CONTAINER_DLL_remove (content->pending_mutations_head,
553 content->pending_mutations_tail,
554 pm_current);
555 GNUNET_free (pm_current);
556 }
557 }
558
559 /* free set content (or at least decrement RC) */
560 set->content = NULL;
561 GNUNET_assert (0 != content->refcount);
562 content->refcount--;
563 if (0 == content->refcount)
564 {
565 GNUNET_assert (NULL != content->elements);
566 GNUNET_CONTAINER_multihashmap_iterate (content->elements,
567 &destroy_elements_iterator,
568 NULL);
569 GNUNET_CONTAINER_multihashmap_destroy (content->elements);
570 content->elements = NULL;
571 GNUNET_free (content);
572 }
573 GNUNET_free (set->excluded_generations);
574 set->excluded_generations = NULL;
575
576 /* remove set from pending copy requests */
577 lcr = lazy_copy_head;
578 while (NULL != lcr)
579 {
580 struct LazyCopyRequest *lcr_current = lcr;
581
582 lcr = lcr->next;
583 if (lcr_current->source_set == set)
584 {
585 GNUNET_CONTAINER_DLL_remove (lazy_copy_head,
586 lazy_copy_tail,
587 lcr_current);
588 GNUNET_free (lcr_current);
589 }
590 }
591 GNUNET_free (set);
592 }
593
594 if (NULL != (listener = cs->listener))
595 {
596 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Destroying client's listener\n");
597 GNUNET_CADET_close_port (listener->open_port);
598 listener->open_port = NULL;
599 while (NULL != (op = listener->op_head))
600 {
601 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
602 "Destroying incoming operation `%u' from peer `%s'\n",
603 (unsigned int) op->client_request_id,
604 GNUNET_i2s (&op->peer));
605 incoming_destroy (op);
606 }
607 GNUNET_CONTAINER_DLL_remove (listener_head, listener_tail, listener);
608 GNUNET_free (listener);
609 }
610 GNUNET_free (cs);
611 num_clients--;
612 if ((GNUNET_YES == in_shutdown) && (0 == num_clients))
613 {
614 if (NULL != cadet)
615 {
616 GNUNET_CADET_disconnect (cadet);
617 cadet = NULL;
618 }
619 }
620}
621
622
623/**
624 * Check a request for a set operation from another peer.
625 *
626 * @param cls the operation state
627 * @param msg the received message
628 * @return #GNUNET_OK if the channel should be kept alive,
629 * #GNUNET_SYSERR to destroy the channel
630 */
631static int
632check_incoming_msg (void *cls, const struct OperationRequestMessage *msg)
633{
634 struct Operation *op = cls;
635 struct Listener *listener = op->listener;
636 const struct GNUNET_MessageHeader *nested_context;
637
638 /* double operation request */
639 if (0 != op->suggest_id)
640 {
641 GNUNET_break_op (0);
642 return GNUNET_SYSERR;
643 }
644 /* This should be equivalent to the previous condition, but can't hurt to check twice */
645 if (NULL == op->listener)
646 {
647 GNUNET_break (0);
648 return GNUNET_SYSERR;
649 }
650 if (listener->operation !=
651 (enum GNUNET_SET_OperationType) ntohl (msg->operation))
652 {
653 GNUNET_break_op (0);
654 return GNUNET_SYSERR;
655 }
656 nested_context = GNUNET_MQ_extract_nested_mh (msg);
657 if ((NULL != nested_context) &&
658 (ntohs (nested_context->size) > GNUNET_SET_CONTEXT_MESSAGE_MAX_SIZE))
659 {
660 GNUNET_break_op (0);
661 return GNUNET_SYSERR;
662 }
663 return GNUNET_OK;
664}
665
666
667/**
668 * Handle a request for a set operation from another peer. Checks if we
669 * have a listener waiting for such a request (and in that case initiates
670 * asking the listener about accepting the connection). If no listener
671 * is waiting, we queue the operation request in hope that a listener
672 * shows up soon (before timeout).
673 *
674 * This msg is expected as the first and only msg handled through the
675 * non-operation bound virtual table, acceptance of this operation replaces
676 * our virtual table and subsequent msgs would be routed differently (as
677 * we then know what type of operation this is).
678 *
679 * @param cls the operation state
680 * @param msg the received message
681 * @return #GNUNET_OK if the channel should be kept alive,
682 * #GNUNET_SYSERR to destroy the channel
683 */
684static void
685handle_incoming_msg (void *cls, const struct OperationRequestMessage *msg)
686{
687 struct Operation *op = cls;
688 struct Listener *listener = op->listener;
689 const struct GNUNET_MessageHeader *nested_context;
690 struct GNUNET_MQ_Envelope *env;
691 struct GNUNET_SET_RequestMessage *cmsg;
692
693 nested_context = GNUNET_MQ_extract_nested_mh (msg);
694 /* Make a copy of the nested_context (application-specific context
695 information that is opaque to set) so we can pass it to the
696 listener later on */
697 if (NULL != nested_context)
698 op->context_msg = GNUNET_copy_message (nested_context);
699 op->remote_element_count = ntohl (msg->element_count);
700 GNUNET_log (
701 GNUNET_ERROR_TYPE_DEBUG,
702 "Received P2P operation request (op %u, port %s) for active listener\n",
703 (uint32_t) ntohl (msg->operation),
704 GNUNET_h2s (&op->listener->app_id));
705 GNUNET_assert (0 == op->suggest_id);
706 if (0 == suggest_id)
707 suggest_id++;
708 op->suggest_id = suggest_id++;
709 GNUNET_assert (NULL != op->timeout_task);
710 GNUNET_SCHEDULER_cancel (op->timeout_task);
711 op->timeout_task = NULL;
712 env = GNUNET_MQ_msg_nested_mh (cmsg,
713 GNUNET_MESSAGE_TYPE_SET_REQUEST,
714 op->context_msg);
715 GNUNET_log (
716 GNUNET_ERROR_TYPE_DEBUG,
717 "Suggesting incoming request with accept id %u to listener %p of client %p\n",
718 op->suggest_id,
719 listener,
720 listener->cs);
721 cmsg->accept_id = htonl (op->suggest_id);
722 cmsg->peer_id = op->peer;
723 GNUNET_MQ_send (listener->cs->mq, env);
724 /* NOTE: GNUNET_CADET_receive_done() will be called in
725 #handle_client_accept() */
726}
727
728
729/**
730 * Add an element to @a set as specified by @a msg
731 *
732 * @param set set to manipulate
733 * @param msg message specifying the change
734 */
735static void
736execute_add (struct Set *set, const struct GNUNET_SET_ElementMessage *msg)
737{
738 struct GNUNET_SET_Element el;
739 struct ElementEntry *ee;
740 struct GNUNET_HashCode hash;
741
742 GNUNET_assert (GNUNET_MESSAGE_TYPE_SET_ADD == ntohs (msg->header.type));
743 el.size = ntohs (msg->header.size) - sizeof(*msg);
744 el.data = &msg[1];
745 el.element_type = ntohs (msg->element_type);
746 GNUNET_SET_element_hash (&el, &hash);
747 ee = GNUNET_CONTAINER_multihashmap_get (set->content->elements, &hash);
748 if (NULL == ee)
749 {
750 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
751 "Client inserts element %s of size %u\n",
752 GNUNET_h2s (&hash),
753 el.size);
754 ee = GNUNET_malloc (el.size + sizeof(*ee));
755 ee->element.size = el.size;
756 GNUNET_memcpy (&ee[1], el.data, el.size);
757 ee->element.data = &ee[1];
758 ee->element.element_type = el.element_type;
759 ee->remote = GNUNET_NO;
760 ee->mutations = NULL;
761 ee->mutations_size = 0;
762 ee->element_hash = hash;
763 GNUNET_break (GNUNET_YES ==
764 GNUNET_CONTAINER_multihashmap_put (
765 set->content->elements,
766 &ee->element_hash,
767 ee,
768 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
769 }
770 else if (GNUNET_YES ==
771 is_element_of_generation (ee,
772 set->current_generation,
773 set->excluded_generations,
774 set->excluded_generations_size))
775 {
776 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
777 "Client inserted element %s of size %u twice (ignored)\n",
778 GNUNET_h2s (&hash),
779 el.size);
780
781 /* same element inserted twice */
782 return;
783 }
784
785 {
786 struct MutationEvent mut = { .generation = set->current_generation,
787 .added = GNUNET_YES };
788 GNUNET_array_append (ee->mutations, ee->mutations_size, mut);
789 }
790 set->vt->add (set->state, ee);
791}
792
793
794/**
795 * Remove an element from @a set as specified by @a msg
796 *
797 * @param set set to manipulate
798 * @param msg message specifying the change
799 */
800static void
801execute_remove (struct Set *set, const struct GNUNET_SET_ElementMessage *msg)
802{
803 struct GNUNET_SET_Element el;
804 struct ElementEntry *ee;
805 struct GNUNET_HashCode hash;
806
807 GNUNET_assert (GNUNET_MESSAGE_TYPE_SET_REMOVE == ntohs (msg->header.type));
808 el.size = ntohs (msg->header.size) - sizeof(*msg);
809 el.data = &msg[1];
810 el.element_type = ntohs (msg->element_type);
811 GNUNET_SET_element_hash (&el, &hash);
812 ee = GNUNET_CONTAINER_multihashmap_get (set->content->elements, &hash);
813 if (NULL == ee)
814 {
815 /* Client tried to remove non-existing element. */
816 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
817 "Client removes non-existing element of size %u\n",
818 el.size);
819 return;
820 }
821 if (GNUNET_NO == is_element_of_generation (ee,
822 set->current_generation,
823 set->excluded_generations,
824 set->excluded_generations_size))
825 {
826 /* Client tried to remove element twice */
827 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
828 "Client removed element of size %u twice (ignored)\n",
829 el.size);
830 return;
831 }
832 else
833 {
834 struct MutationEvent mut = { .generation = set->current_generation,
835 .added = GNUNET_NO };
836
837 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
838 "Client removes element of size %u\n",
839 el.size);
840
841 GNUNET_array_append (ee->mutations, ee->mutations_size, mut);
842 }
843 set->vt->remove (set->state, ee);
844}
845
846
847/**
848 * Perform a mutation on a set as specified by the @a msg
849 *
850 * @param set the set to mutate
851 * @param msg specification of what to change
852 */
853static void
854execute_mutation (struct Set *set, const struct GNUNET_SET_ElementMessage *msg)
855{
856 switch (ntohs (msg->header.type))
857 {
858 case GNUNET_MESSAGE_TYPE_SET_ADD:
859 execute_add (set, msg);
860 break;
861
862 case GNUNET_MESSAGE_TYPE_SET_REMOVE:
863 execute_remove (set, msg);
864 break;
865
866 default:
867 GNUNET_break (0);
868 }
869}
870
871
872/**
873 * Execute mutations that were delayed on a set because of
874 * pending operations.
875 *
876 * @param set the set to execute mutations on
877 */
878static void
879execute_delayed_mutations (struct Set *set)
880{
881 struct PendingMutation *pm;
882
883 if (0 != set->content->iterator_count)
884 return; /* still cannot do this */
885 while (NULL != (pm = set->content->pending_mutations_head))
886 {
887 GNUNET_CONTAINER_DLL_remove (set->content->pending_mutations_head,
888 set->content->pending_mutations_tail,
889 pm);
890 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
891 "Executing pending mutation on %p.\n",
892 pm->set);
893 execute_mutation (pm->set, pm->msg);
894 GNUNET_free (pm->msg);
895 GNUNET_free (pm);
896 }
897}
898
899
900/**
901 * Send the next element of a set to the set's client. The next element is given by
902 * the set's current hashmap iterator. The set's iterator will be set to NULL if there
903 * are no more elements in the set. The caller must ensure that the set's iterator is
904 * valid.
905 *
906 * The client will acknowledge each received element with a
907 * #GNUNET_MESSAGE_TYPE_SET_ITER_ACK message. Our
908 * #handle_client_iter_ack() will then trigger the next transmission.
909 * Note that the #GNUNET_MESSAGE_TYPE_SET_ITER_DONE is not acknowledged.
910 *
911 * @param set set that should send its next element to its client
912 */
913static void
914send_client_element (struct Set *set)
915{
916 int ret;
917 struct ElementEntry *ee;
918 struct GNUNET_MQ_Envelope *ev;
919 struct GNUNET_SET_IterResponseMessage *msg;
920
921 GNUNET_assert (NULL != set->iter);
922 do
923 {
924 ret = GNUNET_CONTAINER_multihashmap_iterator_next (set->iter,
925 NULL,
926 (const void **) &ee);
927 if (GNUNET_NO == ret)
928 {
929 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Iteration on %p done.\n", set);
930 ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_ITER_DONE);
931 GNUNET_CONTAINER_multihashmap_iterator_destroy (set->iter);
932 set->iter = NULL;
933 set->iteration_id++;
934 GNUNET_assert (set->content->iterator_count > 0);
935 set->content->iterator_count--;
936 execute_delayed_mutations (set);
937 GNUNET_MQ_send (set->cs->mq, ev);
938 return;
939 }
940 GNUNET_assert (NULL != ee);
941 }
942 while (GNUNET_NO ==
943 is_element_of_generation (ee,
944 set->iter_generation,
945 set->excluded_generations,
946 set->excluded_generations_size));
947 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
948 "Sending iteration element on %p.\n",
949 set);
950 ev = GNUNET_MQ_msg_extra (msg,
951 ee->element.size,
952 GNUNET_MESSAGE_TYPE_SET_ITER_ELEMENT);
953 GNUNET_memcpy (&msg[1], ee->element.data, ee->element.size);
954 msg->element_type = htons (ee->element.element_type);
955 msg->iteration_id = htons (set->iteration_id);
956 GNUNET_MQ_send (set->cs->mq, ev);
957}
958
959
960/**
961 * Called when a client wants to iterate the elements of a set.
962 * Checks if we have a set associated with the client and if we
963 * can right now start an iteration. If all checks out, starts
964 * sending the elements of the set to the client.
965 *
966 * @param cls client that sent the message
967 * @param m message sent by the client
968 */
969static void
970handle_client_iterate (void *cls, const struct GNUNET_MessageHeader *m)
971{
972 struct ClientState *cs = cls;
973 struct Set *set;
974
975 if (NULL == (set = cs->set))
976 {
977 /* attempt to iterate over a non existing set */
978 GNUNET_break (0);
979 GNUNET_SERVICE_client_drop (cs->client);
980 return;
981 }
982 if (NULL != set->iter)
983 {
984 /* Only one concurrent iterate-action allowed per set */
985 GNUNET_break (0);
986 GNUNET_SERVICE_client_drop (cs->client);
987 return;
988 }
989 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
990 "Iterating set %p in gen %u with %u content elements\n",
991 (void *) set,
992 set->current_generation,
993 GNUNET_CONTAINER_multihashmap_size (set->content->elements));
994 GNUNET_SERVICE_client_continue (cs->client);
995 set->content->iterator_count++;
996 set->iter =
997 GNUNET_CONTAINER_multihashmap_iterator_create (set->content->elements);
998 set->iter_generation = set->current_generation;
999 send_client_element (set);
1000}
1001
1002
1003/**
1004 * Called when a client wants to create a new set. This is typically
1005 * the first request from a client, and includes the type of set
1006 * operation to be performed.
1007 *
1008 * @param cls client that sent the message
1009 * @param msg message sent by the client
1010 */
1011static void
1012handle_client_create_set (void *cls, const struct GNUNET_SET_CreateMessage *msg)
1013{
1014 struct ClientState *cs = cls;
1015 struct Set *set;
1016
1017 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1018 "Client created new set (operation %u)\n",
1019 (uint32_t) ntohl (msg->operation));
1020 if (NULL != cs->set)
1021 {
1022 /* There can only be one set per client */
1023 GNUNET_break (0);
1024 GNUNET_SERVICE_client_drop (cs->client);
1025 return;
1026 }
1027 set = GNUNET_new (struct Set);
1028 switch (ntohl (msg->operation))
1029 {
1030 case GNUNET_SET_OPERATION_INTERSECTION:
1031 set->vt = _GSS_intersection_vt ();
1032 break;
1033
1034 case GNUNET_SET_OPERATION_UNION:
1035 set->vt = _GSS_union_vt ();
1036 break;
1037
1038 default:
1039 GNUNET_free (set);
1040 GNUNET_break (0);
1041 GNUNET_SERVICE_client_drop (cs->client);
1042 return;
1043 }
1044 set->operation = (enum GNUNET_SET_OperationType) ntohl (msg->operation);
1045 set->state = set->vt->create ();
1046 if (NULL == set->state)
1047 {
1048 /* initialization failed (i.e. out of memory) */
1049 GNUNET_free (set);
1050 GNUNET_SERVICE_client_drop (cs->client);
1051 return;
1052 }
1053 set->content = GNUNET_new (struct SetContent);
1054 set->content->refcount = 1;
1055 set->content->elements = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
1056 set->cs = cs;
1057 cs->set = set;
1058 GNUNET_SERVICE_client_continue (cs->client);
1059}
1060
1061
1062/**
1063 * Timeout happens iff:
1064 * - we suggested an operation to our listener,
1065 * but did not receive a response in time
1066 * - we got the channel from a peer but no #GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST
1067 *
1068 * @param cls channel context
1069 */
1070static void
1071incoming_timeout_cb (void *cls)
1072{
1073 struct Operation *op = cls;
1074
1075 op->timeout_task = NULL;
1076 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1077 "Remote peer's incoming request timed out\n");
1078 incoming_destroy (op);
1079}
1080
1081
1082/**
1083 * Method called whenever another peer has added us to a channel the
1084 * other peer initiated. Only called (once) upon reception of data
1085 * from a channel we listen on.
1086 *
1087 * The channel context represents the operation itself and gets added
1088 * to a DLL, from where it gets looked up when our local listener
1089 * client responds to a proposed/suggested operation or connects and
1090 * associates with this operation.
1091 *
1092 * @param cls closure
1093 * @param channel new handle to the channel
1094 * @param source peer that started the channel
1095 * @return initial channel context for the channel
1096 * returns NULL on error
1097 */
1098static void *
1099channel_new_cb (void *cls,
1100 struct GNUNET_CADET_Channel *channel,
1101 const struct GNUNET_PeerIdentity *source)
1102{
1103 struct Listener *listener = cls;
1104 struct Operation *op;
1105
1106 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New incoming channel\n");
1107 op = GNUNET_new (struct Operation);
1108 op->listener = listener;
1109 op->peer = *source;
1110 op->channel = channel;
1111 op->mq = GNUNET_CADET_get_mq (op->channel);
1112 op->salt = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX);
1113 op->timeout_task = GNUNET_SCHEDULER_add_delayed (INCOMING_CHANNEL_TIMEOUT,
1114 &incoming_timeout_cb,
1115 op);
1116 GNUNET_CONTAINER_DLL_insert (listener->op_head, listener->op_tail, op);
1117 return op;
1118}
1119
1120
1121/**
1122 * Function called whenever a channel is destroyed. Should clean up
1123 * any associated state. It must NOT call
1124 * GNUNET_CADET_channel_destroy() on the channel.
1125 *
1126 * The peer_disconnect function is part of a a virtual table set initially either
1127 * when a peer creates a new channel with us, or once we create
1128 * a new channel ourselves (evaluate).
1129 *
1130 * Once we know the exact type of operation (union/intersection), the vt is
1131 * replaced with an operation specific instance (_GSS_[op]_vt).
1132 *
1133 * @param channel_ctx place where local state associated
1134 * with the channel is stored
1135 * @param channel connection to the other end (henceforth invalid)
1136 */
1137static void
1138channel_end_cb (void *channel_ctx, const struct GNUNET_CADET_Channel *channel)
1139{
1140 struct Operation *op = channel_ctx;
1141
1142 op->channel = NULL;
1143 _GSS_operation_destroy2 (op);
1144}
1145
1146
1147/**
1148 * This function probably should not exist
1149 * and be replaced by inlining more specific
1150 * logic in the various places where it is called.
1151 */
1152void
1153_GSS_operation_destroy2 (struct Operation *op)
1154{
1155 struct GNUNET_CADET_Channel *channel;
1156
1157 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "channel_end_cb called\n");
1158 if (NULL != (channel = op->channel))
1159 {
1160 /* This will free op; called conditionally as this helper function
1161 is also called from within the channel disconnect handler. */
1162 op->channel = NULL;
1163 GNUNET_CADET_channel_destroy (channel);
1164 }
1165 if (NULL != op->listener)
1166 {
1167 incoming_destroy (op);
1168 return;
1169 }
1170 if (NULL != op->set)
1171 op->set->vt->channel_death (op);
1172 else
1173 _GSS_operation_destroy (op, GNUNET_YES);
1174 GNUNET_free (op);
1175}
1176
1177
1178/**
1179 * Function called whenever an MQ-channel's transmission window size changes.
1180 *
1181 * The first callback in an outgoing channel will be with a non-zero value
1182 * and will mean the channel is connected to the destination.
1183 *
1184 * For an incoming channel it will be called immediately after the
1185 * #GNUNET_CADET_ConnectEventHandler, also with a non-zero value.
1186 *
1187 * @param cls Channel closure.
1188 * @param channel Connection to the other end (henceforth invalid).
1189 * @param window_size New window size. If the is more messages than buffer size
1190 * this value will be negative..
1191 */
1192static void
1193channel_window_cb (void *cls,
1194 const struct GNUNET_CADET_Channel *channel,
1195 int window_size)
1196{
1197 /* FIXME: not implemented, we could do flow control here... */
1198}
1199
1200
1201/**
1202 * Called when a client wants to create a new listener.
1203 *
1204 * @param cls client that sent the message
1205 * @param msg message sent by the client
1206 */
1207static void
1208handle_client_listen (void *cls, const struct GNUNET_SET_ListenMessage *msg)
1209{
1210 struct ClientState *cs = cls;
1211 struct GNUNET_MQ_MessageHandler cadet_handlers[] =
1212 { GNUNET_MQ_hd_var_size (incoming_msg,
1213 GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST,
1214 struct OperationRequestMessage,
1215 NULL),
1216 GNUNET_MQ_hd_var_size (union_p2p_ibf,
1217 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_IBF,
1218 struct IBFMessage,
1219 NULL),
1220 GNUNET_MQ_hd_var_size (union_p2p_elements,
1221 GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENTS,
1222 struct GNUNET_SET_ElementMessage,
1223 NULL),
1224 GNUNET_MQ_hd_var_size (union_p2p_offer,
1225 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_OFFER,
1226 struct GNUNET_MessageHeader,
1227 NULL),
1228 GNUNET_MQ_hd_var_size (union_p2p_inquiry,
1229 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_INQUIRY,
1230 struct InquiryMessage,
1231 NULL),
1232 GNUNET_MQ_hd_var_size (union_p2p_demand,
1233 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DEMAND,
1234 struct GNUNET_MessageHeader,
1235 NULL),
1236 GNUNET_MQ_hd_fixed_size (union_p2p_done,
1237 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE,
1238 struct GNUNET_MessageHeader,
1239 NULL),
1240 GNUNET_MQ_hd_fixed_size (union_p2p_over,
1241 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_OVER,
1242 struct GNUNET_MessageHeader,
1243 NULL),
1244 GNUNET_MQ_hd_fixed_size (union_p2p_full_done,
1245 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_DONE,
1246 struct GNUNET_MessageHeader,
1247 NULL),
1248 GNUNET_MQ_hd_fixed_size (union_p2p_request_full,
1249 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_REQUEST_FULL,
1250 struct GNUNET_MessageHeader,
1251 NULL),
1252 GNUNET_MQ_hd_var_size (union_p2p_strata_estimator,
1253 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SE,
1254 struct StrataEstimatorMessage,
1255 NULL),
1256 GNUNET_MQ_hd_var_size (union_p2p_strata_estimator,
1257 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SEC,
1258 struct StrataEstimatorMessage,
1259 NULL),
1260 GNUNET_MQ_hd_var_size (union_p2p_full_element,
1261 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_ELEMENT,
1262 struct GNUNET_SET_ElementMessage,
1263 NULL),
1264 GNUNET_MQ_hd_fixed_size (intersection_p2p_element_info,
1265 GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_ELEMENT_INFO,
1266 struct IntersectionElementInfoMessage,
1267 NULL),
1268 GNUNET_MQ_hd_var_size (intersection_p2p_bf,
1269 GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_BF,
1270 struct BFMessage,
1271 NULL),
1272 GNUNET_MQ_hd_fixed_size (intersection_p2p_done,
1273 GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_DONE,
1274 struct IntersectionDoneMessage,
1275 NULL),
1276 GNUNET_MQ_handler_end () };
1277 struct Listener *listener;
1278
1279 if (NULL != cs->listener)
1280 {
1281 /* max. one active listener per client! */
1282 GNUNET_break (0);
1283 GNUNET_SERVICE_client_drop (cs->client);
1284 return;
1285 }
1286 listener = GNUNET_new (struct Listener);
1287 listener->cs = cs;
1288 cs->listener = listener;
1289 listener->app_id = msg->app_id;
1290 listener->operation = (enum GNUNET_SET_OperationType) ntohl (msg->operation);
1291 GNUNET_CONTAINER_DLL_insert (listener_head, listener_tail, listener);
1292 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1293 "New listener created (op %u, port %s)\n",
1294 listener->operation,
1295 GNUNET_h2s (&listener->app_id));
1296 listener->open_port = GNUNET_CADET_open_port (cadet,
1297 &msg->app_id,
1298 &channel_new_cb,
1299 listener,
1300 &channel_window_cb,
1301 &channel_end_cb,
1302 cadet_handlers);
1303 GNUNET_SERVICE_client_continue (cs->client);
1304}
1305
1306
1307/**
1308 * Called when the listening client rejects an operation
1309 * request by another peer.
1310 *
1311 * @param cls client that sent the message
1312 * @param msg message sent by the client
1313 */
1314static void
1315handle_client_reject (void *cls, const struct GNUNET_SET_RejectMessage *msg)
1316{
1317 struct ClientState *cs = cls;
1318 struct Operation *op;
1319
1320 op = get_incoming (ntohl (msg->accept_reject_id));
1321 if (NULL == op)
1322 {
1323 /* no matching incoming operation for this reject;
1324 could be that the other peer already disconnected... */
1325 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1326 "Client rejected unknown operation %u\n",
1327 (unsigned int) ntohl (msg->accept_reject_id));
1328 GNUNET_SERVICE_client_continue (cs->client);
1329 return;
1330 }
1331 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1332 "Peer request (op %u, app %s) rejected by client\n",
1333 op->listener->operation,
1334 GNUNET_h2s (&cs->listener->app_id));
1335 _GSS_operation_destroy2 (op);
1336 GNUNET_SERVICE_client_continue (cs->client);
1337}
1338
1339
1340/**
1341 * Called when a client wants to add or remove an element to a set it inhabits.
1342 *
1343 * @param cls client that sent the message
1344 * @param msg message sent by the client
1345 */
1346static int
1347check_client_mutation (void *cls, const struct GNUNET_SET_ElementMessage *msg)
1348{
1349 /* NOTE: Technically, we should probably check with the
1350 block library whether the element we are given is well-formed */
1351 return GNUNET_OK;
1352}
1353
1354
1355/**
1356 * Called when a client wants to add or remove an element to a set it inhabits.
1357 *
1358 * @param cls client that sent the message
1359 * @param msg message sent by the client
1360 */
1361static void
1362handle_client_mutation (void *cls, const struct GNUNET_SET_ElementMessage *msg)
1363{
1364 struct ClientState *cs = cls;
1365 struct Set *set;
1366
1367 if (NULL == (set = cs->set))
1368 {
1369 /* client without a set requested an operation */
1370 GNUNET_break (0);
1371 GNUNET_SERVICE_client_drop (cs->client);
1372 return;
1373 }
1374 GNUNET_SERVICE_client_continue (cs->client);
1375
1376 if (0 != set->content->iterator_count)
1377 {
1378 struct PendingMutation *pm;
1379
1380 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Scheduling mutation on set\n");
1381 pm = GNUNET_new (struct PendingMutation);
1382 pm->msg =
1383 (struct GNUNET_SET_ElementMessage *) GNUNET_copy_message (&msg->header);
1384 pm->set = set;
1385 GNUNET_CONTAINER_DLL_insert_tail (set->content->pending_mutations_head,
1386 set->content->pending_mutations_tail,
1387 pm);
1388 return;
1389 }
1390 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Executing mutation on set\n");
1391 execute_mutation (set, msg);
1392}
1393
1394
1395/**
1396 * Advance the current generation of a set,
1397 * adding exclusion ranges if necessary.
1398 *
1399 * @param set the set where we want to advance the generation
1400 */
1401static void
1402advance_generation (struct Set *set)
1403{
1404 struct GenerationRange r;
1405
1406 if (set->current_generation == set->content->latest_generation)
1407 {
1408 set->content->latest_generation++;
1409 set->current_generation++;
1410 return;
1411 }
1412
1413 GNUNET_assert (set->current_generation < set->content->latest_generation);
1414
1415 r.start = set->current_generation + 1;
1416 r.end = set->content->latest_generation + 1;
1417 set->content->latest_generation = r.end;
1418 set->current_generation = r.end;
1419 GNUNET_array_append (set->excluded_generations,
1420 set->excluded_generations_size,
1421 r);
1422}
1423
1424
1425/**
1426 * Called when a client wants to initiate a set operation with another
1427 * peer. Initiates the CADET connection to the listener and sends the
1428 * request.
1429 *
1430 * @param cls client that sent the message
1431 * @param msg message sent by the client
1432 * @return #GNUNET_OK if the message is well-formed
1433 */
1434static int
1435check_client_evaluate (void *cls, const struct GNUNET_SET_EvaluateMessage *msg)
1436{
1437 /* FIXME: suboptimal, even if the context below could be NULL,
1438 there are malformed messages this does not check for... */
1439 return GNUNET_OK;
1440}
1441
1442
1443/**
1444 * Called when a client wants to initiate a set operation with another
1445 * peer. Initiates the CADET connection to the listener and sends the
1446 * request.
1447 *
1448 * @param cls client that sent the message
1449 * @param msg message sent by the client
1450 */
1451static void
1452handle_client_evaluate (void *cls, const struct GNUNET_SET_EvaluateMessage *msg)
1453{
1454 struct ClientState *cs = cls;
1455 struct Operation *op = GNUNET_new (struct Operation);
1456 const struct GNUNET_MQ_MessageHandler cadet_handlers[] =
1457 { GNUNET_MQ_hd_var_size (incoming_msg,
1458 GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST,
1459 struct OperationRequestMessage,
1460 op),
1461 GNUNET_MQ_hd_var_size (union_p2p_ibf,
1462 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_IBF,
1463 struct IBFMessage,
1464 op),
1465 GNUNET_MQ_hd_var_size (union_p2p_elements,
1466 GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENTS,
1467 struct GNUNET_SET_ElementMessage,
1468 op),
1469 GNUNET_MQ_hd_var_size (union_p2p_offer,
1470 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_OFFER,
1471 struct GNUNET_MessageHeader,
1472 op),
1473 GNUNET_MQ_hd_var_size (union_p2p_inquiry,
1474 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_INQUIRY,
1475 struct InquiryMessage,
1476 op),
1477 GNUNET_MQ_hd_var_size (union_p2p_demand,
1478 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DEMAND,
1479 struct GNUNET_MessageHeader,
1480 op),
1481 GNUNET_MQ_hd_fixed_size (union_p2p_done,
1482 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE,
1483 struct GNUNET_MessageHeader,
1484 op),
1485 GNUNET_MQ_hd_fixed_size (union_p2p_over,
1486 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_OVER,
1487 struct GNUNET_MessageHeader,
1488 op),
1489 GNUNET_MQ_hd_fixed_size (union_p2p_full_done,
1490 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_DONE,
1491 struct GNUNET_MessageHeader,
1492 op),
1493 GNUNET_MQ_hd_fixed_size (union_p2p_request_full,
1494 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_REQUEST_FULL,
1495 struct GNUNET_MessageHeader,
1496 op),
1497 GNUNET_MQ_hd_var_size (union_p2p_strata_estimator,
1498 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SE,
1499 struct StrataEstimatorMessage,
1500 op),
1501 GNUNET_MQ_hd_var_size (union_p2p_strata_estimator,
1502 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SEC,
1503 struct StrataEstimatorMessage,
1504 op),
1505 GNUNET_MQ_hd_var_size (union_p2p_full_element,
1506 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_ELEMENT,
1507 struct GNUNET_SET_ElementMessage,
1508 op),
1509 GNUNET_MQ_hd_fixed_size (intersection_p2p_element_info,
1510 GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_ELEMENT_INFO,
1511 struct IntersectionElementInfoMessage,
1512 op),
1513 GNUNET_MQ_hd_var_size (intersection_p2p_bf,
1514 GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_BF,
1515 struct BFMessage,
1516 op),
1517 GNUNET_MQ_hd_fixed_size (intersection_p2p_done,
1518 GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_DONE,
1519 struct IntersectionDoneMessage,
1520 op),
1521 GNUNET_MQ_handler_end () };
1522 struct Set *set;
1523 const struct GNUNET_MessageHeader *context;
1524
1525 if (NULL == (set = cs->set))
1526 {
1527 GNUNET_break (0);
1528 GNUNET_free (op);
1529 GNUNET_SERVICE_client_drop (cs->client);
1530 return;
1531 }
1532 op->salt = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX);
1533 op->peer = msg->target_peer;
1534 op->result_mode = ntohl (msg->result_mode);
1535 op->client_request_id = ntohl (msg->request_id);
1536 op->byzantine = msg->byzantine;
1537 op->byzantine_lower_bound = msg->byzantine_lower_bound;
1538 op->force_full = msg->force_full;
1539 op->force_delta = msg->force_delta;
1540 context = GNUNET_MQ_extract_nested_mh (msg);
1541
1542 /* Advance generation values, so that
1543 mutations won't interfer with the running operation. */
1544 op->set = set;
1545 op->generation_created = set->current_generation;
1546 advance_generation (set);
1547 GNUNET_CONTAINER_DLL_insert (set->ops_head, set->ops_tail, op);
1548 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1549 "Creating new CADET channel to port %s for set operation type %u\n",
1550 GNUNET_h2s (&msg->app_id),
1551 set->operation);
1552 op->channel = GNUNET_CADET_channel_create (cadet,
1553 op,
1554 &msg->target_peer,
1555 &msg->app_id,
1556 &channel_window_cb,
1557 &channel_end_cb,
1558 cadet_handlers);
1559 op->mq = GNUNET_CADET_get_mq (op->channel);
1560 op->state = set->vt->evaluate (op, context);
1561 if (NULL == op->state)
1562 {
1563 GNUNET_break (0);
1564 GNUNET_SERVICE_client_drop (cs->client);
1565 return;
1566 }
1567 GNUNET_SERVICE_client_continue (cs->client);
1568}
1569
1570
1571/**
1572 * Handle an ack from a client, and send the next element. Note
1573 * that we only expect acks for set elements, not after the
1574 * #GNUNET_MESSAGE_TYPE_SET_ITER_DONE message.
1575 *
1576 * @param cls client the client
1577 * @param ack the message
1578 */
1579static void
1580handle_client_iter_ack (void *cls, const struct GNUNET_SET_IterAckMessage *ack)
1581{
1582 struct ClientState *cs = cls;
1583 struct Set *set;
1584
1585 if (NULL == (set = cs->set))
1586 {
1587 /* client without a set acknowledged receiving a value */
1588 GNUNET_break (0);
1589 GNUNET_SERVICE_client_drop (cs->client);
1590 return;
1591 }
1592 if (NULL == set->iter)
1593 {
1594 /* client sent an ack, but we were not expecting one (as
1595 set iteration has finished) */
1596 GNUNET_break (0);
1597 GNUNET_SERVICE_client_drop (cs->client);
1598 return;
1599 }
1600 GNUNET_SERVICE_client_continue (cs->client);
1601 if (ntohl (ack->send_more))
1602 {
1603 send_client_element (set);
1604 }
1605 else
1606 {
1607 GNUNET_CONTAINER_multihashmap_iterator_destroy (set->iter);
1608 set->iter = NULL;
1609 set->iteration_id++;
1610 }
1611}
1612
1613
1614/**
1615 * Handle a request from the client to copy a set.
1616 *
1617 * @param cls the client
1618 * @param mh the message
1619 */
1620static void
1621handle_client_copy_lazy_prepare (void *cls,
1622 const struct GNUNET_MessageHeader *mh)
1623{
1624 struct ClientState *cs = cls;
1625 struct Set *set;
1626 struct LazyCopyRequest *cr;
1627 struct GNUNET_MQ_Envelope *ev;
1628 struct GNUNET_SET_CopyLazyResponseMessage *resp_msg;
1629
1630 if (NULL == (set = cs->set))
1631 {
1632 /* client without a set requested an operation */
1633 GNUNET_break (0);
1634 GNUNET_SERVICE_client_drop (cs->client);
1635 return;
1636 }
1637 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1638 "Client requested creation of lazy copy\n");
1639 cr = GNUNET_new (struct LazyCopyRequest);
1640 cr->cookie = ++lazy_copy_cookie;
1641 cr->source_set = set;
1642 GNUNET_CONTAINER_DLL_insert (lazy_copy_head, lazy_copy_tail, cr);
1643 ev = GNUNET_MQ_msg (resp_msg, GNUNET_MESSAGE_TYPE_SET_COPY_LAZY_RESPONSE);
1644 resp_msg->cookie = cr->cookie;
1645 GNUNET_MQ_send (set->cs->mq, ev);
1646 GNUNET_SERVICE_client_continue (cs->client);
1647}
1648
1649
1650/**
1651 * Handle a request from the client to connect to a copy of a set.
1652 *
1653 * @param cls the client
1654 * @param msg the message
1655 */
1656static void
1657handle_client_copy_lazy_connect (
1658 void *cls,
1659 const struct GNUNET_SET_CopyLazyConnectMessage *msg)
1660{
1661 struct ClientState *cs = cls;
1662 struct LazyCopyRequest *cr;
1663 struct Set *set;
1664 int found;
1665
1666 if (NULL != cs->set)
1667 {
1668 /* There can only be one set per client */
1669 GNUNET_break (0);
1670 GNUNET_SERVICE_client_drop (cs->client);
1671 return;
1672 }
1673 found = GNUNET_NO;
1674 for (cr = lazy_copy_head; NULL != cr; cr = cr->next)
1675 {
1676 if (cr->cookie == msg->cookie)
1677 {
1678 found = GNUNET_YES;
1679 break;
1680 }
1681 }
1682 if (GNUNET_NO == found)
1683 {
1684 /* client asked for copy with cookie we don't know */
1685 GNUNET_break (0);
1686 GNUNET_SERVICE_client_drop (cs->client);
1687 return;
1688 }
1689 GNUNET_CONTAINER_DLL_remove (lazy_copy_head, lazy_copy_tail, cr);
1690 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1691 "Client %p requested use of lazy copy\n",
1692 cs);
1693 set = GNUNET_new (struct Set);
1694 switch (cr->source_set->operation)
1695 {
1696 case GNUNET_SET_OPERATION_INTERSECTION:
1697 set->vt = _GSS_intersection_vt ();
1698 break;
1699
1700 case GNUNET_SET_OPERATION_UNION:
1701 set->vt = _GSS_union_vt ();
1702 break;
1703
1704 default:
1705 GNUNET_assert (0);
1706 return;
1707 }
1708
1709 if (NULL == set->vt->copy_state)
1710 {
1711 /* Lazy copy not supported for this set operation */
1712 GNUNET_break (0);
1713 GNUNET_free (set);
1714 GNUNET_free (cr);
1715 GNUNET_SERVICE_client_drop (cs->client);
1716 return;
1717 }
1718
1719 set->operation = cr->source_set->operation;
1720 set->state = set->vt->copy_state (cr->source_set->state);
1721 set->content = cr->source_set->content;
1722 set->content->refcount++;
1723
1724 set->current_generation = cr->source_set->current_generation;
1725 set->excluded_generations_size = cr->source_set->excluded_generations_size;
1726 set->excluded_generations =
1727 GNUNET_memdup (cr->source_set->excluded_generations,
1728 set->excluded_generations_size
1729 * sizeof(struct GenerationRange));
1730
1731 /* Advance the generation of the new set, so that mutations to the
1732 of the cloned set and the source set are independent. */
1733 advance_generation (set);
1734 set->cs = cs;
1735 cs->set = set;
1736 GNUNET_free (cr);
1737 GNUNET_SERVICE_client_continue (cs->client);
1738}
1739
1740
1741/**
1742 * Handle a request from the client to cancel a running set operation.
1743 *
1744 * @param cls the client
1745 * @param msg the message
1746 */
1747static void
1748handle_client_cancel (void *cls, const struct GNUNET_SET_CancelMessage *msg)
1749{
1750 struct ClientState *cs = cls;
1751 struct Set *set;
1752 struct Operation *op;
1753 int found;
1754
1755 if (NULL == (set = cs->set))
1756 {
1757 /* client without a set requested an operation */
1758 GNUNET_break (0);
1759 GNUNET_SERVICE_client_drop (cs->client);
1760 return;
1761 }
1762 found = GNUNET_NO;
1763 for (op = set->ops_head; NULL != op; op = op->next)
1764 {
1765 if (op->client_request_id == ntohl (msg->request_id))
1766 {
1767 found = GNUNET_YES;
1768 break;
1769 }
1770 }
1771 if (GNUNET_NO == found)
1772 {
1773 /* It may happen that the operation was already destroyed due to
1774 * the other peer disconnecting. The client may not know about this
1775 * yet and try to cancel the (just barely non-existent) operation.
1776 * So this is not a hard error.
1777 */GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1778 "Client canceled non-existent op %u\n",
1779 (uint32_t) ntohl (msg->request_id));
1780 }
1781 else
1782 {
1783 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1784 "Client requested cancel for op %u\n",
1785 (uint32_t) ntohl (msg->request_id));
1786 _GSS_operation_destroy (op, GNUNET_YES);
1787 }
1788 GNUNET_SERVICE_client_continue (cs->client);
1789}
1790
1791
1792/**
1793 * Handle a request from the client to accept a set operation that
1794 * came from a remote peer. We forward the accept to the associated
1795 * operation for handling
1796 *
1797 * @param cls the client
1798 * @param msg the message
1799 */
1800static void
1801handle_client_accept (void *cls, const struct GNUNET_SET_AcceptMessage *msg)
1802{
1803 struct ClientState *cs = cls;
1804 struct Set *set;
1805 struct Operation *op;
1806 struct GNUNET_SET_ResultMessage *result_message;
1807 struct GNUNET_MQ_Envelope *ev;
1808 struct Listener *listener;
1809
1810 if (NULL == (set = cs->set))
1811 {
1812 /* client without a set requested to accept */
1813 GNUNET_break (0);
1814 GNUNET_SERVICE_client_drop (cs->client);
1815 return;
1816 }
1817 op = get_incoming (ntohl (msg->accept_reject_id));
1818 if (NULL == op)
1819 {
1820 /* It is not an error if the set op does not exist -- it may
1821 * have been destroyed when the partner peer disconnected. */
1822 GNUNET_log (
1823 GNUNET_ERROR_TYPE_INFO,
1824 "Client %p accepted request %u of listener %p that is no longer active\n",
1825 cs,
1826 ntohl (msg->accept_reject_id),
1827 cs->listener);
1828 ev = GNUNET_MQ_msg (result_message, GNUNET_MESSAGE_TYPE_SET_RESULT);
1829 result_message->request_id = msg->request_id;
1830 result_message->result_status = htons (GNUNET_SET_STATUS_FAILURE);
1831 GNUNET_MQ_send (set->cs->mq, ev);
1832 GNUNET_SERVICE_client_continue (cs->client);
1833 return;
1834 }
1835 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1836 "Client accepting request %u\n",
1837 (uint32_t) ntohl (msg->accept_reject_id));
1838 listener = op->listener;
1839 op->listener = NULL;
1840 GNUNET_CONTAINER_DLL_remove (listener->op_head, listener->op_tail, op);
1841 op->set = set;
1842 GNUNET_CONTAINER_DLL_insert (set->ops_head, set->ops_tail, op);
1843 op->client_request_id = ntohl (msg->request_id);
1844 op->result_mode = ntohl (msg->result_mode);
1845 op->byzantine = msg->byzantine;
1846 op->byzantine_lower_bound = msg->byzantine_lower_bound;
1847 op->force_full = msg->force_full;
1848 op->force_delta = msg->force_delta;
1849
1850 /* Advance generation values, so that future mutations do not
1851 interfer with the running operation. */
1852 op->generation_created = set->current_generation;
1853 advance_generation (set);
1854 GNUNET_assert (NULL == op->state);
1855 op->state = set->vt->accept (op);
1856 if (NULL == op->state)
1857 {
1858 GNUNET_break (0);
1859 GNUNET_SERVICE_client_drop (cs->client);
1860 return;
1861 }
1862 /* Now allow CADET to continue, as we did not do this in
1863 #handle_incoming_msg (as we wanted to first see if the
1864 local client would accept the request). */
1865 GNUNET_CADET_receive_done (op->channel);
1866 GNUNET_SERVICE_client_continue (cs->client);
1867}
1868
1869
1870/**
1871 * Called to clean up, after a shutdown has been requested.
1872 *
1873 * @param cls closure, NULL
1874 */
1875static void
1876shutdown_task (void *cls)
1877{
1878 /* Delay actual shutdown to allow service to disconnect clients */
1879 in_shutdown = GNUNET_YES;
1880 if (0 == num_clients)
1881 {
1882 if (NULL != cadet)
1883 {
1884 GNUNET_CADET_disconnect (cadet);
1885 cadet = NULL;
1886 }
1887 }
1888 GNUNET_STATISTICS_destroy (_GSS_statistics, GNUNET_YES);
1889 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "handled shutdown request\n");
1890}
1891
1892
1893/**
1894 * Function called by the service's run
1895 * method to run service-specific setup code.
1896 *
1897 * @param cls closure
1898 * @param cfg configuration to use
1899 * @param service the initialized service
1900 */
1901static void
1902run (void *cls,
1903 const struct GNUNET_CONFIGURATION_Handle *cfg,
1904 struct GNUNET_SERVICE_Handle *service)
1905{
1906 /* FIXME: need to modify SERVICE (!) API to allow
1907 us to run a shutdown task *after* clients were
1908 forcefully disconnected! */
1909 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
1910 _GSS_statistics = GNUNET_STATISTICS_create ("set", cfg);
1911 cadet = GNUNET_CADET_connect (cfg);
1912 if (NULL == cadet)
1913 {
1914 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1915 _ ("Could not connect to CADET service\n"));
1916 GNUNET_SCHEDULER_shutdown ();
1917 return;
1918 }
1919}
1920
1921
1922/**
1923 * Define "main" method using service macro.
1924 */
1925GNUNET_SERVICE_MAIN (
1926 "set",
1927 GNUNET_SERVICE_OPTION_NONE,
1928 &run,
1929 &client_connect_cb,
1930 &client_disconnect_cb,
1931 NULL,
1932 GNUNET_MQ_hd_fixed_size (client_accept,
1933 GNUNET_MESSAGE_TYPE_SET_ACCEPT,
1934 struct GNUNET_SET_AcceptMessage,
1935 NULL),
1936 GNUNET_MQ_hd_fixed_size (client_iter_ack,
1937 GNUNET_MESSAGE_TYPE_SET_ITER_ACK,
1938 struct GNUNET_SET_IterAckMessage,
1939 NULL),
1940 GNUNET_MQ_hd_var_size (client_mutation,
1941 GNUNET_MESSAGE_TYPE_SET_ADD,
1942 struct GNUNET_SET_ElementMessage,
1943 NULL),
1944 GNUNET_MQ_hd_fixed_size (client_create_set,
1945 GNUNET_MESSAGE_TYPE_SET_CREATE,
1946 struct GNUNET_SET_CreateMessage,
1947 NULL),
1948 GNUNET_MQ_hd_fixed_size (client_iterate,
1949 GNUNET_MESSAGE_TYPE_SET_ITER_REQUEST,
1950 struct GNUNET_MessageHeader,
1951 NULL),
1952 GNUNET_MQ_hd_var_size (client_evaluate,
1953 GNUNET_MESSAGE_TYPE_SET_EVALUATE,
1954 struct GNUNET_SET_EvaluateMessage,
1955 NULL),
1956 GNUNET_MQ_hd_fixed_size (client_listen,
1957 GNUNET_MESSAGE_TYPE_SET_LISTEN,
1958 struct GNUNET_SET_ListenMessage,
1959 NULL),
1960 GNUNET_MQ_hd_fixed_size (client_reject,
1961 GNUNET_MESSAGE_TYPE_SET_REJECT,
1962 struct GNUNET_SET_RejectMessage,
1963 NULL),
1964 GNUNET_MQ_hd_var_size (client_mutation,
1965 GNUNET_MESSAGE_TYPE_SET_REMOVE,
1966 struct GNUNET_SET_ElementMessage,
1967 NULL),
1968 GNUNET_MQ_hd_fixed_size (client_cancel,
1969 GNUNET_MESSAGE_TYPE_SET_CANCEL,
1970 struct GNUNET_SET_CancelMessage,
1971 NULL),
1972 GNUNET_MQ_hd_fixed_size (client_copy_lazy_prepare,
1973 GNUNET_MESSAGE_TYPE_SET_COPY_LAZY_PREPARE,
1974 struct GNUNET_MessageHeader,
1975 NULL),
1976 GNUNET_MQ_hd_fixed_size (client_copy_lazy_connect,
1977 GNUNET_MESSAGE_TYPE_SET_COPY_LAZY_CONNECT,
1978 struct GNUNET_SET_CopyLazyConnectMessage,
1979 NULL),
1980 GNUNET_MQ_handler_end ());
1981
1982
1983/* end of gnunet-service-set.c */
diff --git a/src/contrib/service/set/gnunet-service-set.h b/src/contrib/service/set/gnunet-service-set.h
new file mode 100644
index 000000000..abdec7f08
--- /dev/null
+++ b/src/contrib/service/set/gnunet-service-set.h
@@ -0,0 +1,665 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013-2017 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file set/gnunet-service-set.h
22 * @brief common components for the implementation the different set operations
23 * @author Florian Dold
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_SET_H_PRIVATE
27#define GNUNET_SERVICE_SET_H_PRIVATE
28
29#include "platform.h"
30#include "gnunet_util_lib.h"
31#include "gnunet_protocols.h"
32#include "gnunet_applications.h"
33#include "gnunet_core_service.h"
34#include "gnunet_cadet_service.h"
35#include "gnunet_set_service.h"
36#include "set.h"
37
38
39/**
40 * Implementation-specific set state. Used as opaque pointer, and
41 * specified further in the respective implementation.
42 */
43struct SetState;
44
45/**
46 * Implementation-specific set operation. Used as opaque pointer, and
47 * specified further in the respective implementation.
48 */
49struct OperationState;
50
51/**
52 * A set that supports a specific operation with other peers.
53 */
54struct Set;
55
56/**
57 * Information about an element element in the set. All elements are
58 * stored in a hash-table from their hash-code to their 'struct
59 * Element', so that the remove and add operations are reasonably
60 * fast.
61 */
62struct ElementEntry;
63
64/**
65 * Operation context used to execute a set operation.
66 */
67struct Operation;
68
69
70/**
71 * Signature of functions that create the implementation-specific
72 * state for a set supporting a specific operation.
73 *
74 * @return a set state specific to the supported operation, NULL on error
75 */
76typedef struct SetState *
77(*SetCreateImpl) (void);
78
79
80/**
81 * Signature of functions that implement the add/remove functionality
82 * for a set supporting a specific operation.
83 *
84 * @param set implementation-specific set state
85 * @param ee element message from the client
86 */
87typedef void
88(*SetAddRemoveImpl) (struct SetState *state,
89 struct ElementEntry *ee);
90
91
92/**
93 * Make a copy of a set's internal state.
94 *
95 * @param state set state to copy
96 * @return copy of the internal state
97 */
98typedef struct SetState *
99(*SetCopyStateImpl) (struct SetState *state);
100
101
102/**
103 * Signature of functions that implement the destruction of the
104 * implementation-specific set state.
105 *
106 * @param state the set state, contains implementation-specific data
107 */
108typedef void
109(*SetDestroyImpl) (struct SetState *state);
110
111
112/**
113 * Signature of functions that implement accepting a set operation.
114 *
115 * @param op operation that is created by accepting the operation,
116 * should be initialized by the implementation
117 * @return operation-specific state to keep in @a op
118 */
119typedef struct OperationState *
120(*OpAcceptImpl) (struct Operation *op);
121
122
123/**
124 * Signature of functions that implement starting the evaluation of
125 * set operations.
126 *
127 * @param op operation that is created, should be initialized to
128 * begin the evaluation
129 * @param opaque_context message to be transmitted to the listener
130 * to convince it to accept, may be NULL
131 * @return operation-specific state to keep in @a op
132 */
133typedef struct OperationState *
134(*OpEvaluateImpl) (struct Operation *op,
135 const struct GNUNET_MessageHeader *opaque_context);
136
137/**
138 * Signature of functions that implement operation cancellation.
139 * This includes notifying the client about the operation's final
140 * state.
141 *
142 * @param op operation state
143 */
144typedef void
145(*OpCancelImpl) (struct Operation *op);
146
147
148/**
149 * Signature of functions called when the CADET channel died.
150 *
151 * @param op operation state
152 */
153typedef void
154(*OpChannelDeathImpl) (struct Operation *op);
155
156
157/**
158 * Dispatch table for a specific set operation. Every set operation
159 * has to implement the callback in this struct.
160 */
161struct SetVT
162{
163 /**
164 * Callback for the set creation.
165 */
166 SetCreateImpl create;
167
168 /**
169 * Callback for element insertion
170 */
171 SetAddRemoveImpl add;
172
173 /**
174 * Callback for element removal.
175 */
176 SetAddRemoveImpl remove;
177
178 /**
179 * Callback for making a copy of a set's internal state.
180 */
181 SetCopyStateImpl copy_state;
182
183 /**
184 * Callback for destruction of the set state.
185 */
186 SetDestroyImpl destroy_set;
187
188 /**
189 * Callback for accepting a set operation request
190 */
191 OpAcceptImpl accept;
192
193 /**
194 * Callback for starting evaluation with a remote peer.
195 */
196 OpEvaluateImpl evaluate;
197
198 /**
199 * Callback for canceling an operation.
200 */
201 OpCancelImpl cancel;
202
203 /**
204 * Callback called in case the CADET channel died.
205 */
206 OpChannelDeathImpl channel_death;
207};
208
209
210/**
211 * MutationEvent gives information about changes
212 * to an element (removal / addition) in a set content.
213 */
214struct MutationEvent
215{
216 /**
217 * First generation affected by this mutation event.
218 *
219 * If @a generation is 0, this mutation event is a list
220 * sentinel element.
221 */
222 unsigned int generation;
223
224 /**
225 * If @a added is #GNUNET_YES, then this is a
226 * `remove` event, otherwise it is an `add` event.
227 */
228 int added;
229};
230
231
232/**
233 * Information about an element element in the set. All elements are
234 * stored in a hash-table from their hash-code to their `struct
235 * Element`, so that the remove and add operations are reasonably
236 * fast.
237 */
238struct ElementEntry
239{
240 /**
241 * The actual element. The data for the element
242 * should be allocated at the end of this struct.
243 */
244 struct GNUNET_SET_Element element;
245
246 /**
247 * Hash of the element. For set union: Will be used to derive the
248 * different IBF keys for different salts.
249 */
250 struct GNUNET_HashCode element_hash;
251
252 /**
253 * If @a mutations is not NULL, it contains
254 * a list of mutations, ordered by increasing generation.
255 * The list is terminated by a sentinel event with `generation`
256 * set to 0.
257 *
258 * If @a mutations is NULL, then this element exists in all generations
259 * of the respective set content this element belongs to.
260 */
261 struct MutationEvent *mutations;
262
263 /**
264 * Number of elements in the array @a mutations.
265 */
266 unsigned int mutations_size;
267
268 /**
269 * #GNUNET_YES if the element is a remote element, and does not belong
270 * to the operation's set.
271 */
272 int remote;
273};
274
275
276/**
277 * A listener is inhabited by a client, and waits for evaluation
278 * requests from remote peers.
279 */
280struct Listener;
281
282
283/**
284 * State we keep per client.
285 */
286struct ClientState
287{
288 /**
289 * Set, if associated with the client, otherwise NULL.
290 */
291 struct Set *set;
292
293 /**
294 * Listener, if associated with the client, otherwise NULL.
295 */
296 struct Listener *listener;
297
298 /**
299 * Client handle.
300 */
301 struct GNUNET_SERVICE_Client *client;
302
303 /**
304 * Message queue.
305 */
306 struct GNUNET_MQ_Handle *mq;
307};
308
309
310/**
311 * Operation context used to execute a set operation.
312 */
313struct Operation
314{
315 /**
316 * Kept in a DLL of the listener, if @e listener is non-NULL.
317 */
318 struct Operation *next;
319
320 /**
321 * Kept in a DLL of the listener, if @e listener is non-NULL.
322 */
323 struct Operation *prev;
324
325 /**
326 * Channel to the peer.
327 */
328 struct GNUNET_CADET_Channel *channel;
329
330 /**
331 * Port this operation runs on.
332 */
333 struct Listener *listener;
334
335 /**
336 * Message queue for the channel.
337 */
338 struct GNUNET_MQ_Handle *mq;
339
340 /**
341 * Context message, may be NULL.
342 */
343 struct GNUNET_MessageHeader *context_msg;
344
345 /**
346 * Set associated with the operation, NULL until the spec has been
347 * associated with a set.
348 */
349 struct Set *set;
350
351 /**
352 * Operation-specific operation state. Note that the exact
353 * type depends on this being a union or intersection operation
354 * (and thus on @e vt).
355 */
356 struct OperationState *state;
357
358 /**
359 * The identity of the requesting peer. Needs to
360 * be stored here as the op spec might not have been created yet.
361 */
362 struct GNUNET_PeerIdentity peer;
363
364 /**
365 * Timeout task, if the incoming peer has not been accepted
366 * after the timeout, it will be disconnected.
367 */
368 struct GNUNET_SCHEDULER_Task *timeout_task;
369
370 /**
371 * Salt to use for the operation.
372 */
373 uint32_t salt;
374
375 /**
376 * Remote peers element count
377 */
378 uint32_t remote_element_count;
379
380 /**
381 * ID used to identify an operation between service and client
382 */
383 uint32_t client_request_id;
384
385 /**
386 * When are elements sent to the client, and which elements are sent?
387 */
388 enum GNUNET_SET_ResultMode result_mode;
389
390 /**
391 * Always use delta operation instead of sending full sets,
392 * even it it's less efficient.
393 */
394 int force_delta;
395
396 /**
397 * Always send full sets, even if delta operations would
398 * be more efficient.
399 */
400 int force_full;
401
402 /**
403 * #GNUNET_YES to fail operations where Byzantine faults
404 * are suspected
405 */
406 int byzantine;
407
408 /**
409 * Lower bound for the set size, used only when
410 * byzantine mode is enabled.
411 */
412 int byzantine_lower_bound;
413
414 /**
415 * Unique request id for the request from a remote peer, sent to the
416 * client, which will accept or reject the request. Set to '0' iff
417 * the request has not been suggested yet.
418 */
419 uint32_t suggest_id;
420
421 /**
422 * Generation in which the operation handle
423 * was created.
424 */
425 unsigned int generation_created;
426};
427
428
429/**
430 * SetContent stores the actual set elements, which may be shared by
431 * multiple generations derived from one set.
432 */
433struct SetContent
434{
435 /**
436 * Maps `struct GNUNET_HashCode *` to `struct ElementEntry *`.
437 */
438 struct GNUNET_CONTAINER_MultiHashMap *elements;
439
440 /**
441 * Mutations requested by the client that we're
442 * unable to execute right now because we're iterating
443 * over the underlying hash map of elements.
444 */
445 struct PendingMutation *pending_mutations_head;
446
447 /**
448 * Mutations requested by the client that we're
449 * unable to execute right now because we're iterating
450 * over the underlying hash map of elements.
451 */
452 struct PendingMutation *pending_mutations_tail;
453
454 /**
455 * Number of references to the content.
456 */
457 unsigned int refcount;
458
459 /**
460 * FIXME: document!
461 */
462 unsigned int latest_generation;
463
464 /**
465 * Number of concurrently active iterators.
466 */
467 int iterator_count;
468};
469
470
471struct GenerationRange
472{
473 /**
474 * First generation that is excluded.
475 */
476 unsigned int start;
477
478 /**
479 * Generation after the last excluded generation.
480 */
481 unsigned int end;
482};
483
484
485/**
486 * Information about a mutation to apply to a set.
487 */
488struct PendingMutation
489{
490 /**
491 * Mutations are kept in a DLL.
492 */
493 struct PendingMutation *prev;
494
495 /**
496 * Mutations are kept in a DLL.
497 */
498 struct PendingMutation *next;
499
500 /**
501 * Set this mutation is about.
502 */
503 struct Set *set;
504
505 /**
506 * Message that describes the desired mutation.
507 * May only be a #GNUNET_MESSAGE_TYPE_SET_ADD or
508 * #GNUNET_MESSAGE_TYPE_SET_REMOVE.
509 */
510 struct GNUNET_SET_ElementMessage *msg;
511};
512
513
514/**
515 * A set that supports a specific operation with other peers.
516 */
517struct Set
518{
519 /**
520 * Sets are held in a doubly linked list (in `sets_head` and `sets_tail`).
521 */
522 struct Set *next;
523
524 /**
525 * Sets are held in a doubly linked list.
526 */
527 struct Set *prev;
528
529 /**
530 * Client that owns the set. Only one client may own a set,
531 * and there can only be one set per client.
532 */
533 struct ClientState *cs;
534
535 /**
536 * Content, possibly shared by multiple sets,
537 * and thus reference counted.
538 */
539 struct SetContent *content;
540
541 /**
542 * Virtual table for this set. Determined by the operation type of
543 * this set.
544 *
545 * Used only for Add/remove of elements and when receiving an incoming
546 * operation from a remote peer.
547 */
548 const struct SetVT *vt;
549
550 /**
551 * Implementation-specific state.
552 */
553 struct SetState *state;
554
555 /**
556 * Current state of iterating elements for the client.
557 * NULL if we are not currently iterating.
558 */
559 struct GNUNET_CONTAINER_MultiHashMapIterator *iter;
560
561 /**
562 * Evaluate operations are held in a linked list.
563 */
564 struct Operation *ops_head;
565
566 /**
567 * Evaluate operations are held in a linked list.
568 */
569 struct Operation *ops_tail;
570
571 /**
572 * List of generations we have to exclude, due to lazy copies.
573 */
574 struct GenerationRange *excluded_generations;
575
576 /**
577 * Current generation, that is, number of previously executed
578 * operations and lazy copies on the underlying set content.
579 */
580 unsigned int current_generation;
581
582 /**
583 * Number of elements in array @a excluded_generations.
584 */
585 unsigned int excluded_generations_size;
586
587 /**
588 * Type of operation supported for this set
589 */
590 enum GNUNET_SET_OperationType operation;
591
592 /**
593 * Generation we're currently iteration over.
594 */
595 unsigned int iter_generation;
596
597 /**
598 * Each @e iter is assigned a unique number, so that the client
599 * can distinguish iterations.
600 */
601 uint16_t iteration_id;
602};
603
604
605extern struct GNUNET_STATISTICS_Handle *_GSS_statistics;
606
607
608/**
609 * Destroy the given operation. Used for any operation where both
610 * peers were known and that thus actually had a vt and channel. Must
611 * not be used for operations where 'listener' is still set and we do
612 * not know the other peer.
613 *
614 * Call the implementation-specific cancel function of the operation.
615 * Disconnects from the remote peer. Does not disconnect the client,
616 * as there may be multiple operations per set.
617 *
618 * @param op operation to destroy
619 * @param gc #GNUNET_YES to perform garbage collection on the set
620 */
621void
622_GSS_operation_destroy (struct Operation *op,
623 int gc);
624
625
626/**
627 * This function probably should not exist
628 * and be replaced by inlining more specific
629 * logic in the various places where it is called.
630 */
631void
632_GSS_operation_destroy2 (struct Operation *op);
633
634
635/**
636 * Get the table with implementing functions for set union.
637 *
638 * @return the operation specific VTable
639 */
640const struct SetVT *
641_GSS_union_vt (void);
642
643
644/**
645 * Get the table with implementing functions for set intersection.
646 *
647 * @return the operation specific VTable
648 */
649const struct SetVT *
650_GSS_intersection_vt (void);
651
652
653/**
654 * Is element @a ee part of the set used by @a op?
655 *
656 * @param ee element to test
657 * @param op operation the defines the set and its generation
658 * @return #GNUNET_YES if the element is in the set, #GNUNET_NO if not
659 */
660int
661_GSS_is_element_of_operation (struct ElementEntry *ee,
662 struct Operation *op);
663
664
665#endif
diff --git a/src/contrib/service/set/gnunet-service-set_intersection.c b/src/contrib/service/set/gnunet-service-set_intersection.c
new file mode 100644
index 000000000..51a8d0dbc
--- /dev/null
+++ b/src/contrib/service/set/gnunet-service-set_intersection.c
@@ -0,0 +1,1324 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013-2017 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file set/gnunet-service-set_intersection.c
22 * @brief two-peer set intersection
23 * @author Christian Fuchs
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_statistics_service.h"
29#include "gnunet-service-set.h"
30#include "gnunet_block_lib.h"
31#include "gnunet-service-set_protocol.h"
32#include "gnunet-service-set_intersection.h"
33#include <gcrypt.h>
34
35
36/**
37 * Current phase we are in for a intersection operation.
38 */
39enum IntersectionOperationPhase
40{
41 /**
42 * We are just starting.
43 */
44 PHASE_INITIAL,
45
46 /**
47 * We have send the number of our elements to the other
48 * peer, but did not setup our element set yet.
49 */
50 PHASE_COUNT_SENT,
51
52 /**
53 * We have initialized our set and are now reducing it by exchanging
54 * Bloom filters until one party notices the their element hashes
55 * are equal.
56 */
57 PHASE_BF_EXCHANGE,
58
59 /**
60 * We must next send the P2P DONE message (after finishing mostly
61 * with the local client). Then we will wait for the channel to close.
62 */
63 PHASE_MUST_SEND_DONE,
64
65 /**
66 * We have received the P2P DONE message, and must finish with the
67 * local client before terminating the channel.
68 */
69 PHASE_DONE_RECEIVED,
70
71 /**
72 * The protocol is over. Results may still have to be sent to the
73 * client.
74 */
75 PHASE_FINISHED
76};
77
78
79/**
80 * State of an evaluate operation with another peer.
81 */
82struct OperationState
83{
84 /**
85 * The bf we currently receive
86 */
87 struct GNUNET_CONTAINER_BloomFilter *remote_bf;
88
89 /**
90 * BF of the set's element.
91 */
92 struct GNUNET_CONTAINER_BloomFilter *local_bf;
93
94 /**
95 * Remaining elements in the intersection operation.
96 * Maps element-id-hashes to 'elements in our set'.
97 */
98 struct GNUNET_CONTAINER_MultiHashMap *my_elements;
99
100 /**
101 * Iterator for sending the final set of @e my_elements to the client.
102 */
103 struct GNUNET_CONTAINER_MultiHashMapIterator *full_result_iter;
104
105 /**
106 * Evaluate operations are held in a linked list.
107 */
108 struct OperationState *next;
109
110 /**
111 * Evaluate operations are held in a linked list.
112 */
113 struct OperationState *prev;
114
115 /**
116 * For multipart BF transmissions, we have to store the
117 * bloomfilter-data until we fully received it.
118 */
119 char *bf_data;
120
121 /**
122 * XOR of the keys of all of the elements (remaining) in my set.
123 * Always updated when elements are added or removed to
124 * @e my_elements.
125 */
126 struct GNUNET_HashCode my_xor;
127
128 /**
129 * XOR of the keys of all of the elements (remaining) in
130 * the other peer's set. Updated when we receive the
131 * other peer's Bloom filter.
132 */
133 struct GNUNET_HashCode other_xor;
134
135 /**
136 * How many bytes of @e bf_data are valid?
137 */
138 uint32_t bf_data_offset;
139
140 /**
141 * Current element count contained within @e my_elements.
142 * (May differ briefly during initialization.)
143 */
144 uint32_t my_element_count;
145
146 /**
147 * size of the bloomfilter in @e bf_data.
148 */
149 uint32_t bf_data_size;
150
151 /**
152 * size of the bloomfilter
153 */
154 uint32_t bf_bits_per_element;
155
156 /**
157 * Salt currently used for BF construction (by us or the other peer,
158 * depending on where we are in the code).
159 */
160 uint32_t salt;
161
162 /**
163 * Current state of the operation.
164 */
165 enum IntersectionOperationPhase phase;
166
167 /**
168 * Generation in which the operation handle
169 * was created.
170 */
171 unsigned int generation_created;
172
173 /**
174 * Did we send the client that we are done?
175 */
176 int client_done_sent;
177
178 /**
179 * Set whenever we reach the state where the death of the
180 * channel is perfectly find and should NOT result in the
181 * operation being cancelled.
182 */
183 int channel_death_expected;
184};
185
186
187/**
188 * Extra state required for efficient set intersection.
189 * Merely tracks the total number of elements.
190 */
191struct SetState
192{
193 /**
194 * Number of currently valid elements in the set which have not been
195 * removed.
196 */
197 uint32_t current_set_element_count;
198};
199
200
201/**
202 * If applicable in the current operation mode, send a result message
203 * to the client indicating we removed an element.
204 *
205 * @param op intersection operation
206 * @param element element to send
207 */
208static void
209send_client_removed_element (struct Operation *op,
210 struct GNUNET_SET_Element *element)
211{
212 struct GNUNET_MQ_Envelope *ev;
213 struct GNUNET_SET_ResultMessage *rm;
214
215 if (GNUNET_SET_RESULT_REMOVED != op->result_mode)
216 return; /* Wrong mode for transmitting removed elements */
217 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
218 "Sending removed element (size %u) to client\n",
219 element->size);
220 GNUNET_STATISTICS_update (_GSS_statistics,
221 "# Element removed messages sent",
222 1,
223 GNUNET_NO);
224 GNUNET_assert (0 != op->client_request_id);
225 ev = GNUNET_MQ_msg_extra (rm,
226 element->size,
227 GNUNET_MESSAGE_TYPE_SET_RESULT);
228 if (NULL == ev)
229 {
230 GNUNET_break (0);
231 return;
232 }
233 rm->result_status = htons (GNUNET_SET_STATUS_OK);
234 rm->request_id = htonl (op->client_request_id);
235 rm->element_type = element->element_type;
236 GNUNET_memcpy (&rm[1],
237 element->data,
238 element->size);
239 GNUNET_MQ_send (op->set->cs->mq,
240 ev);
241}
242
243
244/**
245 * Fills the "my_elements" hashmap with all relevant elements.
246 *
247 * @param cls the `struct Operation *` we are performing
248 * @param key current key code
249 * @param value the `struct ElementEntry *` from the hash map
250 * @return #GNUNET_YES (we should continue to iterate)
251 */
252static int
253filtered_map_initialization (void *cls,
254 const struct GNUNET_HashCode *key,
255 void *value)
256{
257 struct Operation *op = cls;
258 struct ElementEntry *ee = value;
259 struct GNUNET_HashCode mutated_hash;
260
261
262 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
263 "FIMA called for %s:%u\n",
264 GNUNET_h2s (&ee->element_hash),
265 ee->element.size);
266
267 if (GNUNET_NO == _GSS_is_element_of_operation (ee, op))
268 {
269 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
270 "Reduced initialization, not starting with %s:%u (wrong generation)\n",
271 GNUNET_h2s (&ee->element_hash),
272 ee->element.size);
273 return GNUNET_YES; /* element not valid in our operation's generation */
274 }
275
276 /* Test if element is in other peer's bloomfilter */
277 GNUNET_BLOCK_mingle_hash (&ee->element_hash,
278 op->state->salt,
279 &mutated_hash);
280 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
281 "Testing mingled hash %s with salt %u\n",
282 GNUNET_h2s (&mutated_hash),
283 op->state->salt);
284 if (GNUNET_NO ==
285 GNUNET_CONTAINER_bloomfilter_test (op->state->remote_bf,
286 &mutated_hash))
287 {
288 /* remove this element */
289 send_client_removed_element (op,
290 &ee->element);
291 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
292 "Reduced initialization, not starting with %s:%u\n",
293 GNUNET_h2s (&ee->element_hash),
294 ee->element.size);
295 return GNUNET_YES;
296 }
297 op->state->my_element_count++;
298 GNUNET_CRYPTO_hash_xor (&op->state->my_xor,
299 &ee->element_hash,
300 &op->state->my_xor);
301 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
302 "Filtered initialization of my_elements, adding %s:%u\n",
303 GNUNET_h2s (&ee->element_hash),
304 ee->element.size);
305 GNUNET_break (GNUNET_YES ==
306 GNUNET_CONTAINER_multihashmap_put (op->state->my_elements,
307 &ee->element_hash,
308 ee,
309 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
310
311 return GNUNET_YES;
312}
313
314
315/**
316 * Removes elements from our hashmap if they are not contained within the
317 * provided remote bloomfilter.
318 *
319 * @param cls closure with the `struct Operation *`
320 * @param key current key code
321 * @param value value in the hash map
322 * @return #GNUNET_YES (we should continue to iterate)
323 */
324static int
325iterator_bf_reduce (void *cls,
326 const struct GNUNET_HashCode *key,
327 void *value)
328{
329 struct Operation *op = cls;
330 struct ElementEntry *ee = value;
331 struct GNUNET_HashCode mutated_hash;
332
333 GNUNET_BLOCK_mingle_hash (&ee->element_hash,
334 op->state->salt,
335 &mutated_hash);
336 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
337 "Testing mingled hash %s with salt %u\n",
338 GNUNET_h2s (&mutated_hash),
339 op->state->salt);
340 if (GNUNET_NO ==
341 GNUNET_CONTAINER_bloomfilter_test (op->state->remote_bf,
342 &mutated_hash))
343 {
344 GNUNET_break (0 < op->state->my_element_count);
345 op->state->my_element_count--;
346 GNUNET_CRYPTO_hash_xor (&op->state->my_xor,
347 &ee->element_hash,
348 &op->state->my_xor);
349 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
350 "Bloom filter reduction of my_elements, removing %s:%u\n",
351 GNUNET_h2s (&ee->element_hash),
352 ee->element.size);
353 GNUNET_assert (GNUNET_YES ==
354 GNUNET_CONTAINER_multihashmap_remove (op->state->my_elements,
355 &ee->element_hash,
356 ee));
357 send_client_removed_element (op,
358 &ee->element);
359 }
360 else
361 {
362 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
363 "Bloom filter reduction of my_elements, keeping %s:%u\n",
364 GNUNET_h2s (&ee->element_hash),
365 ee->element.size);
366 }
367 return GNUNET_YES;
368}
369
370
371/**
372 * Create initial bloomfilter based on all the elements given.
373 *
374 * @param cls the `struct Operation *`
375 * @param key current key code
376 * @param value the `struct ElementEntry` to process
377 * @return #GNUNET_YES (we should continue to iterate)
378 */
379static int
380iterator_bf_create (void *cls,
381 const struct GNUNET_HashCode *key,
382 void *value)
383{
384 struct Operation *op = cls;
385 struct ElementEntry *ee = value;
386 struct GNUNET_HashCode mutated_hash;
387
388 GNUNET_BLOCK_mingle_hash (&ee->element_hash,
389 op->state->salt,
390 &mutated_hash);
391 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
392 "Initializing BF with hash %s with salt %u\n",
393 GNUNET_h2s (&mutated_hash),
394 op->state->salt);
395 GNUNET_CONTAINER_bloomfilter_add (op->state->local_bf,
396 &mutated_hash);
397 return GNUNET_YES;
398}
399
400
401/**
402 * Inform the client that the intersection operation has failed,
403 * and proceed to destroy the evaluate operation.
404 *
405 * @param op the intersection operation to fail
406 */
407static void
408fail_intersection_operation (struct Operation *op)
409{
410 struct GNUNET_MQ_Envelope *ev;
411 struct GNUNET_SET_ResultMessage *msg;
412
413 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
414 "Intersection operation failed\n");
415 GNUNET_STATISTICS_update (_GSS_statistics,
416 "# Intersection operations failed",
417 1,
418 GNUNET_NO);
419 if (NULL != op->state->my_elements)
420 {
421 GNUNET_CONTAINER_multihashmap_destroy (op->state->my_elements);
422 op->state->my_elements = NULL;
423 }
424 ev = GNUNET_MQ_msg (msg,
425 GNUNET_MESSAGE_TYPE_SET_RESULT);
426 msg->result_status = htons (GNUNET_SET_STATUS_FAILURE);
427 msg->request_id = htonl (op->client_request_id);
428 msg->element_type = htons (0);
429 GNUNET_MQ_send (op->set->cs->mq,
430 ev);
431 _GSS_operation_destroy (op,
432 GNUNET_YES);
433}
434
435
436/**
437 * Send a bloomfilter to our peer. After the result done message has
438 * been sent to the client, destroy the evaluate operation.
439 *
440 * @param op intersection operation
441 */
442static void
443send_bloomfilter (struct Operation *op)
444{
445 struct GNUNET_MQ_Envelope *ev;
446 struct BFMessage *msg;
447 uint32_t bf_size;
448 uint32_t bf_elementbits;
449 uint32_t chunk_size;
450 char *bf_data;
451 uint32_t offset;
452
453 /* We consider the ratio of the set sizes to determine
454 the number of bits per element, as the smaller set
455 should use more bits to maximize its set reduction
456 potential and minimize overall bandwidth consumption. */
457 bf_elementbits = 2 + ceil (log2 ((double)
458 (op->remote_element_count
459 / (double) op->state->my_element_count)));
460 if (bf_elementbits < 1)
461 bf_elementbits = 1; /* make sure k is not 0 */
462 /* optimize BF-size to ~50% of bits set */
463 bf_size = ceil ((double) (op->state->my_element_count
464 * bf_elementbits / log (2)));
465 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
466 "Sending Bloom filter (%u) of size %u bytes\n",
467 (unsigned int) bf_elementbits,
468 (unsigned int) bf_size);
469 op->state->local_bf = GNUNET_CONTAINER_bloomfilter_init (NULL,
470 bf_size,
471 bf_elementbits);
472 op->state->salt = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
473 UINT32_MAX);
474 GNUNET_CONTAINER_multihashmap_iterate (op->state->my_elements,
475 &iterator_bf_create,
476 op);
477
478 /* send our Bloom filter */
479 GNUNET_STATISTICS_update (_GSS_statistics,
480 "# Intersection Bloom filters sent",
481 1,
482 GNUNET_NO);
483 chunk_size = 60 * 1024 - sizeof(struct BFMessage);
484 if (bf_size <= chunk_size)
485 {
486 /* singlepart */
487 chunk_size = bf_size;
488 ev = GNUNET_MQ_msg_extra (msg,
489 chunk_size,
490 GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_BF);
491 GNUNET_assert (GNUNET_SYSERR !=
492 GNUNET_CONTAINER_bloomfilter_get_raw_data (
493 op->state->local_bf,
494 (char *) &msg[1],
495 bf_size));
496 msg->sender_element_count = htonl (op->state->my_element_count);
497 msg->bloomfilter_total_length = htonl (bf_size);
498 msg->bits_per_element = htonl (bf_elementbits);
499 msg->sender_mutator = htonl (op->state->salt);
500 msg->element_xor_hash = op->state->my_xor;
501 GNUNET_MQ_send (op->mq, ev);
502 }
503 else
504 {
505 /* multipart */
506 bf_data = GNUNET_malloc (bf_size);
507 GNUNET_assert (GNUNET_SYSERR !=
508 GNUNET_CONTAINER_bloomfilter_get_raw_data (
509 op->state->local_bf,
510 bf_data,
511 bf_size));
512 offset = 0;
513 while (offset < bf_size)
514 {
515 if (bf_size - chunk_size < offset)
516 chunk_size = bf_size - offset;
517 ev = GNUNET_MQ_msg_extra (msg,
518 chunk_size,
519 GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_BF);
520 GNUNET_memcpy (&msg[1],
521 &bf_data[offset],
522 chunk_size);
523 offset += chunk_size;
524 msg->sender_element_count = htonl (op->state->my_element_count);
525 msg->bloomfilter_total_length = htonl (bf_size);
526 msg->bits_per_element = htonl (bf_elementbits);
527 msg->sender_mutator = htonl (op->state->salt);
528 msg->element_xor_hash = op->state->my_xor;
529 GNUNET_MQ_send (op->mq, ev);
530 }
531 GNUNET_free (bf_data);
532 }
533 GNUNET_CONTAINER_bloomfilter_free (op->state->local_bf);
534 op->state->local_bf = NULL;
535}
536
537
538/**
539 * Signal to the client that the operation has finished and
540 * destroy the operation.
541 *
542 * @param cls operation to destroy
543 */
544static void
545send_client_done_and_destroy (void *cls)
546{
547 struct Operation *op = cls;
548 struct GNUNET_MQ_Envelope *ev;
549 struct GNUNET_SET_ResultMessage *rm;
550
551 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
552 "Intersection succeeded, sending DONE to local client\n");
553 GNUNET_STATISTICS_update (_GSS_statistics,
554 "# Intersection operations succeeded",
555 1,
556 GNUNET_NO);
557 ev = GNUNET_MQ_msg (rm,
558 GNUNET_MESSAGE_TYPE_SET_RESULT);
559 rm->request_id = htonl (op->client_request_id);
560 rm->result_status = htons (GNUNET_SET_STATUS_DONE);
561 rm->element_type = htons (0);
562 GNUNET_MQ_send (op->set->cs->mq,
563 ev);
564 _GSS_operation_destroy (op,
565 GNUNET_YES);
566}
567
568
569/**
570 * Remember that we are done dealing with the local client
571 * AND have sent the other peer our message that we are done,
572 * so we are not just waiting for the channel to die before
573 * telling the local client that we are done as our last act.
574 *
575 * @param cls the `struct Operation`.
576 */
577static void
578finished_local_operations (void *cls)
579{
580 struct Operation *op = cls;
581
582 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
583 "DONE sent to other peer, now waiting for other end to close the channel\n");
584 op->state->phase = PHASE_FINISHED;
585 op->state->channel_death_expected = GNUNET_YES;
586}
587
588
589/**
590 * Notify the other peer that we are done. Once this message
591 * is out, we still need to notify the local client that we
592 * are done.
593 *
594 * @param op operation to notify for.
595 */
596static void
597send_p2p_done (struct Operation *op)
598{
599 struct GNUNET_MQ_Envelope *ev;
600 struct IntersectionDoneMessage *idm;
601
602 GNUNET_assert (PHASE_MUST_SEND_DONE == op->state->phase);
603 GNUNET_assert (GNUNET_NO == op->state->channel_death_expected);
604 ev = GNUNET_MQ_msg (idm,
605 GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_DONE);
606 idm->final_element_count = htonl (op->state->my_element_count);
607 idm->element_xor_hash = op->state->my_xor;
608 GNUNET_MQ_notify_sent (ev,
609 &finished_local_operations,
610 op);
611 GNUNET_MQ_send (op->mq,
612 ev);
613}
614
615
616/**
617 * Send all elements in the full result iterator.
618 *
619 * @param cls the `struct Operation *`
620 */
621static void
622send_remaining_elements (void *cls)
623{
624 struct Operation *op = cls;
625 const void *nxt;
626 const struct ElementEntry *ee;
627 struct GNUNET_MQ_Envelope *ev;
628 struct GNUNET_SET_ResultMessage *rm;
629 const struct GNUNET_SET_Element *element;
630 int res;
631
632 res = GNUNET_CONTAINER_multihashmap_iterator_next (
633 op->state->full_result_iter,
634 NULL,
635 &nxt);
636 if (GNUNET_NO == res)
637 {
638 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
639 "Sending done and destroy because iterator ran out\n");
640 GNUNET_CONTAINER_multihashmap_iterator_destroy (
641 op->state->full_result_iter);
642 op->state->full_result_iter = NULL;
643 if (PHASE_DONE_RECEIVED == op->state->phase)
644 {
645 op->state->phase = PHASE_FINISHED;
646 send_client_done_and_destroy (op);
647 }
648 else if (PHASE_MUST_SEND_DONE == op->state->phase)
649 {
650 send_p2p_done (op);
651 }
652 else
653 {
654 GNUNET_assert (0);
655 }
656 return;
657 }
658 ee = nxt;
659 element = &ee->element;
660 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
661 "Sending element %s:%u to client (full set)\n",
662 GNUNET_h2s (&ee->element_hash),
663 element->size);
664 GNUNET_assert (0 != op->client_request_id);
665 ev = GNUNET_MQ_msg_extra (rm,
666 element->size,
667 GNUNET_MESSAGE_TYPE_SET_RESULT);
668 GNUNET_assert (NULL != ev);
669 rm->result_status = htons (GNUNET_SET_STATUS_OK);
670 rm->request_id = htonl (op->client_request_id);
671 rm->element_type = element->element_type;
672 GNUNET_memcpy (&rm[1],
673 element->data,
674 element->size);
675 GNUNET_MQ_notify_sent (ev,
676 &send_remaining_elements,
677 op);
678 GNUNET_MQ_send (op->set->cs->mq,
679 ev);
680}
681
682
683/**
684 * Fills the "my_elements" hashmap with the initial set of
685 * (non-deleted) elements from the set of the specification.
686 *
687 * @param cls closure with the `struct Operation *`
688 * @param key current key code for the element
689 * @param value value in the hash map with the `struct ElementEntry *`
690 * @return #GNUNET_YES (we should continue to iterate)
691 */
692static int
693initialize_map_unfiltered (void *cls,
694 const struct GNUNET_HashCode *key,
695 void *value)
696{
697 struct ElementEntry *ee = value;
698 struct Operation *op = cls;
699
700 if (GNUNET_NO == _GSS_is_element_of_operation (ee, op))
701 return GNUNET_YES; /* element not live in operation's generation */
702 GNUNET_CRYPTO_hash_xor (&op->state->my_xor,
703 &ee->element_hash,
704 &op->state->my_xor);
705 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
706 "Initial full initialization of my_elements, adding %s:%u\n",
707 GNUNET_h2s (&ee->element_hash),
708 ee->element.size);
709 GNUNET_break (GNUNET_YES ==
710 GNUNET_CONTAINER_multihashmap_put (op->state->my_elements,
711 &ee->element_hash,
712 ee,
713 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
714 return GNUNET_YES;
715}
716
717
718/**
719 * Send our element count to the peer, in case our element count is
720 * lower than theirs.
721 *
722 * @param op intersection operation
723 */
724static void
725send_element_count (struct Operation *op)
726{
727 struct GNUNET_MQ_Envelope *ev;
728 struct IntersectionElementInfoMessage *msg;
729
730 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
731 "Sending our element count (%u)\n",
732 op->state->my_element_count);
733 ev = GNUNET_MQ_msg (msg,
734 GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_ELEMENT_INFO);
735 msg->sender_element_count = htonl (op->state->my_element_count);
736 GNUNET_MQ_send (op->mq, ev);
737}
738
739
740/**
741 * We go first, initialize our map with all elements and
742 * send the first Bloom filter.
743 *
744 * @param op operation to start exchange for
745 */
746static void
747begin_bf_exchange (struct Operation *op)
748{
749 op->state->phase = PHASE_BF_EXCHANGE;
750 GNUNET_CONTAINER_multihashmap_iterate (op->set->content->elements,
751 &initialize_map_unfiltered,
752 op);
753 send_bloomfilter (op);
754}
755
756
757void
758handle_intersection_p2p_element_info (void *cls,
759 const struct
760 IntersectionElementInfoMessage *msg)
761{
762 struct Operation *op = cls;
763
764 if (GNUNET_SET_OPERATION_INTERSECTION != op->set->operation)
765 {
766 GNUNET_break_op (0);
767 fail_intersection_operation (op);
768 return;
769 }
770 op->remote_element_count = ntohl (msg->sender_element_count);
771 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
772 "Received remote element count (%u), I have %u\n",
773 op->remote_element_count,
774 op->state->my_element_count);
775 if (((PHASE_INITIAL != op->state->phase) &&
776 (PHASE_COUNT_SENT != op->state->phase)) ||
777 (op->state->my_element_count > op->remote_element_count) ||
778 (0 == op->state->my_element_count) ||
779 (0 == op->remote_element_count))
780 {
781 GNUNET_break_op (0);
782 fail_intersection_operation (op);
783 return;
784 }
785 GNUNET_break (NULL == op->state->remote_bf);
786 begin_bf_exchange (op);
787 GNUNET_CADET_receive_done (op->channel);
788}
789
790
791/**
792 * Process a Bloomfilter once we got all the chunks.
793 *
794 * @param op the intersection operation
795 */
796static void
797process_bf (struct Operation *op)
798{
799 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
800 "Received BF in phase %u, foreign count is %u, my element count is %u/%u\n",
801 op->state->phase,
802 op->remote_element_count,
803 op->state->my_element_count,
804 GNUNET_CONTAINER_multihashmap_size (op->set->content->elements));
805 switch (op->state->phase)
806 {
807 case PHASE_INITIAL:
808 GNUNET_break_op (0);
809 fail_intersection_operation (op);
810 return;
811
812 case PHASE_COUNT_SENT:
813 /* This is the first BF being sent, build our initial map with
814 filtering in place */
815 op->state->my_element_count = 0;
816 GNUNET_CONTAINER_multihashmap_iterate (op->set->content->elements,
817 &filtered_map_initialization,
818 op);
819 break;
820
821 case PHASE_BF_EXCHANGE:
822 /* Update our set by reduction */
823 GNUNET_CONTAINER_multihashmap_iterate (op->state->my_elements,
824 &iterator_bf_reduce,
825 op);
826 break;
827
828 case PHASE_MUST_SEND_DONE:
829 GNUNET_break_op (0);
830 fail_intersection_operation (op);
831 return;
832
833 case PHASE_DONE_RECEIVED:
834 GNUNET_break_op (0);
835 fail_intersection_operation (op);
836 return;
837
838 case PHASE_FINISHED:
839 GNUNET_break_op (0);
840 fail_intersection_operation (op);
841 return;
842 }
843 GNUNET_CONTAINER_bloomfilter_free (op->state->remote_bf);
844 op->state->remote_bf = NULL;
845
846 if ((0 == op->state->my_element_count) || /* fully disjoint */
847 ((op->state->my_element_count == op->remote_element_count) &&
848 (0 == GNUNET_memcmp (&op->state->my_xor,
849 &op->state->other_xor))))
850 {
851 /* we are done */
852 op->state->phase = PHASE_MUST_SEND_DONE;
853 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
854 "Intersection succeeded, sending DONE to other peer\n");
855 GNUNET_CONTAINER_bloomfilter_free (op->state->local_bf);
856 op->state->local_bf = NULL;
857 if (GNUNET_SET_RESULT_FULL == op->result_mode)
858 {
859 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
860 "Sending full result set (%u elements)\n",
861 GNUNET_CONTAINER_multihashmap_size (op->state->my_elements));
862 op->state->full_result_iter
863 = GNUNET_CONTAINER_multihashmap_iterator_create (
864 op->state->my_elements);
865 send_remaining_elements (op);
866 return;
867 }
868 send_p2p_done (op);
869 return;
870 }
871 op->state->phase = PHASE_BF_EXCHANGE;
872 send_bloomfilter (op);
873}
874
875
876/**
877 * Check an BF message from a remote peer.
878 *
879 * @param cls the intersection operation
880 * @param msg the header of the message
881 * @return #GNUNET_OK if @a msg is well-formed
882 */
883int
884check_intersection_p2p_bf (void *cls,
885 const struct BFMessage *msg)
886{
887 struct Operation *op = cls;
888
889 if (GNUNET_SET_OPERATION_INTERSECTION != op->set->operation)
890 {
891 GNUNET_break_op (0);
892 return GNUNET_SYSERR;
893 }
894 return GNUNET_OK;
895}
896
897
898/**
899 * Handle an BF message from a remote peer.
900 *
901 * @param cls the intersection operation
902 * @param msg the header of the message
903 */
904void
905handle_intersection_p2p_bf (void *cls,
906 const struct BFMessage *msg)
907{
908 struct Operation *op = cls;
909 uint32_t bf_size;
910 uint32_t chunk_size;
911 uint32_t bf_bits_per_element;
912
913 switch (op->state->phase)
914 {
915 case PHASE_INITIAL:
916 GNUNET_break_op (0);
917 fail_intersection_operation (op);
918 return;
919
920 case PHASE_COUNT_SENT:
921 case PHASE_BF_EXCHANGE:
922 bf_size = ntohl (msg->bloomfilter_total_length);
923 bf_bits_per_element = ntohl (msg->bits_per_element);
924 chunk_size = htons (msg->header.size) - sizeof(struct BFMessage);
925 op->state->other_xor = msg->element_xor_hash;
926 if (bf_size == chunk_size)
927 {
928 if (NULL != op->state->bf_data)
929 {
930 GNUNET_break_op (0);
931 fail_intersection_operation (op);
932 return;
933 }
934 /* single part, done here immediately */
935 op->state->remote_bf
936 = GNUNET_CONTAINER_bloomfilter_init ((const char *) &msg[1],
937 bf_size,
938 bf_bits_per_element);
939 op->state->salt = ntohl (msg->sender_mutator);
940 op->remote_element_count = ntohl (msg->sender_element_count);
941 process_bf (op);
942 break;
943 }
944 /* multipart chunk */
945 if (NULL == op->state->bf_data)
946 {
947 /* first chunk, initialize */
948 op->state->bf_data = GNUNET_malloc (bf_size);
949 op->state->bf_data_size = bf_size;
950 op->state->bf_bits_per_element = bf_bits_per_element;
951 op->state->bf_data_offset = 0;
952 op->state->salt = ntohl (msg->sender_mutator);
953 op->remote_element_count = ntohl (msg->sender_element_count);
954 }
955 else
956 {
957 /* increment */
958 if ((op->state->bf_data_size != bf_size) ||
959 (op->state->bf_bits_per_element != bf_bits_per_element) ||
960 (op->state->bf_data_offset + chunk_size > bf_size) ||
961 (op->state->salt != ntohl (msg->sender_mutator)) ||
962 (op->remote_element_count != ntohl (msg->sender_element_count)))
963 {
964 GNUNET_break_op (0);
965 fail_intersection_operation (op);
966 return;
967 }
968 }
969 GNUNET_memcpy (&op->state->bf_data[op->state->bf_data_offset],
970 (const char *) &msg[1],
971 chunk_size);
972 op->state->bf_data_offset += chunk_size;
973 if (op->state->bf_data_offset == bf_size)
974 {
975 /* last chunk, run! */
976 op->state->remote_bf
977 = GNUNET_CONTAINER_bloomfilter_init (op->state->bf_data,
978 bf_size,
979 bf_bits_per_element);
980 GNUNET_free (op->state->bf_data);
981 op->state->bf_data = NULL;
982 op->state->bf_data_size = 0;
983 process_bf (op);
984 }
985 break;
986
987 default:
988 GNUNET_break_op (0);
989 fail_intersection_operation (op);
990 return;
991 }
992 GNUNET_CADET_receive_done (op->channel);
993}
994
995
996/**
997 * Remove all elements from our hashmap.
998 *
999 * @param cls closure with the `struct Operation *`
1000 * @param key current key code
1001 * @param value value in the hash map
1002 * @return #GNUNET_YES (we should continue to iterate)
1003 */
1004static int
1005filter_all (void *cls,
1006 const struct GNUNET_HashCode *key,
1007 void *value)
1008{
1009 struct Operation *op = cls;
1010 struct ElementEntry *ee = value;
1011
1012 GNUNET_break (0 < op->state->my_element_count);
1013 op->state->my_element_count--;
1014 GNUNET_CRYPTO_hash_xor (&op->state->my_xor,
1015 &ee->element_hash,
1016 &op->state->my_xor);
1017 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1018 "Final reduction of my_elements, removing %s:%u\n",
1019 GNUNET_h2s (&ee->element_hash),
1020 ee->element.size);
1021 GNUNET_assert (GNUNET_YES ==
1022 GNUNET_CONTAINER_multihashmap_remove (op->state->my_elements,
1023 &ee->element_hash,
1024 ee));
1025 send_client_removed_element (op,
1026 &ee->element);
1027 return GNUNET_YES;
1028}
1029
1030
1031/**
1032 * Handle a done message from a remote peer
1033 *
1034 * @param cls the intersection operation
1035 * @param idm the message
1036 */
1037void
1038handle_intersection_p2p_done (void *cls,
1039 const struct IntersectionDoneMessage *idm)
1040{
1041 struct Operation *op = cls;
1042
1043 if (GNUNET_SET_OPERATION_INTERSECTION != op->set->operation)
1044 {
1045 GNUNET_break_op (0);
1046 fail_intersection_operation (op);
1047 return;
1048 }
1049 if (PHASE_BF_EXCHANGE != op->state->phase)
1050 {
1051 /* wrong phase to conclude? FIXME: Or should we allow this
1052 if the other peer has _initially_ already an empty set? */
1053 GNUNET_break_op (0);
1054 fail_intersection_operation (op);
1055 return;
1056 }
1057 if (0 == ntohl (idm->final_element_count))
1058 {
1059 /* other peer determined empty set is the intersection,
1060 remove all elements */
1061 GNUNET_CONTAINER_multihashmap_iterate (op->state->my_elements,
1062 &filter_all,
1063 op);
1064 }
1065 if ((op->state->my_element_count != ntohl (idm->final_element_count)) ||
1066 (0 != GNUNET_memcmp (&op->state->my_xor,
1067 &idm->element_xor_hash)))
1068 {
1069 /* Other peer thinks we are done, but we disagree on the result! */
1070 GNUNET_break_op (0);
1071 fail_intersection_operation (op);
1072 return;
1073 }
1074 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1075 "Got IntersectionDoneMessage, have %u elements in intersection\n",
1076 op->state->my_element_count);
1077 op->state->phase = PHASE_DONE_RECEIVED;
1078 GNUNET_CADET_receive_done (op->channel);
1079
1080 GNUNET_assert (GNUNET_NO == op->state->client_done_sent);
1081 if (GNUNET_SET_RESULT_FULL == op->result_mode)
1082 {
1083 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1084 "Sending full result set to client (%u elements)\n",
1085 GNUNET_CONTAINER_multihashmap_size (op->state->my_elements));
1086 op->state->full_result_iter
1087 = GNUNET_CONTAINER_multihashmap_iterator_create (op->state->my_elements);
1088 send_remaining_elements (op);
1089 return;
1090 }
1091 op->state->phase = PHASE_FINISHED;
1092 send_client_done_and_destroy (op);
1093}
1094
1095
1096/**
1097 * Initiate a set intersection operation with a remote peer.
1098 *
1099 * @param op operation that is created, should be initialized to
1100 * begin the evaluation
1101 * @param opaque_context message to be transmitted to the listener
1102 * to convince it to accept, may be NULL
1103 * @return operation-specific state to keep in @a op
1104 */
1105static struct OperationState *
1106intersection_evaluate (struct Operation *op,
1107 const struct GNUNET_MessageHeader *opaque_context)
1108{
1109 struct OperationState *state;
1110 struct GNUNET_MQ_Envelope *ev;
1111 struct OperationRequestMessage *msg;
1112
1113 ev = GNUNET_MQ_msg_nested_mh (msg,
1114 GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST,
1115 opaque_context);
1116 if (NULL == ev)
1117 {
1118 /* the context message is too large!? */
1119 GNUNET_break (0);
1120 return NULL;
1121 }
1122 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1123 "Initiating intersection operation evaluation\n");
1124 state = GNUNET_new (struct OperationState);
1125 /* we started the operation, thus we have to send the operation request */
1126 state->phase = PHASE_INITIAL;
1127 state->my_element_count = op->set->state->current_set_element_count;
1128 state->my_elements
1129 = GNUNET_CONTAINER_multihashmap_create (state->my_element_count,
1130 GNUNET_YES);
1131
1132 msg->operation = htonl (GNUNET_SET_OPERATION_INTERSECTION);
1133 msg->element_count = htonl (state->my_element_count);
1134 GNUNET_MQ_send (op->mq,
1135 ev);
1136 state->phase = PHASE_COUNT_SENT;
1137 if (NULL != opaque_context)
1138 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1139 "Sent op request with context message\n");
1140 else
1141 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1142 "Sent op request without context message\n");
1143 return state;
1144}
1145
1146
1147/**
1148 * Accept an intersection operation request from a remote peer. Only
1149 * initializes the private operation state.
1150 *
1151 * @param op operation that will be accepted as an intersection operation
1152 */
1153static struct OperationState *
1154intersection_accept (struct Operation *op)
1155{
1156 struct OperationState *state;
1157
1158 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1159 "Accepting set intersection operation\n");
1160 state = GNUNET_new (struct OperationState);
1161 state->phase = PHASE_INITIAL;
1162 state->my_element_count
1163 = op->set->state->current_set_element_count;
1164 state->my_elements
1165 = GNUNET_CONTAINER_multihashmap_create (GNUNET_MIN (state->my_element_count,
1166 op->remote_element_count),
1167 GNUNET_YES);
1168 op->state = state;
1169 if (op->remote_element_count < state->my_element_count)
1170 {
1171 /* If the other peer (Alice) has fewer elements than us (Bob),
1172 we just send the count as Alice should send the first BF */
1173 send_element_count (op);
1174 state->phase = PHASE_COUNT_SENT;
1175 return state;
1176 }
1177 /* We have fewer elements, so we start with the BF */
1178 begin_bf_exchange (op);
1179 return state;
1180}
1181
1182
1183/**
1184 * Destroy the intersection operation. Only things specific to the
1185 * intersection operation are destroyed.
1186 *
1187 * @param op intersection operation to destroy
1188 */
1189static void
1190intersection_op_cancel (struct Operation *op)
1191{
1192 /* check if the op was canceled twice */
1193 GNUNET_assert (NULL != op->state);
1194 if (NULL != op->state->remote_bf)
1195 {
1196 GNUNET_CONTAINER_bloomfilter_free (op->state->remote_bf);
1197 op->state->remote_bf = NULL;
1198 }
1199 if (NULL != op->state->local_bf)
1200 {
1201 GNUNET_CONTAINER_bloomfilter_free (op->state->local_bf);
1202 op->state->local_bf = NULL;
1203 }
1204 if (NULL != op->state->my_elements)
1205 {
1206 GNUNET_CONTAINER_multihashmap_destroy (op->state->my_elements);
1207 op->state->my_elements = NULL;
1208 }
1209 if (NULL != op->state->full_result_iter)
1210 {
1211 GNUNET_CONTAINER_multihashmap_iterator_destroy (
1212 op->state->full_result_iter);
1213 op->state->full_result_iter = NULL;
1214 }
1215 GNUNET_free (op->state);
1216 op->state = NULL;
1217 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1218 "Destroying intersection op state done\n");
1219}
1220
1221
1222/**
1223 * Create a new set supporting the intersection operation.
1224 *
1225 * @return the newly created set
1226 */
1227static struct SetState *
1228intersection_set_create ()
1229{
1230 struct SetState *set_state;
1231
1232 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1233 "Intersection set created\n");
1234 set_state = GNUNET_new (struct SetState);
1235 set_state->current_set_element_count = 0;
1236
1237 return set_state;
1238}
1239
1240
1241/**
1242 * Add the element from the given element message to the set.
1243 *
1244 * @param set_state state of the set want to add to
1245 * @param ee the element to add to the set
1246 */
1247static void
1248intersection_add (struct SetState *set_state,
1249 struct ElementEntry *ee)
1250{
1251 set_state->current_set_element_count++;
1252}
1253
1254
1255/**
1256 * Destroy a set that supports the intersection operation
1257 *
1258 * @param set_state the set to destroy
1259 */
1260static void
1261intersection_set_destroy (struct SetState *set_state)
1262{
1263 GNUNET_free (set_state);
1264}
1265
1266
1267/**
1268 * Remove the element given in the element message from the set.
1269 *
1270 * @param set_state state of the set to remove from
1271 * @param element set element to remove
1272 */
1273static void
1274intersection_remove (struct SetState *set_state,
1275 struct ElementEntry *element)
1276{
1277 GNUNET_assert (0 < set_state->current_set_element_count);
1278 set_state->current_set_element_count--;
1279}
1280
1281
1282/**
1283 * Callback for channel death for the intersection operation.
1284 *
1285 * @param op operation that lost the channel
1286 */
1287static void
1288intersection_channel_death (struct Operation *op)
1289{
1290 if (GNUNET_YES == op->state->channel_death_expected)
1291 {
1292 /* oh goodie, we are done! */
1293 send_client_done_and_destroy (op);
1294 }
1295 else
1296 {
1297 /* sorry, channel went down early, too bad. */
1298 _GSS_operation_destroy (op,
1299 GNUNET_YES);
1300 }
1301}
1302
1303
1304/**
1305 * Get the table with implementing functions for set intersection.
1306 *
1307 * @return the operation specific VTable
1308 */
1309const struct SetVT *
1310_GSS_intersection_vt ()
1311{
1312 static const struct SetVT intersection_vt = {
1313 .create = &intersection_set_create,
1314 .add = &intersection_add,
1315 .remove = &intersection_remove,
1316 .destroy_set = &intersection_set_destroy,
1317 .evaluate = &intersection_evaluate,
1318 .accept = &intersection_accept,
1319 .cancel = &intersection_op_cancel,
1320 .channel_death = &intersection_channel_death,
1321 };
1322
1323 return &intersection_vt;
1324}
diff --git a/src/contrib/service/set/gnunet-service-set_intersection.h b/src/contrib/service/set/gnunet-service-set_intersection.h
new file mode 100644
index 000000000..d8884d0a7
--- /dev/null
+++ b/src/contrib/service/set/gnunet-service-set_intersection.h
@@ -0,0 +1,79 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013-2017 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file set/gnunet-service-set_intersection.h
22 * @brief two-peer set operations
23 * @author Florian Dold
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_SET_INTERSECTION_H
27#define GNUNET_SERVICE_SET_INTERSECTION_H
28
29#include "gnunet-service-set.h"
30
31
32/**
33 * Check an BF message from a remote peer.
34 *
35 * @param cls the intersection operation
36 * @param msg the header of the message
37 * @return #GNUNET_OK if @a msg is well-formed
38 */
39int
40check_intersection_p2p_bf (void *cls,
41 const struct BFMessage *msg);
42
43
44/**
45 * Handle an BF message from a remote peer.
46 *
47 * @param cls the intersection operation
48 * @param msg the header of the message
49 */
50void
51handle_intersection_p2p_bf (void *cls,
52 const struct BFMessage *msg);
53
54
55/**
56 * Handle the initial `struct IntersectionElementInfoMessage` from a
57 * remote peer.
58 *
59 * @param cls the intersection operation
60 * @param msg the header of the message
61 */
62void
63handle_intersection_p2p_element_info (void *cls,
64 const struct
65 IntersectionElementInfoMessage *msg);
66
67
68/**
69 * Handle a done message from a remote peer
70 *
71 * @param cls the intersection operation
72 * @param mh the message
73 */
74void
75handle_intersection_p2p_done (void *cls,
76 const struct IntersectionDoneMessage *idm);
77
78
79#endif
diff --git a/src/contrib/service/set/gnunet-service-set_protocol.h b/src/contrib/service/set/gnunet-service-set_protocol.h
new file mode 100644
index 000000000..a2803ee47
--- /dev/null
+++ b/src/contrib/service/set/gnunet-service-set_protocol.h
@@ -0,0 +1,226 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013, 2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @author Florian Dold
22 * @author Christian Grothoff
23 * @file set/gnunet-service-set_protocol.h
24 * @brief Peer-to-Peer messages for gnunet set
25 */
26#ifndef SET_PROTOCOL_H
27#define SET_PROTOCOL_H
28
29#include "platform.h"
30#include "gnunet_common.h"
31
32
33GNUNET_NETWORK_STRUCT_BEGIN
34
35struct OperationRequestMessage
36{
37 /**
38 * Type: #GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST
39 */
40 struct GNUNET_MessageHeader header;
41
42 /**
43 * Operation to request, values from `enum GNUNET_SET_OperationType`
44 */
45 uint32_t operation GNUNET_PACKED;
46
47 /**
48 * For Intersection: my element count
49 */
50 uint32_t element_count GNUNET_PACKED;
51
52 /**
53 * Application-specific identifier of the request.
54 */
55 struct GNUNET_HashCode app_idX;
56
57 /* rest: optional message */
58};
59
60
61/**
62 * Message containing buckets of an invertible bloom filter.
63 *
64 * If an IBF has too many buckets for an IBF message,
65 * it is split into multiple messages.
66 */
67struct IBFMessage
68{
69 /**
70 * Type: #GNUNET_MESSAGE_TYPE_SET_UNION_P2P_IBF
71 */
72 struct GNUNET_MessageHeader header;
73
74 /**
75 * Order of the whole ibf, where
76 * num_buckets = 2^order
77 */
78 uint8_t order;
79
80 /**
81 * Padding, must be 0.
82 */
83 uint8_t reserved1;
84
85 /**
86 * Padding, must be 0.
87 */
88 uint16_t reserved2 GNUNET_PACKED;
89
90 /**
91 * Offset of the strata in the rest of the message
92 */
93 uint32_t offset GNUNET_PACKED;
94
95 /**
96 * Salt used when hashing elements for this IBF.
97 */
98 uint32_t salt GNUNET_PACKED;
99
100 /* rest: buckets */
101};
102
103
104struct InquiryMessage
105{
106 /**
107 * Type: #GNUNET_MESSAGE_TYPE_SET_UNION_P2P_IBF
108 */
109 struct GNUNET_MessageHeader header;
110
111 /**
112 * Salt used when hashing elements for this inquiry.
113 */
114 uint32_t salt GNUNET_PACKED;
115
116 /**
117 * Reserved, set to 0.
118 */
119 uint32_t reserved GNUNET_PACKED;
120
121 /* rest: inquiry IBF keys */
122};
123
124
125/**
126 * During intersection, the first (and possibly second) message
127 * send it the number of elements in the set, to allow the peers
128 * to decide who should start with the Bloom filter.
129 */
130struct IntersectionElementInfoMessage
131{
132 /**
133 * Type: #GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_ELEMENT_INFO
134 */
135 struct GNUNET_MessageHeader header;
136
137 /**
138 * mutator used with this bloomfilter.
139 */
140 uint32_t sender_element_count GNUNET_PACKED;
141};
142
143
144/**
145 * Bloom filter messages exchanged for set intersection calculation.
146 */
147struct BFMessage
148{
149 /**
150 * Type: #GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_BF
151 */
152 struct GNUNET_MessageHeader header;
153
154 /**
155 * Number of elements the sender still has in the set.
156 */
157 uint32_t sender_element_count GNUNET_PACKED;
158
159 /**
160 * XOR of all hashes over all elements remaining in the set.
161 * Used to determine termination.
162 */
163 struct GNUNET_HashCode element_xor_hash;
164
165 /**
166 * Mutator used with this bloomfilter.
167 */
168 uint32_t sender_mutator GNUNET_PACKED;
169
170 /**
171 * Total length of the bloomfilter data.
172 */
173 uint32_t bloomfilter_total_length GNUNET_PACKED;
174
175 /**
176 * Number of bits (k-value) used in encoding the bloomfilter.
177 */
178 uint32_t bits_per_element GNUNET_PACKED;
179
180 /**
181 * rest: the sender's bloomfilter
182 */
183};
184
185
186/**
187 * Last message, send to confirm the final set. Contains the element
188 * count as it is possible that the peer determined that we were done
189 * by getting the empty set, which in that case also needs to be
190 * communicated.
191 */
192struct IntersectionDoneMessage
193{
194 /**
195 * Type: #GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_DONE
196 */
197 struct GNUNET_MessageHeader header;
198
199 /**
200 * Final number of elements in intersection.
201 */
202 uint32_t final_element_count GNUNET_PACKED;
203
204 /**
205 * XOR of all hashes over all elements remaining in the set.
206 */
207 struct GNUNET_HashCode element_xor_hash;
208};
209
210
211/**
212 * Strata estimator together with the peer's overall set size.
213 */
214struct StrataEstimatorMessage
215{
216 /**
217 * Type: #GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SE(C)
218 */
219 struct GNUNET_MessageHeader header;
220
221 uint64_t set_size;
222};
223
224GNUNET_NETWORK_STRUCT_END
225
226#endif
diff --git a/src/contrib/service/set/gnunet-service-set_union.c b/src/contrib/service/set/gnunet-service-set_union.c
new file mode 100644
index 000000000..3a2bc8bd7
--- /dev/null
+++ b/src/contrib/service/set/gnunet-service-set_union.c
@@ -0,0 +1,2463 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013-2017 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file set/gnunet-service-set_union.c
22 * @brief two-peer set operations
23 * @author Florian Dold
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_statistics_service.h"
29#include "gnunet-service-set.h"
30#include "ibf.h"
31#include "gnunet-service-set_union.h"
32#include "gnunet-service-set_union_strata_estimator.h"
33#include "gnunet-service-set_protocol.h"
34#include <gcrypt.h>
35
36
37#define LOG(kind, ...) GNUNET_log_from (kind, "set-union", __VA_ARGS__)
38
39
40/**
41 * Number of IBFs in a strata estimator.
42 */
43#define SE_STRATA_COUNT 32
44
45/**
46 * Size of the IBFs in the strata estimator.
47 */
48#define SE_IBF_SIZE 80
49
50/**
51 * The hash num parameter for the difference digests and strata estimators.
52 */
53#define SE_IBF_HASH_NUM 4
54
55/**
56 * Number of buckets that can be transmitted in one message.
57 */
58#define MAX_BUCKETS_PER_MESSAGE ((1 << 15) / IBF_BUCKET_SIZE)
59
60/**
61 * The maximum size of an ibf we use is 2^(MAX_IBF_ORDER).
62 * Choose this value so that computing the IBF is still cheaper
63 * than transmitting all values.
64 */
65#define MAX_IBF_ORDER (20)
66
67/**
68 * Number of buckets used in the ibf per estimated
69 * difference.
70 */
71#define IBF_ALPHA 4
72
73
74/**
75 * Current phase we are in for a union operation.
76 */
77enum UnionOperationPhase
78{
79 /**
80 * We sent the request message, and expect a strata estimator.
81 */
82 PHASE_EXPECT_SE,
83
84 /**
85 * We sent the strata estimator, and expect an IBF. This phase is entered once
86 * upon initialization and later via #PHASE_EXPECT_ELEMENTS_AND_REQUESTS.
87 *
88 * XXX: could use better wording.
89 * XXX: repurposed to also expect a "request full set" message, should be renamed
90 *
91 * After receiving the complete IBF, we enter #PHASE_EXPECT_ELEMENTS
92 */
93 PHASE_EXPECT_IBF,
94
95 /**
96 * Continuation for multi part IBFs.
97 */
98 PHASE_EXPECT_IBF_CONT,
99
100 /**
101 * We are decoding an IBF.
102 */
103 PHASE_INVENTORY_ACTIVE,
104
105 /**
106 * The other peer is decoding the IBF we just sent.
107 */
108 PHASE_INVENTORY_PASSIVE,
109
110 /**
111 * The protocol is almost finished, but we still have to flush our message
112 * queue and/or expect some elements.
113 */
114 PHASE_FINISH_CLOSING,
115
116 /**
117 * In the penultimate phase,
118 * we wait until all our demands
119 * are satisfied. Then we send a done
120 * message, and wait for another done message.
121 */
122 PHASE_FINISH_WAITING,
123
124 /**
125 * In the ultimate phase, we wait until
126 * our demands are satisfied and then
127 * quit (sending another DONE message).
128 */
129 PHASE_DONE,
130
131 /**
132 * After sending the full set, wait for responses with the elements
133 * that the local peer is missing.
134 */
135 PHASE_FULL_SENDING,
136};
137
138
139/**
140 * State of an evaluate operation with another peer.
141 */
142struct OperationState
143{
144 /**
145 * Copy of the set's strata estimator at the time of
146 * creation of this operation.
147 */
148 struct StrataEstimator *se;
149
150 /**
151 * The IBF we currently receive.
152 */
153 struct InvertibleBloomFilter *remote_ibf;
154
155 /**
156 * The IBF with the local set's element.
157 */
158 struct InvertibleBloomFilter *local_ibf;
159
160 /**
161 * Maps unsalted IBF-Keys to elements.
162 * Used as a multihashmap, the keys being the lower 32bit of the IBF-Key.
163 * Colliding IBF-Keys are linked.
164 */
165 struct GNUNET_CONTAINER_MultiHashMap32 *key_to_element;
166
167 /**
168 * Current state of the operation.
169 */
170 enum UnionOperationPhase phase;
171
172 /**
173 * Did we send the client that we are done?
174 */
175 int client_done_sent;
176
177 /**
178 * Number of ibf buckets already received into the @a remote_ibf.
179 */
180 unsigned int ibf_buckets_received;
181
182 /**
183 * Hashes for elements that we have demanded from the other peer.
184 */
185 struct GNUNET_CONTAINER_MultiHashMap *demanded_hashes;
186
187 /**
188 * Salt that we're using for sending IBFs
189 */
190 uint32_t salt_send;
191
192 /**
193 * Salt for the IBF we've received and that we're currently decoding.
194 */
195 uint32_t salt_receive;
196
197 /**
198 * Number of elements we received from the other peer
199 * that were not in the local set yet.
200 */
201 uint32_t received_fresh;
202
203 /**
204 * Total number of elements received from the other peer.
205 */
206 uint32_t received_total;
207
208 /**
209 * Initial size of our set, just before
210 * the operation started.
211 */
212 uint64_t initial_size;
213};
214
215
216/**
217 * The key entry is used to associate an ibf key with an element.
218 */
219struct KeyEntry
220{
221 /**
222 * IBF key for the entry, derived from the current salt.
223 */
224 struct IBF_Key ibf_key;
225
226 /**
227 * The actual element associated with the key.
228 *
229 * Only owned by the union operation if element->operation
230 * is #GNUNET_YES.
231 */
232 struct ElementEntry *element;
233
234 /**
235 * Did we receive this element?
236 * Even if element->is_foreign is false, we might
237 * have received the element, so this indicates that
238 * the other peer has it.
239 */
240 int received;
241};
242
243
244/**
245 * Used as a closure for sending elements
246 * with a specific IBF key.
247 */
248struct SendElementClosure
249{
250 /**
251 * The IBF key whose matching elements should be
252 * sent.
253 */
254 struct IBF_Key ibf_key;
255
256 /**
257 * Operation for which the elements
258 * should be sent.
259 */
260 struct Operation *op;
261};
262
263
264/**
265 * Extra state required for efficient set union.
266 */
267struct SetState
268{
269 /**
270 * The strata estimator is only generated once for
271 * each set.
272 * The IBF keys are derived from the element hashes with
273 * salt=0.
274 */
275 struct StrataEstimator *se;
276};
277
278
279/**
280 * Iterator over hash map entries, called to
281 * destroy the linked list of colliding ibf key entries.
282 *
283 * @param cls closure
284 * @param key current key code
285 * @param value value in the hash map
286 * @return #GNUNET_YES if we should continue to iterate,
287 * #GNUNET_NO if not.
288 */
289static int
290destroy_key_to_element_iter (void *cls,
291 uint32_t key,
292 void *value)
293{
294 struct KeyEntry *k = value;
295
296 GNUNET_assert (NULL != k);
297 if (GNUNET_YES == k->element->remote)
298 {
299 GNUNET_free (k->element);
300 k->element = NULL;
301 }
302 GNUNET_free (k);
303 return GNUNET_YES;
304}
305
306
307/**
308 * Destroy the union operation. Only things specific to the union
309 * operation are destroyed.
310 *
311 * @param op union operation to destroy
312 */
313static void
314union_op_cancel (struct Operation *op)
315{
316 LOG (GNUNET_ERROR_TYPE_DEBUG,
317 "destroying union op\n");
318 /* check if the op was canceled twice */
319 GNUNET_assert (NULL != op->state);
320 if (NULL != op->state->remote_ibf)
321 {
322 ibf_destroy (op->state->remote_ibf);
323 op->state->remote_ibf = NULL;
324 }
325 if (NULL != op->state->demanded_hashes)
326 {
327 GNUNET_CONTAINER_multihashmap_destroy (op->state->demanded_hashes);
328 op->state->demanded_hashes = NULL;
329 }
330 if (NULL != op->state->local_ibf)
331 {
332 ibf_destroy (op->state->local_ibf);
333 op->state->local_ibf = NULL;
334 }
335 if (NULL != op->state->se)
336 {
337 strata_estimator_destroy (op->state->se);
338 op->state->se = NULL;
339 }
340 if (NULL != op->state->key_to_element)
341 {
342 GNUNET_CONTAINER_multihashmap32_iterate (op->state->key_to_element,
343 &destroy_key_to_element_iter,
344 NULL);
345 GNUNET_CONTAINER_multihashmap32_destroy (op->state->key_to_element);
346 op->state->key_to_element = NULL;
347 }
348 GNUNET_free (op->state);
349 op->state = NULL;
350 LOG (GNUNET_ERROR_TYPE_DEBUG,
351 "destroying union op done\n");
352}
353
354
355/**
356 * Inform the client that the union operation has failed,
357 * and proceed to destroy the evaluate operation.
358 *
359 * @param op the union operation to fail
360 */
361static void
362fail_union_operation (struct Operation *op)
363{
364 struct GNUNET_MQ_Envelope *ev;
365 struct GNUNET_SET_ResultMessage *msg;
366
367 LOG (GNUNET_ERROR_TYPE_WARNING,
368 "union operation failed\n");
369 ev = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_RESULT);
370 msg->result_status = htons (GNUNET_SET_STATUS_FAILURE);
371 msg->request_id = htonl (op->client_request_id);
372 msg->element_type = htons (0);
373 GNUNET_MQ_send (op->set->cs->mq,
374 ev);
375 _GSS_operation_destroy (op, GNUNET_YES);
376}
377
378
379/**
380 * Derive the IBF key from a hash code and
381 * a salt.
382 *
383 * @param src the hash code
384 * @return the derived IBF key
385 */
386static struct IBF_Key
387get_ibf_key (const struct GNUNET_HashCode *src)
388{
389 struct IBF_Key key;
390 uint16_t salt = 0;
391
392 GNUNET_assert (GNUNET_OK ==
393 GNUNET_CRYPTO_kdf (&key, sizeof(key),
394 src, sizeof *src,
395 &salt, sizeof(salt),
396 NULL, 0));
397 return key;
398}
399
400
401/**
402 * Context for #op_get_element_iterator
403 */
404struct GetElementContext
405{
406 /**
407 * FIXME.
408 */
409 struct GNUNET_HashCode hash;
410
411 /**
412 * FIXME.
413 */
414 struct KeyEntry *k;
415};
416
417
418/**
419 * Iterator over the mapping from IBF keys to element entries. Checks if we
420 * have an element with a given GNUNET_HashCode.
421 *
422 * @param cls closure
423 * @param key current key code
424 * @param value value in the hash map
425 * @return #GNUNET_YES if we should search further,
426 * #GNUNET_NO if we've found the element.
427 */
428static int
429op_get_element_iterator (void *cls,
430 uint32_t key,
431 void *value)
432{
433 struct GetElementContext *ctx = cls;
434 struct KeyEntry *k = value;
435
436 GNUNET_assert (NULL != k);
437 if (0 == GNUNET_CRYPTO_hash_cmp (&k->element->element_hash,
438 &ctx->hash))
439 {
440 ctx->k = k;
441 return GNUNET_NO;
442 }
443 return GNUNET_YES;
444}
445
446
447/**
448 * Determine whether the given element is already in the operation's element
449 * set.
450 *
451 * @param op operation that should be tested for 'element_hash'
452 * @param element_hash hash of the element to look for
453 * @return #GNUNET_YES if the element has been found, #GNUNET_NO otherwise
454 */
455static struct KeyEntry *
456op_get_element (struct Operation *op,
457 const struct GNUNET_HashCode *element_hash)
458{
459 int ret;
460 struct IBF_Key ibf_key;
461 struct GetElementContext ctx = { { { 0 } }, 0 };
462
463 ctx.hash = *element_hash;
464
465 ibf_key = get_ibf_key (element_hash);
466 ret = GNUNET_CONTAINER_multihashmap32_get_multiple (op->state->key_to_element,
467 (uint32_t) ibf_key.key_val,
468 op_get_element_iterator,
469 &ctx);
470
471 /* was the iteration aborted because we found the element? */
472 if (GNUNET_SYSERR == ret)
473 {
474 GNUNET_assert (NULL != ctx.k);
475 return ctx.k;
476 }
477 return NULL;
478}
479
480
481/**
482 * Insert an element into the union operation's
483 * key-to-element mapping. Takes ownership of 'ee'.
484 * Note that this does not insert the element in the set,
485 * only in the operation's key-element mapping.
486 * This is done to speed up re-tried operations, if some elements
487 * were transmitted, and then the IBF fails to decode.
488 *
489 * XXX: clarify ownership, doesn't sound right.
490 *
491 * @param op the union operation
492 * @param ee the element entry
493 * @param received was this element received from the remote peer?
494 */
495static void
496op_register_element (struct Operation *op,
497 struct ElementEntry *ee,
498 int received)
499{
500 struct IBF_Key ibf_key;
501 struct KeyEntry *k;
502
503 ibf_key = get_ibf_key (&ee->element_hash);
504 k = GNUNET_new (struct KeyEntry);
505 k->element = ee;
506 k->ibf_key = ibf_key;
507 k->received = received;
508 GNUNET_assert (GNUNET_OK ==
509 GNUNET_CONTAINER_multihashmap32_put (op->state->key_to_element,
510 (uint32_t) ibf_key.key_val,
511 k,
512 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
513}
514
515
516/**
517 * FIXME.
518 */
519static void
520salt_key (const struct IBF_Key *k_in,
521 uint32_t salt,
522 struct IBF_Key *k_out)
523{
524 int s = salt % 64;
525 uint64_t x = k_in->key_val;
526
527 /* rotate ibf key */
528 x = (x >> s) | (x << (64 - s));
529 k_out->key_val = x;
530}
531
532
533/**
534 * FIXME.
535 */
536static void
537unsalt_key (const struct IBF_Key *k_in,
538 uint32_t salt,
539 struct IBF_Key *k_out)
540{
541 int s = salt % 64;
542 uint64_t x = k_in->key_val;
543
544 x = (x << s) | (x >> (64 - s));
545 k_out->key_val = x;
546}
547
548
549/**
550 * Insert a key into an ibf.
551 *
552 * @param cls the ibf
553 * @param key unused
554 * @param value the key entry to get the key from
555 */
556static int
557prepare_ibf_iterator (void *cls,
558 uint32_t key,
559 void *value)
560{
561 struct Operation *op = cls;
562 struct KeyEntry *ke = value;
563 struct IBF_Key salted_key;
564
565 LOG (GNUNET_ERROR_TYPE_DEBUG,
566 "[OP %p] inserting %lx (hash %s) into ibf\n",
567 op,
568 (unsigned long) ke->ibf_key.key_val,
569 GNUNET_h2s (&ke->element->element_hash));
570 salt_key (&ke->ibf_key,
571 op->state->salt_send,
572 &salted_key);
573 ibf_insert (op->state->local_ibf, salted_key);
574 return GNUNET_YES;
575}
576
577
578/**
579 * Iterator for initializing the
580 * key-to-element mapping of a union operation
581 *
582 * @param cls the union operation `struct Operation *`
583 * @param key unused
584 * @param value the `struct ElementEntry *` to insert
585 * into the key-to-element mapping
586 * @return #GNUNET_YES (to continue iterating)
587 */
588static int
589init_key_to_element_iterator (void *cls,
590 const struct GNUNET_HashCode *key,
591 void *value)
592{
593 struct Operation *op = cls;
594 struct ElementEntry *ee = value;
595
596 /* make sure that the element belongs to the set at the time
597 * of creating the operation */
598 if (GNUNET_NO ==
599 _GSS_is_element_of_operation (ee,
600 op))
601 return GNUNET_YES;
602 GNUNET_assert (GNUNET_NO == ee->remote);
603 op_register_element (op,
604 ee,
605 GNUNET_NO);
606 return GNUNET_YES;
607}
608
609
610/**
611 * Initialize the IBF key to element mapping local to this set
612 * operation.
613 *
614 * @param op the set union operation
615 */
616static void
617initialize_key_to_element (struct Operation *op)
618{
619 unsigned int len;
620
621 GNUNET_assert (NULL == op->state->key_to_element);
622 len = GNUNET_CONTAINER_multihashmap_size (op->set->content->elements);
623 op->state->key_to_element = GNUNET_CONTAINER_multihashmap32_create (len + 1);
624 GNUNET_CONTAINER_multihashmap_iterate (op->set->content->elements,
625 &init_key_to_element_iterator,
626 op);
627}
628
629
630/**
631 * Create an ibf with the operation's elements
632 * of the specified size
633 *
634 * @param op the union operation
635 * @param size size of the ibf to create
636 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
637 */
638static int
639prepare_ibf (struct Operation *op,
640 uint32_t size)
641{
642 GNUNET_assert (NULL != op->state->key_to_element);
643
644 if (NULL != op->state->local_ibf)
645 ibf_destroy (op->state->local_ibf);
646 op->state->local_ibf = ibf_create (size, SE_IBF_HASH_NUM);
647 if (NULL == op->state->local_ibf)
648 {
649 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
650 "Failed to allocate local IBF\n");
651 return GNUNET_SYSERR;
652 }
653 GNUNET_CONTAINER_multihashmap32_iterate (op->state->key_to_element,
654 &prepare_ibf_iterator,
655 op);
656 return GNUNET_OK;
657}
658
659
660/**
661 * Send an ibf of appropriate size.
662 *
663 * Fragments the IBF into multiple messages if necessary.
664 *
665 * @param op the union operation
666 * @param ibf_order order of the ibf to send, size=2^order
667 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
668 */
669static int
670send_ibf (struct Operation *op,
671 uint16_t ibf_order)
672{
673 unsigned int buckets_sent = 0;
674 struct InvertibleBloomFilter *ibf;
675
676 if (GNUNET_OK !=
677 prepare_ibf (op, 1 << ibf_order))
678 {
679 /* allocation failed */
680 return GNUNET_SYSERR;
681 }
682
683 LOG (GNUNET_ERROR_TYPE_DEBUG,
684 "sending ibf of size %u\n",
685 1 << ibf_order);
686
687 {
688 char name[64] = { 0 };
689 snprintf (name, sizeof(name), "# sent IBF (order %u)", ibf_order);
690 GNUNET_STATISTICS_update (_GSS_statistics, name, 1, GNUNET_NO);
691 }
692
693 ibf = op->state->local_ibf;
694
695 while (buckets_sent < (1 << ibf_order))
696 {
697 unsigned int buckets_in_message;
698 struct GNUNET_MQ_Envelope *ev;
699 struct IBFMessage *msg;
700
701 buckets_in_message = (1 << ibf_order) - buckets_sent;
702 /* limit to maximum */
703 if (buckets_in_message > MAX_BUCKETS_PER_MESSAGE)
704 buckets_in_message = MAX_BUCKETS_PER_MESSAGE;
705
706 ev = GNUNET_MQ_msg_extra (msg,
707 buckets_in_message * IBF_BUCKET_SIZE,
708 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_IBF);
709 msg->reserved1 = 0;
710 msg->reserved2 = 0;
711 msg->order = ibf_order;
712 msg->offset = htonl (buckets_sent);
713 msg->salt = htonl (op->state->salt_send);
714 ibf_write_slice (ibf, buckets_sent,
715 buckets_in_message, &msg[1]);
716 buckets_sent += buckets_in_message;
717 LOG (GNUNET_ERROR_TYPE_DEBUG,
718 "ibf chunk size %u, %u/%u sent\n",
719 buckets_in_message,
720 buckets_sent,
721 1 << ibf_order);
722 GNUNET_MQ_send (op->mq, ev);
723 }
724
725 /* The other peer must decode the IBF, so
726 * we're passive. */
727 op->state->phase = PHASE_INVENTORY_PASSIVE;
728 return GNUNET_OK;
729}
730
731
732/**
733 * Compute the necessary order of an ibf
734 * from the size of the symmetric set difference.
735 *
736 * @param diff the difference
737 * @return the required size of the ibf
738 */
739static unsigned int
740get_order_from_difference (unsigned int diff)
741{
742 unsigned int ibf_order;
743
744 ibf_order = 2;
745 while (((1 << ibf_order) < (IBF_ALPHA * diff) ||
746 ((1 << ibf_order) < SE_IBF_HASH_NUM)) &&
747 (ibf_order < MAX_IBF_ORDER))
748 ibf_order++;
749 // add one for correction
750 return ibf_order + 1;
751}
752
753
754/**
755 * Send a set element.
756 *
757 * @param cls the union operation `struct Operation *`
758 * @param key unused
759 * @param value the `struct ElementEntry *` to insert
760 * into the key-to-element mapping
761 * @return #GNUNET_YES (to continue iterating)
762 */
763static int
764send_full_element_iterator (void *cls,
765 const struct GNUNET_HashCode *key,
766 void *value)
767{
768 struct Operation *op = cls;
769 struct GNUNET_SET_ElementMessage *emsg;
770 struct ElementEntry *ee = value;
771 struct GNUNET_SET_Element *el = &ee->element;
772 struct GNUNET_MQ_Envelope *ev;
773
774 LOG (GNUNET_ERROR_TYPE_DEBUG,
775 "Sending element %s\n",
776 GNUNET_h2s (key));
777 ev = GNUNET_MQ_msg_extra (emsg,
778 el->size,
779 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_ELEMENT);
780 emsg->element_type = htons (el->element_type);
781 GNUNET_memcpy (&emsg[1],
782 el->data,
783 el->size);
784 GNUNET_MQ_send (op->mq,
785 ev);
786 return GNUNET_YES;
787}
788
789
790/**
791 * Switch to full set transmission for @a op.
792 *
793 * @param op operation to switch to full set transmission.
794 */
795static void
796send_full_set (struct Operation *op)
797{
798 struct GNUNET_MQ_Envelope *ev;
799
800 op->state->phase = PHASE_FULL_SENDING;
801 LOG (GNUNET_ERROR_TYPE_DEBUG,
802 "Dedicing to transmit the full set\n");
803 /* FIXME: use a more memory-friendly way of doing this with an
804 iterator, just as we do in the non-full case! */
805 (void) GNUNET_CONTAINER_multihashmap_iterate (op->set->content->elements,
806 &send_full_element_iterator,
807 op);
808 ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_DONE);
809 GNUNET_MQ_send (op->mq,
810 ev);
811}
812
813
814/**
815 * Handle a strata estimator from a remote peer
816 *
817 * @param cls the union operation
818 * @param msg the message
819 */
820int
821check_union_p2p_strata_estimator (void *cls,
822 const struct StrataEstimatorMessage *msg)
823{
824 struct Operation *op = cls;
825 int is_compressed;
826 size_t len;
827
828 if (op->state->phase != PHASE_EXPECT_SE)
829 {
830 GNUNET_break (0);
831 return GNUNET_SYSERR;
832 }
833 is_compressed = (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SEC == htons (
834 msg->header.type));
835 len = ntohs (msg->header.size) - sizeof(struct StrataEstimatorMessage);
836 if ((GNUNET_NO == is_compressed) &&
837 (len != SE_STRATA_COUNT * SE_IBF_SIZE * IBF_BUCKET_SIZE))
838 {
839 GNUNET_break (0);
840 return GNUNET_SYSERR;
841 }
842 return GNUNET_OK;
843}
844
845
846/**
847 * Handle a strata estimator from a remote peer
848 *
849 * @param cls the union operation
850 * @param msg the message
851 */
852void
853handle_union_p2p_strata_estimator (void *cls,
854 const struct StrataEstimatorMessage *msg)
855{
856 struct Operation *op = cls;
857 struct StrataEstimator *remote_se;
858 unsigned int diff;
859 uint64_t other_size;
860 size_t len;
861 int is_compressed;
862
863 is_compressed = (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SEC == htons (
864 msg->header.type));
865 GNUNET_STATISTICS_update (_GSS_statistics,
866 "# bytes of SE received",
867 ntohs (msg->header.size),
868 GNUNET_NO);
869 len = ntohs (msg->header.size) - sizeof(struct StrataEstimatorMessage);
870 other_size = GNUNET_ntohll (msg->set_size);
871 remote_se = strata_estimator_create (SE_STRATA_COUNT,
872 SE_IBF_SIZE,
873 SE_IBF_HASH_NUM);
874 if (NULL == remote_se)
875 {
876 /* insufficient resources, fail */
877 fail_union_operation (op);
878 return;
879 }
880 if (GNUNET_OK !=
881 strata_estimator_read (&msg[1],
882 len,
883 is_compressed,
884 remote_se))
885 {
886 /* decompression failed */
887 strata_estimator_destroy (remote_se);
888 fail_union_operation (op);
889 return;
890 }
891 GNUNET_assert (NULL != op->state->se);
892 diff = strata_estimator_difference (remote_se,
893 op->state->se);
894
895 if (diff > 200)
896 diff = diff * 3 / 2;
897
898 strata_estimator_destroy (remote_se);
899 strata_estimator_destroy (op->state->se);
900 op->state->se = NULL;
901 LOG (GNUNET_ERROR_TYPE_DEBUG,
902 "got se diff=%d, using ibf size %d\n",
903 diff,
904 1U << get_order_from_difference (diff));
905
906 {
907 char *set_debug;
908
909 set_debug = getenv ("GNUNET_SET_BENCHMARK");
910 if ((NULL != set_debug) &&
911 (0 == strcmp (set_debug, "1")))
912 {
913 FILE *f = fopen ("set.log", "a");
914 fprintf (f, "%llu\n", (unsigned long long) diff);
915 fclose (f);
916 }
917 }
918
919 if ((GNUNET_YES == op->byzantine) &&
920 (other_size < op->byzantine_lower_bound))
921 {
922 GNUNET_break (0);
923 fail_union_operation (op);
924 return;
925 }
926
927 if ((GNUNET_YES == op->force_full) ||
928 (diff > op->state->initial_size / 4) ||
929 (0 == other_size))
930 {
931 LOG (GNUNET_ERROR_TYPE_DEBUG,
932 "Deciding to go for full set transmission (diff=%d, own set=%llu)\n",
933 diff,
934 (unsigned long long) op->state->initial_size);
935 GNUNET_STATISTICS_update (_GSS_statistics,
936 "# of full sends",
937 1,
938 GNUNET_NO);
939 if ((op->state->initial_size <= other_size) ||
940 (0 == other_size))
941 {
942 send_full_set (op);
943 }
944 else
945 {
946 struct GNUNET_MQ_Envelope *ev;
947
948 LOG (GNUNET_ERROR_TYPE_DEBUG,
949 "Telling other peer that we expect its full set\n");
950 op->state->phase = PHASE_EXPECT_IBF;
951 ev = GNUNET_MQ_msg_header (
952 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_REQUEST_FULL);
953 GNUNET_MQ_send (op->mq,
954 ev);
955 }
956 }
957 else
958 {
959 GNUNET_STATISTICS_update (_GSS_statistics,
960 "# of ibf sends",
961 1,
962 GNUNET_NO);
963 if (GNUNET_OK !=
964 send_ibf (op,
965 get_order_from_difference (diff)))
966 {
967 /* Internal error, best we can do is shut the connection */
968 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
969 "Failed to send IBF, closing connection\n");
970 fail_union_operation (op);
971 return;
972 }
973 }
974 GNUNET_CADET_receive_done (op->channel);
975}
976
977
978/**
979 * Iterator to send elements to a remote peer
980 *
981 * @param cls closure with the element key and the union operation
982 * @param key ignored
983 * @param value the key entry
984 */
985static int
986send_offers_iterator (void *cls,
987 uint32_t key,
988 void *value)
989{
990 struct SendElementClosure *sec = cls;
991 struct Operation *op = sec->op;
992 struct KeyEntry *ke = value;
993 struct GNUNET_MQ_Envelope *ev;
994 struct GNUNET_MessageHeader *mh;
995
996 /* Detect 32-bit key collision for the 64-bit IBF keys. */
997 if (ke->ibf_key.key_val != sec->ibf_key.key_val)
998 return GNUNET_YES;
999
1000 ev = GNUNET_MQ_msg_header_extra (mh,
1001 sizeof(struct GNUNET_HashCode),
1002 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_OFFER);
1003
1004 GNUNET_assert (NULL != ev);
1005 *(struct GNUNET_HashCode *) &mh[1] = ke->element->element_hash;
1006 LOG (GNUNET_ERROR_TYPE_DEBUG,
1007 "[OP %p] sending element offer (%s) to peer\n",
1008 op,
1009 GNUNET_h2s (&ke->element->element_hash));
1010 GNUNET_MQ_send (op->mq, ev);
1011 return GNUNET_YES;
1012}
1013
1014
1015/**
1016 * Send offers (in the form of GNUNET_Hash-es) to the remote peer for the given IBF key.
1017 *
1018 * @param op union operation
1019 * @param ibf_key IBF key of interest
1020 */
1021static void
1022send_offers_for_key (struct Operation *op,
1023 struct IBF_Key ibf_key)
1024{
1025 struct SendElementClosure send_cls;
1026
1027 send_cls.ibf_key = ibf_key;
1028 send_cls.op = op;
1029 (void) GNUNET_CONTAINER_multihashmap32_get_multiple (
1030 op->state->key_to_element,
1031 (uint32_t) ibf_key.
1032 key_val,
1033 &send_offers_iterator,
1034 &send_cls);
1035}
1036
1037
1038/**
1039 * Decode which elements are missing on each side, and
1040 * send the appropriate offers and inquiries.
1041 *
1042 * @param op union operation
1043 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
1044 */
1045static int
1046decode_and_send (struct Operation *op)
1047{
1048 struct IBF_Key key;
1049 struct IBF_Key last_key;
1050 int side;
1051 unsigned int num_decoded;
1052 struct InvertibleBloomFilter *diff_ibf;
1053
1054 GNUNET_assert (PHASE_INVENTORY_ACTIVE == op->state->phase);
1055
1056 if (GNUNET_OK !=
1057 prepare_ibf (op,
1058 op->state->remote_ibf->size))
1059 {
1060 GNUNET_break (0);
1061 /* allocation failed */
1062 return GNUNET_SYSERR;
1063 }
1064 diff_ibf = ibf_dup (op->state->local_ibf);
1065 ibf_subtract (diff_ibf,
1066 op->state->remote_ibf);
1067
1068 ibf_destroy (op->state->remote_ibf);
1069 op->state->remote_ibf = NULL;
1070
1071 LOG (GNUNET_ERROR_TYPE_DEBUG,
1072 "decoding IBF (size=%u)\n",
1073 diff_ibf->size);
1074
1075 num_decoded = 0;
1076 key.key_val = 0; /* just to avoid compiler thinking we use undef'ed variable */
1077
1078 while (1)
1079 {
1080 int res;
1081 int cycle_detected = GNUNET_NO;
1082
1083 last_key = key;
1084
1085 res = ibf_decode (diff_ibf, &side, &key);
1086 if (res == GNUNET_OK)
1087 {
1088 LOG (GNUNET_ERROR_TYPE_DEBUG,
1089 "decoded ibf key %lx\n",
1090 (unsigned long) key.key_val);
1091 num_decoded += 1;
1092 if ((num_decoded > diff_ibf->size) ||
1093 ((num_decoded > 1) &&
1094 (last_key.key_val == key.key_val)))
1095 {
1096 LOG (GNUNET_ERROR_TYPE_DEBUG,
1097 "detected cyclic ibf (decoded %u/%u)\n",
1098 num_decoded,
1099 diff_ibf->size);
1100 cycle_detected = GNUNET_YES;
1101 }
1102 }
1103 if ((GNUNET_SYSERR == res) ||
1104 (GNUNET_YES == cycle_detected))
1105 {
1106 int next_order;
1107 next_order = 0;
1108 while (1 << next_order < diff_ibf->size)
1109 next_order++;
1110 next_order++;
1111 if (next_order <= MAX_IBF_ORDER)
1112 {
1113 LOG (GNUNET_ERROR_TYPE_DEBUG,
1114 "decoding failed, sending larger ibf (size %u)\n",
1115 1 << next_order);
1116 GNUNET_STATISTICS_update (_GSS_statistics,
1117 "# of IBF retries",
1118 1,
1119 GNUNET_NO);
1120 op->state->salt_send++;
1121 if (GNUNET_OK !=
1122 send_ibf (op, next_order))
1123 {
1124 /* Internal error, best we can do is shut the connection */
1125 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1126 "Failed to send IBF, closing connection\n");
1127 fail_union_operation (op);
1128 ibf_destroy (diff_ibf);
1129 return GNUNET_SYSERR;
1130 }
1131 }
1132 else
1133 {
1134 GNUNET_STATISTICS_update (_GSS_statistics,
1135 "# of failed union operations (too large)",
1136 1,
1137 GNUNET_NO);
1138 // XXX: Send the whole set, element-by-element
1139 LOG (GNUNET_ERROR_TYPE_ERROR,
1140 "set union failed: reached ibf limit\n");
1141 fail_union_operation (op);
1142 ibf_destroy (diff_ibf);
1143 return GNUNET_SYSERR;
1144 }
1145 break;
1146 }
1147 if (GNUNET_NO == res)
1148 {
1149 struct GNUNET_MQ_Envelope *ev;
1150
1151 LOG (GNUNET_ERROR_TYPE_DEBUG,
1152 "transmitted all values, sending DONE\n");
1153 ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE);
1154 GNUNET_MQ_send (op->mq, ev);
1155 /* We now wait until we get a DONE message back
1156 * and then wait for our MQ to be flushed and all our
1157 * demands be delivered. */
1158 break;
1159 }
1160 if (1 == side)
1161 {
1162 struct IBF_Key unsalted_key;
1163
1164 unsalt_key (&key,
1165 op->state->salt_receive,
1166 &unsalted_key);
1167 send_offers_for_key (op,
1168 unsalted_key);
1169 }
1170 else if (-1 == side)
1171 {
1172 struct GNUNET_MQ_Envelope *ev;
1173 struct InquiryMessage *msg;
1174
1175 /* It may be nice to merge multiple requests, but with CADET's corking it is not worth
1176 * the effort additional complexity. */
1177 ev = GNUNET_MQ_msg_extra (msg,
1178 sizeof(struct IBF_Key),
1179 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_INQUIRY);
1180 msg->salt = htonl (op->state->salt_receive);
1181 GNUNET_memcpy (&msg[1],
1182 &key,
1183 sizeof(struct IBF_Key));
1184 LOG (GNUNET_ERROR_TYPE_DEBUG,
1185 "sending element inquiry for IBF key %lx\n",
1186 (unsigned long) key.key_val);
1187 GNUNET_MQ_send (op->mq, ev);
1188 }
1189 else
1190 {
1191 GNUNET_assert (0);
1192 }
1193 }
1194 ibf_destroy (diff_ibf);
1195 return GNUNET_OK;
1196}
1197
1198
1199/**
1200 * Check an IBF message from a remote peer.
1201 *
1202 * Reassemble the IBF from multiple pieces, and
1203 * process the whole IBF once possible.
1204 *
1205 * @param cls the union operation
1206 * @param msg the header of the message
1207 * @return #GNUNET_OK if @a msg is well-formed
1208 */
1209int
1210check_union_p2p_ibf (void *cls,
1211 const struct IBFMessage *msg)
1212{
1213 struct Operation *op = cls;
1214 unsigned int buckets_in_message;
1215
1216 if (GNUNET_SET_OPERATION_UNION != op->set->operation)
1217 {
1218 GNUNET_break_op (0);
1219 return GNUNET_SYSERR;
1220 }
1221 buckets_in_message = (ntohs (msg->header.size) - sizeof *msg)
1222 / IBF_BUCKET_SIZE;
1223 if (0 == buckets_in_message)
1224 {
1225 GNUNET_break_op (0);
1226 return GNUNET_SYSERR;
1227 }
1228 if ((ntohs (msg->header.size) - sizeof *msg) != buckets_in_message
1229 * IBF_BUCKET_SIZE)
1230 {
1231 GNUNET_break_op (0);
1232 return GNUNET_SYSERR;
1233 }
1234 if (op->state->phase == PHASE_EXPECT_IBF_CONT)
1235 {
1236 if (ntohl (msg->offset) != op->state->ibf_buckets_received)
1237 {
1238 GNUNET_break_op (0);
1239 return GNUNET_SYSERR;
1240 }
1241 if (1 << msg->order != op->state->remote_ibf->size)
1242 {
1243 GNUNET_break_op (0);
1244 return GNUNET_SYSERR;
1245 }
1246 if (ntohl (msg->salt) != op->state->salt_receive)
1247 {
1248 GNUNET_break_op (0);
1249 return GNUNET_SYSERR;
1250 }
1251 }
1252 else if ((op->state->phase != PHASE_INVENTORY_PASSIVE) &&
1253 (op->state->phase != PHASE_EXPECT_IBF))
1254 {
1255 GNUNET_break_op (0);
1256 return GNUNET_SYSERR;
1257 }
1258
1259 return GNUNET_OK;
1260}
1261
1262
1263/**
1264 * Handle an IBF message from a remote peer.
1265 *
1266 * Reassemble the IBF from multiple pieces, and
1267 * process the whole IBF once possible.
1268 *
1269 * @param cls the union operation
1270 * @param msg the header of the message
1271 */
1272void
1273handle_union_p2p_ibf (void *cls,
1274 const struct IBFMessage *msg)
1275{
1276 struct Operation *op = cls;
1277 unsigned int buckets_in_message;
1278
1279 buckets_in_message = (ntohs (msg->header.size) - sizeof *msg)
1280 / IBF_BUCKET_SIZE;
1281 if ((op->state->phase == PHASE_INVENTORY_PASSIVE) ||
1282 (op->state->phase == PHASE_EXPECT_IBF))
1283 {
1284 op->state->phase = PHASE_EXPECT_IBF_CONT;
1285 GNUNET_assert (NULL == op->state->remote_ibf);
1286 LOG (GNUNET_ERROR_TYPE_DEBUG,
1287 "Creating new ibf of size %u\n",
1288 1 << msg->order);
1289 op->state->remote_ibf = ibf_create (1 << msg->order, SE_IBF_HASH_NUM);
1290 op->state->salt_receive = ntohl (msg->salt);
1291 LOG (GNUNET_ERROR_TYPE_DEBUG,
1292 "Receiving new IBF with salt %u\n",
1293 op->state->salt_receive);
1294 if (NULL == op->state->remote_ibf)
1295 {
1296 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1297 "Failed to parse remote IBF, closing connection\n");
1298 fail_union_operation (op);
1299 return;
1300 }
1301 op->state->ibf_buckets_received = 0;
1302 if (0 != ntohl (msg->offset))
1303 {
1304 GNUNET_break_op (0);
1305 fail_union_operation (op);
1306 return;
1307 }
1308 }
1309 else
1310 {
1311 GNUNET_assert (op->state->phase == PHASE_EXPECT_IBF_CONT);
1312 LOG (GNUNET_ERROR_TYPE_DEBUG,
1313 "Received more of IBF\n");
1314 }
1315 GNUNET_assert (NULL != op->state->remote_ibf);
1316
1317 ibf_read_slice (&msg[1],
1318 op->state->ibf_buckets_received,
1319 buckets_in_message,
1320 op->state->remote_ibf);
1321 op->state->ibf_buckets_received += buckets_in_message;
1322
1323 if (op->state->ibf_buckets_received == op->state->remote_ibf->size)
1324 {
1325 LOG (GNUNET_ERROR_TYPE_DEBUG,
1326 "received full ibf\n");
1327 op->state->phase = PHASE_INVENTORY_ACTIVE;
1328 if (GNUNET_OK !=
1329 decode_and_send (op))
1330 {
1331 /* Internal error, best we can do is shut down */
1332 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1333 "Failed to decode IBF, closing connection\n");
1334 fail_union_operation (op);
1335 return;
1336 }
1337 }
1338 GNUNET_CADET_receive_done (op->channel);
1339}
1340
1341
1342/**
1343 * Send a result message to the client indicating
1344 * that there is a new element.
1345 *
1346 * @param op union operation
1347 * @param element element to send
1348 * @param status status to send with the new element
1349 */
1350static void
1351send_client_element (struct Operation *op,
1352 struct GNUNET_SET_Element *element,
1353 int status)
1354{
1355 struct GNUNET_MQ_Envelope *ev;
1356 struct GNUNET_SET_ResultMessage *rm;
1357
1358 LOG (GNUNET_ERROR_TYPE_DEBUG,
1359 "sending element (size %u) to client\n",
1360 element->size);
1361 GNUNET_assert (0 != op->client_request_id);
1362 ev = GNUNET_MQ_msg_extra (rm, element->size, GNUNET_MESSAGE_TYPE_SET_RESULT);
1363 if (NULL == ev)
1364 {
1365 GNUNET_MQ_discard (ev);
1366 GNUNET_break (0);
1367 return;
1368 }
1369 rm->result_status = htons (status);
1370 rm->request_id = htonl (op->client_request_id);
1371 rm->element_type = htons (element->element_type);
1372 rm->current_size = GNUNET_htonll (GNUNET_CONTAINER_multihashmap32_size (
1373 op->state->key_to_element));
1374 GNUNET_memcpy (&rm[1],
1375 element->data,
1376 element->size);
1377 GNUNET_MQ_send (op->set->cs->mq,
1378 ev);
1379}
1380
1381
1382/**
1383 * Signal to the client that the operation has finished and
1384 * destroy the operation.
1385 *
1386 * @param cls operation to destroy
1387 */
1388static void
1389send_client_done (void *cls)
1390{
1391 struct Operation *op = cls;
1392 struct GNUNET_MQ_Envelope *ev;
1393 struct GNUNET_SET_ResultMessage *rm;
1394
1395 if (GNUNET_YES == op->state->client_done_sent)
1396 {
1397 return;
1398 }
1399
1400 if (PHASE_DONE != op->state->phase)
1401 {
1402 LOG (GNUNET_ERROR_TYPE_WARNING,
1403 "Union operation failed\n");
1404 GNUNET_STATISTICS_update (_GSS_statistics,
1405 "# Union operations failed",
1406 1,
1407 GNUNET_NO);
1408 ev = GNUNET_MQ_msg (rm, GNUNET_MESSAGE_TYPE_SET_RESULT);
1409 rm->result_status = htons (GNUNET_SET_STATUS_FAILURE);
1410 rm->request_id = htonl (op->client_request_id);
1411 rm->element_type = htons (0);
1412 GNUNET_MQ_send (op->set->cs->mq,
1413 ev);
1414 return;
1415 }
1416
1417 op->state->client_done_sent = GNUNET_YES;
1418
1419 GNUNET_STATISTICS_update (_GSS_statistics,
1420 "# Union operations succeeded",
1421 1,
1422 GNUNET_NO);
1423 LOG (GNUNET_ERROR_TYPE_INFO,
1424 "Signalling client that union operation is done\n");
1425 ev = GNUNET_MQ_msg (rm,
1426 GNUNET_MESSAGE_TYPE_SET_RESULT);
1427 rm->request_id = htonl (op->client_request_id);
1428 rm->result_status = htons (GNUNET_SET_STATUS_DONE);
1429 rm->element_type = htons (0);
1430 rm->current_size = GNUNET_htonll (GNUNET_CONTAINER_multihashmap32_size (
1431 op->state->key_to_element));
1432 GNUNET_MQ_send (op->set->cs->mq,
1433 ev);
1434}
1435
1436
1437/**
1438 * Tests if the operation is finished, and if so notify.
1439 *
1440 * @param op operation to check
1441 */
1442static void
1443maybe_finish (struct Operation *op)
1444{
1445 unsigned int num_demanded;
1446
1447 num_demanded = GNUNET_CONTAINER_multihashmap_size (
1448 op->state->demanded_hashes);
1449
1450 if (PHASE_FINISH_WAITING == op->state->phase)
1451 {
1452 LOG (GNUNET_ERROR_TYPE_DEBUG,
1453 "In PHASE_FINISH_WAITING, pending %u demands\n",
1454 num_demanded);
1455 if (0 == num_demanded)
1456 {
1457 struct GNUNET_MQ_Envelope *ev;
1458
1459 op->state->phase = PHASE_DONE;
1460 ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE);
1461 GNUNET_MQ_send (op->mq,
1462 ev);
1463 /* We now wait until the other peer sends P2P_OVER
1464 * after it got all elements from us. */
1465 }
1466 }
1467 if (PHASE_FINISH_CLOSING == op->state->phase)
1468 {
1469 LOG (GNUNET_ERROR_TYPE_DEBUG,
1470 "In PHASE_FINISH_CLOSING, pending %u demands\n",
1471 num_demanded);
1472 if (0 == num_demanded)
1473 {
1474 op->state->phase = PHASE_DONE;
1475 send_client_done (op);
1476 _GSS_operation_destroy2 (op);
1477 }
1478 }
1479}
1480
1481
1482/**
1483 * Check an element message from a remote peer.
1484 *
1485 * @param cls the union operation
1486 * @param emsg the message
1487 */
1488int
1489check_union_p2p_elements (void *cls,
1490 const struct GNUNET_SET_ElementMessage *emsg)
1491{
1492 struct Operation *op = cls;
1493
1494 if (GNUNET_SET_OPERATION_UNION != op->set->operation)
1495 {
1496 GNUNET_break_op (0);
1497 return GNUNET_SYSERR;
1498 }
1499 if (0 == GNUNET_CONTAINER_multihashmap_size (op->state->demanded_hashes))
1500 {
1501 GNUNET_break_op (0);
1502 return GNUNET_SYSERR;
1503 }
1504 return GNUNET_OK;
1505}
1506
1507
1508/**
1509 * Handle an element message from a remote peer.
1510 * Sent by the other peer either because we decoded an IBF and placed a demand,
1511 * or because the other peer switched to full set transmission.
1512 *
1513 * @param cls the union operation
1514 * @param emsg the message
1515 */
1516void
1517handle_union_p2p_elements (void *cls,
1518 const struct GNUNET_SET_ElementMessage *emsg)
1519{
1520 struct Operation *op = cls;
1521 struct ElementEntry *ee;
1522 struct KeyEntry *ke;
1523 uint16_t element_size;
1524
1525 element_size = ntohs (emsg->header.size) - sizeof(struct
1526 GNUNET_SET_ElementMessage);
1527 ee = GNUNET_malloc (sizeof(struct ElementEntry) + element_size);
1528 GNUNET_memcpy (&ee[1],
1529 &emsg[1],
1530 element_size);
1531 ee->element.size = element_size;
1532 ee->element.data = &ee[1];
1533 ee->element.element_type = ntohs (emsg->element_type);
1534 ee->remote = GNUNET_YES;
1535 GNUNET_SET_element_hash (&ee->element,
1536 &ee->element_hash);
1537 if (GNUNET_NO ==
1538 GNUNET_CONTAINER_multihashmap_remove (op->state->demanded_hashes,
1539 &ee->element_hash,
1540 NULL))
1541 {
1542 /* We got something we didn't demand, since it's not in our map. */
1543 GNUNET_break_op (0);
1544 fail_union_operation (op);
1545 return;
1546 }
1547
1548 LOG (GNUNET_ERROR_TYPE_DEBUG,
1549 "Got element (size %u, hash %s) from peer\n",
1550 (unsigned int) element_size,
1551 GNUNET_h2s (&ee->element_hash));
1552
1553 GNUNET_STATISTICS_update (_GSS_statistics,
1554 "# received elements",
1555 1,
1556 GNUNET_NO);
1557 GNUNET_STATISTICS_update (_GSS_statistics,
1558 "# exchanged elements",
1559 1,
1560 GNUNET_NO);
1561
1562 op->state->received_total++;
1563
1564 ke = op_get_element (op, &ee->element_hash);
1565 if (NULL != ke)
1566 {
1567 /* Got repeated element. Should not happen since
1568 * we track demands. */
1569 GNUNET_STATISTICS_update (_GSS_statistics,
1570 "# repeated elements",
1571 1,
1572 GNUNET_NO);
1573 ke->received = GNUNET_YES;
1574 GNUNET_free (ee);
1575 }
1576 else
1577 {
1578 LOG (GNUNET_ERROR_TYPE_DEBUG,
1579 "Registering new element from remote peer\n");
1580 op->state->received_fresh++;
1581 op_register_element (op, ee, GNUNET_YES);
1582 /* only send results immediately if the client wants it */
1583 switch (op->result_mode)
1584 {
1585 case GNUNET_SET_RESULT_ADDED:
1586 send_client_element (op, &ee->element, GNUNET_SET_STATUS_OK);
1587 break;
1588
1589 case GNUNET_SET_RESULT_SYMMETRIC:
1590 send_client_element (op, &ee->element, GNUNET_SET_STATUS_ADD_LOCAL);
1591 break;
1592
1593 default:
1594 /* Result mode not supported, should have been caught earlier. */
1595 GNUNET_break (0);
1596 break;
1597 }
1598 }
1599
1600 if ((op->state->received_total > 8) &&
1601 (op->state->received_fresh < op->state->received_total / 3))
1602 {
1603 /* The other peer gave us lots of old elements, there's something wrong. */
1604 GNUNET_break_op (0);
1605 fail_union_operation (op);
1606 return;
1607 }
1608 GNUNET_CADET_receive_done (op->channel);
1609 maybe_finish (op);
1610}
1611
1612
1613/**
1614 * Check a full element message from a remote peer.
1615 *
1616 * @param cls the union operation
1617 * @param emsg the message
1618 */
1619int
1620check_union_p2p_full_element (void *cls,
1621 const struct GNUNET_SET_ElementMessage *emsg)
1622{
1623 struct Operation *op = cls;
1624
1625 if (GNUNET_SET_OPERATION_UNION != op->set->operation)
1626 {
1627 GNUNET_break_op (0);
1628 return GNUNET_SYSERR;
1629 }
1630 // FIXME: check that we expect full elements here?
1631 return GNUNET_OK;
1632}
1633
1634
1635/**
1636 * Handle an element message from a remote peer.
1637 *
1638 * @param cls the union operation
1639 * @param emsg the message
1640 */
1641void
1642handle_union_p2p_full_element (void *cls,
1643 const struct GNUNET_SET_ElementMessage *emsg)
1644{
1645 struct Operation *op = cls;
1646 struct ElementEntry *ee;
1647 struct KeyEntry *ke;
1648 uint16_t element_size;
1649
1650 element_size = ntohs (emsg->header.size) - sizeof(struct
1651 GNUNET_SET_ElementMessage);
1652 ee = GNUNET_malloc (sizeof(struct ElementEntry) + element_size);
1653 GNUNET_memcpy (&ee[1], &emsg[1], element_size);
1654 ee->element.size = element_size;
1655 ee->element.data = &ee[1];
1656 ee->element.element_type = ntohs (emsg->element_type);
1657 ee->remote = GNUNET_YES;
1658 GNUNET_SET_element_hash (&ee->element, &ee->element_hash);
1659
1660 LOG (GNUNET_ERROR_TYPE_DEBUG,
1661 "Got element (full diff, size %u, hash %s) from peer\n",
1662 (unsigned int) element_size,
1663 GNUNET_h2s (&ee->element_hash));
1664
1665 GNUNET_STATISTICS_update (_GSS_statistics,
1666 "# received elements",
1667 1,
1668 GNUNET_NO);
1669 GNUNET_STATISTICS_update (_GSS_statistics,
1670 "# exchanged elements",
1671 1,
1672 GNUNET_NO);
1673
1674 op->state->received_total++;
1675
1676 ke = op_get_element (op, &ee->element_hash);
1677 if (NULL != ke)
1678 {
1679 /* Got repeated element. Should not happen since
1680 * we track demands. */
1681 GNUNET_STATISTICS_update (_GSS_statistics,
1682 "# repeated elements",
1683 1,
1684 GNUNET_NO);
1685 ke->received = GNUNET_YES;
1686 GNUNET_free (ee);
1687 }
1688 else
1689 {
1690 LOG (GNUNET_ERROR_TYPE_DEBUG,
1691 "Registering new element from remote peer\n");
1692 op->state->received_fresh++;
1693 op_register_element (op, ee, GNUNET_YES);
1694 /* only send results immediately if the client wants it */
1695 switch (op->result_mode)
1696 {
1697 case GNUNET_SET_RESULT_ADDED:
1698 send_client_element (op, &ee->element, GNUNET_SET_STATUS_OK);
1699 break;
1700
1701 case GNUNET_SET_RESULT_SYMMETRIC:
1702 send_client_element (op, &ee->element, GNUNET_SET_STATUS_ADD_LOCAL);
1703 break;
1704
1705 default:
1706 /* Result mode not supported, should have been caught earlier. */
1707 GNUNET_break (0);
1708 break;
1709 }
1710 }
1711
1712 if ((GNUNET_YES == op->byzantine) &&
1713 (op->state->received_total > 384 + op->state->received_fresh * 4) &&
1714 (op->state->received_fresh < op->state->received_total / 6))
1715 {
1716 /* The other peer gave us lots of old elements, there's something wrong. */
1717 LOG (GNUNET_ERROR_TYPE_ERROR,
1718 "Other peer sent only %llu/%llu fresh elements, failing operation\n",
1719 (unsigned long long) op->state->received_fresh,
1720 (unsigned long long) op->state->received_total);
1721 GNUNET_break_op (0);
1722 fail_union_operation (op);
1723 return;
1724 }
1725 GNUNET_CADET_receive_done (op->channel);
1726}
1727
1728
1729/**
1730 * Send offers (for GNUNET_Hash-es) in response
1731 * to inquiries (for IBF_Key-s).
1732 *
1733 * @param cls the union operation
1734 * @param msg the message
1735 */
1736int
1737check_union_p2p_inquiry (void *cls,
1738 const struct InquiryMessage *msg)
1739{
1740 struct Operation *op = cls;
1741 unsigned int num_keys;
1742
1743 if (GNUNET_SET_OPERATION_UNION != op->set->operation)
1744 {
1745 GNUNET_break_op (0);
1746 return GNUNET_SYSERR;
1747 }
1748 if (op->state->phase != PHASE_INVENTORY_PASSIVE)
1749 {
1750 GNUNET_break_op (0);
1751 return GNUNET_SYSERR;
1752 }
1753 num_keys = (ntohs (msg->header.size) - sizeof(struct InquiryMessage))
1754 / sizeof(struct IBF_Key);
1755 if ((ntohs (msg->header.size) - sizeof(struct InquiryMessage))
1756 != num_keys * sizeof(struct IBF_Key))
1757 {
1758 GNUNET_break_op (0);
1759 return GNUNET_SYSERR;
1760 }
1761 return GNUNET_OK;
1762}
1763
1764
1765/**
1766 * Send offers (for GNUNET_Hash-es) in response
1767 * to inquiries (for IBF_Key-s).
1768 *
1769 * @param cls the union operation
1770 * @param msg the message
1771 */
1772void
1773handle_union_p2p_inquiry (void *cls,
1774 const struct InquiryMessage *msg)
1775{
1776 struct Operation *op = cls;
1777 const struct IBF_Key *ibf_key;
1778 unsigned int num_keys;
1779
1780 LOG (GNUNET_ERROR_TYPE_DEBUG,
1781 "Received union inquiry\n");
1782 num_keys = (ntohs (msg->header.size) - sizeof(struct InquiryMessage))
1783 / sizeof(struct IBF_Key);
1784 ibf_key = (const struct IBF_Key *) &msg[1];
1785 while (0 != num_keys--)
1786 {
1787 struct IBF_Key unsalted_key;
1788
1789 unsalt_key (ibf_key,
1790 ntohl (msg->salt),
1791 &unsalted_key);
1792 send_offers_for_key (op,
1793 unsalted_key);
1794 ibf_key++;
1795 }
1796 GNUNET_CADET_receive_done (op->channel);
1797}
1798
1799
1800/**
1801 * Iterator over hash map entries, called to
1802 * destroy the linked list of colliding ibf key entries.
1803 *
1804 * @param cls closure
1805 * @param key current key code
1806 * @param value value in the hash map
1807 * @return #GNUNET_YES if we should continue to iterate,
1808 * #GNUNET_NO if not.
1809 */
1810static int
1811send_missing_full_elements_iter (void *cls,
1812 uint32_t key,
1813 void *value)
1814{
1815 struct Operation *op = cls;
1816 struct KeyEntry *ke = value;
1817 struct GNUNET_MQ_Envelope *ev;
1818 struct GNUNET_SET_ElementMessage *emsg;
1819 struct ElementEntry *ee = ke->element;
1820
1821 if (GNUNET_YES == ke->received)
1822 return GNUNET_YES;
1823 ev = GNUNET_MQ_msg_extra (emsg,
1824 ee->element.size,
1825 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_ELEMENT);
1826 GNUNET_memcpy (&emsg[1],
1827 ee->element.data,
1828 ee->element.size);
1829 emsg->element_type = htons (ee->element.element_type);
1830 GNUNET_MQ_send (op->mq,
1831 ev);
1832 return GNUNET_YES;
1833}
1834
1835
1836/**
1837 * Handle a request for full set transmission.
1838 *
1839 * @param cls closure, a set union operation
1840 * @param mh the demand message
1841 */
1842void
1843handle_union_p2p_request_full (void *cls,
1844 const struct GNUNET_MessageHeader *mh)
1845{
1846 struct Operation *op = cls;
1847
1848 LOG (GNUNET_ERROR_TYPE_DEBUG,
1849 "Received request for full set transmission\n");
1850 if (GNUNET_SET_OPERATION_UNION != op->set->operation)
1851 {
1852 GNUNET_break_op (0);
1853 fail_union_operation (op);
1854 return;
1855 }
1856 if (PHASE_EXPECT_IBF != op->state->phase)
1857 {
1858 GNUNET_break_op (0);
1859 fail_union_operation (op);
1860 return;
1861 }
1862
1863 // FIXME: we need to check that our set is larger than the
1864 // byzantine_lower_bound by some threshold
1865 send_full_set (op);
1866 GNUNET_CADET_receive_done (op->channel);
1867}
1868
1869
1870/**
1871 * Handle a "full done" message.
1872 *
1873 * @param cls closure, a set union operation
1874 * @param mh the demand message
1875 */
1876void
1877handle_union_p2p_full_done (void *cls,
1878 const struct GNUNET_MessageHeader *mh)
1879{
1880 struct Operation *op = cls;
1881
1882 switch (op->state->phase)
1883 {
1884 case PHASE_EXPECT_IBF:
1885 {
1886 struct GNUNET_MQ_Envelope *ev;
1887
1888 LOG (GNUNET_ERROR_TYPE_DEBUG,
1889 "got FULL DONE, sending elements that other peer is missing\n");
1890
1891 /* send all the elements that did not come from the remote peer */
1892 GNUNET_CONTAINER_multihashmap32_iterate (op->state->key_to_element,
1893 &send_missing_full_elements_iter,
1894 op);
1895
1896 ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_DONE);
1897 GNUNET_MQ_send (op->mq,
1898 ev);
1899 op->state->phase = PHASE_DONE;
1900 /* we now wait until the other peer sends us the OVER message*/
1901 }
1902 break;
1903
1904 case PHASE_FULL_SENDING:
1905 {
1906 LOG (GNUNET_ERROR_TYPE_DEBUG,
1907 "got FULL DONE, finishing\n");
1908 /* We sent the full set, and got the response for that. We're done. */
1909 op->state->phase = PHASE_DONE;
1910 GNUNET_CADET_receive_done (op->channel);
1911 send_client_done (op);
1912 _GSS_operation_destroy2 (op);
1913 return;
1914 }
1915 break;
1916
1917 default:
1918 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1919 "Handle full done phase is %u\n",
1920 (unsigned) op->state->phase);
1921 GNUNET_break_op (0);
1922 fail_union_operation (op);
1923 return;
1924 }
1925 GNUNET_CADET_receive_done (op->channel);
1926}
1927
1928
1929/**
1930 * Check a demand by the other peer for elements based on a list
1931 * of `struct GNUNET_HashCode`s.
1932 *
1933 * @param cls closure, a set union operation
1934 * @param mh the demand message
1935 * @return #GNUNET_OK if @a mh is well-formed
1936 */
1937int
1938check_union_p2p_demand (void *cls,
1939 const struct GNUNET_MessageHeader *mh)
1940{
1941 struct Operation *op = cls;
1942 unsigned int num_hashes;
1943
1944 if (GNUNET_SET_OPERATION_UNION != op->set->operation)
1945 {
1946 GNUNET_break_op (0);
1947 return GNUNET_SYSERR;
1948 }
1949 num_hashes = (ntohs (mh->size) - sizeof(struct GNUNET_MessageHeader))
1950 / sizeof(struct GNUNET_HashCode);
1951 if ((ntohs (mh->size) - sizeof(struct GNUNET_MessageHeader))
1952 != num_hashes * sizeof(struct GNUNET_HashCode))
1953 {
1954 GNUNET_break_op (0);
1955 return GNUNET_SYSERR;
1956 }
1957 return GNUNET_OK;
1958}
1959
1960
1961/**
1962 * Handle a demand by the other peer for elements based on a list
1963 * of `struct GNUNET_HashCode`s.
1964 *
1965 * @param cls closure, a set union operation
1966 * @param mh the demand message
1967 */
1968void
1969handle_union_p2p_demand (void *cls,
1970 const struct GNUNET_MessageHeader *mh)
1971{
1972 struct Operation *op = cls;
1973 struct ElementEntry *ee;
1974 struct GNUNET_SET_ElementMessage *emsg;
1975 const struct GNUNET_HashCode *hash;
1976 unsigned int num_hashes;
1977 struct GNUNET_MQ_Envelope *ev;
1978
1979 num_hashes = (ntohs (mh->size) - sizeof(struct GNUNET_MessageHeader))
1980 / sizeof(struct GNUNET_HashCode);
1981 for (hash = (const struct GNUNET_HashCode *) &mh[1];
1982 num_hashes > 0;
1983 hash++, num_hashes--)
1984 {
1985 ee = GNUNET_CONTAINER_multihashmap_get (op->set->content->elements,
1986 hash);
1987 if (NULL == ee)
1988 {
1989 /* Demand for non-existing element. */
1990 GNUNET_break_op (0);
1991 fail_union_operation (op);
1992 return;
1993 }
1994 if (GNUNET_NO == _GSS_is_element_of_operation (ee, op))
1995 {
1996 /* Probably confused lazily copied sets. */
1997 GNUNET_break_op (0);
1998 fail_union_operation (op);
1999 return;
2000 }
2001 ev = GNUNET_MQ_msg_extra (emsg, ee->element.size,
2002 GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENTS);
2003 GNUNET_memcpy (&emsg[1], ee->element.data, ee->element.size);
2004 emsg->reserved = htons (0);
2005 emsg->element_type = htons (ee->element.element_type);
2006 LOG (GNUNET_ERROR_TYPE_DEBUG,
2007 "[OP %p] Sending demanded element (size %u, hash %s) to peer\n",
2008 op,
2009 (unsigned int) ee->element.size,
2010 GNUNET_h2s (&ee->element_hash));
2011 GNUNET_MQ_send (op->mq, ev);
2012 GNUNET_STATISTICS_update (_GSS_statistics,
2013 "# exchanged elements",
2014 1,
2015 GNUNET_NO);
2016
2017 switch (op->result_mode)
2018 {
2019 case GNUNET_SET_RESULT_ADDED:
2020 /* Nothing to do. */
2021 break;
2022
2023 case GNUNET_SET_RESULT_SYMMETRIC:
2024 send_client_element (op, &ee->element, GNUNET_SET_STATUS_ADD_REMOTE);
2025 break;
2026
2027 default:
2028 /* Result mode not supported, should have been caught earlier. */
2029 GNUNET_break (0);
2030 break;
2031 }
2032 }
2033 GNUNET_CADET_receive_done (op->channel);
2034}
2035
2036
2037/**
2038 * Check offer (of `struct GNUNET_HashCode`s).
2039 *
2040 * @param cls the union operation
2041 * @param mh the message
2042 * @return #GNUNET_OK if @a mh is well-formed
2043 */
2044int
2045check_union_p2p_offer (void *cls,
2046 const struct GNUNET_MessageHeader *mh)
2047{
2048 struct Operation *op = cls;
2049 unsigned int num_hashes;
2050
2051 if (GNUNET_SET_OPERATION_UNION != op->set->operation)
2052 {
2053 GNUNET_break_op (0);
2054 return GNUNET_SYSERR;
2055 }
2056 /* look up elements and send them */
2057 if ((op->state->phase != PHASE_INVENTORY_PASSIVE) &&
2058 (op->state->phase != PHASE_INVENTORY_ACTIVE))
2059 {
2060 GNUNET_break_op (0);
2061 return GNUNET_SYSERR;
2062 }
2063 num_hashes = (ntohs (mh->size) - sizeof(struct GNUNET_MessageHeader))
2064 / sizeof(struct GNUNET_HashCode);
2065 if ((ntohs (mh->size) - sizeof(struct GNUNET_MessageHeader)) !=
2066 num_hashes * sizeof(struct GNUNET_HashCode))
2067 {
2068 GNUNET_break_op (0);
2069 return GNUNET_SYSERR;
2070 }
2071 return GNUNET_OK;
2072}
2073
2074
2075/**
2076 * Handle offers (of `struct GNUNET_HashCode`s) and
2077 * respond with demands (of `struct GNUNET_HashCode`s).
2078 *
2079 * @param cls the union operation
2080 * @param mh the message
2081 */
2082void
2083handle_union_p2p_offer (void *cls,
2084 const struct GNUNET_MessageHeader *mh)
2085{
2086 struct Operation *op = cls;
2087 const struct GNUNET_HashCode *hash;
2088 unsigned int num_hashes;
2089
2090 num_hashes = (ntohs (mh->size) - sizeof(struct GNUNET_MessageHeader))
2091 / sizeof(struct GNUNET_HashCode);
2092 for (hash = (const struct GNUNET_HashCode *) &mh[1];
2093 num_hashes > 0;
2094 hash++, num_hashes--)
2095 {
2096 struct ElementEntry *ee;
2097 struct GNUNET_MessageHeader *demands;
2098 struct GNUNET_MQ_Envelope *ev;
2099
2100 ee = GNUNET_CONTAINER_multihashmap_get (op->set->content->elements,
2101 hash);
2102 if (NULL != ee)
2103 if (GNUNET_YES == _GSS_is_element_of_operation (ee, op))
2104 continue;
2105
2106 if (GNUNET_YES ==
2107 GNUNET_CONTAINER_multihashmap_contains (op->state->demanded_hashes,
2108 hash))
2109 {
2110 LOG (GNUNET_ERROR_TYPE_DEBUG,
2111 "Skipped sending duplicate demand\n");
2112 continue;
2113 }
2114
2115 GNUNET_assert (GNUNET_OK ==
2116 GNUNET_CONTAINER_multihashmap_put (
2117 op->state->demanded_hashes,
2118 hash,
2119 NULL,
2120 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
2121
2122 LOG (GNUNET_ERROR_TYPE_DEBUG,
2123 "[OP %p] Requesting element (hash %s)\n",
2124 op, GNUNET_h2s (hash));
2125 ev = GNUNET_MQ_msg_header_extra (demands,
2126 sizeof(struct GNUNET_HashCode),
2127 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DEMAND);
2128 GNUNET_memcpy (&demands[1],
2129 hash,
2130 sizeof(struct GNUNET_HashCode));
2131 GNUNET_MQ_send (op->mq, ev);
2132 }
2133 GNUNET_CADET_receive_done (op->channel);
2134}
2135
2136
2137/**
2138 * Handle a done message from a remote peer
2139 *
2140 * @param cls the union operation
2141 * @param mh the message
2142 */
2143void
2144handle_union_p2p_done (void *cls,
2145 const struct GNUNET_MessageHeader *mh)
2146{
2147 struct Operation *op = cls;
2148
2149 if (GNUNET_SET_OPERATION_UNION != op->set->operation)
2150 {
2151 GNUNET_break_op (0);
2152 fail_union_operation (op);
2153 return;
2154 }
2155 switch (op->state->phase)
2156 {
2157 case PHASE_INVENTORY_PASSIVE:
2158 /* We got all requests, but still have to send our elements in response. */
2159 op->state->phase = PHASE_FINISH_WAITING;
2160
2161 LOG (GNUNET_ERROR_TYPE_DEBUG,
2162 "got DONE (as passive partner), waiting for our demands to be satisfied\n");
2163 /* The active peer is done sending offers
2164 * and inquiries. This means that all
2165 * our responses to that (demands and offers)
2166 * must be in flight (queued or in mesh).
2167 *
2168 * We should notify the active peer once
2169 * all our demands are satisfied, so that the active
2170 * peer can quit if we gave it everything.
2171 */GNUNET_CADET_receive_done (op->channel);
2172 maybe_finish (op);
2173 return;
2174
2175 case PHASE_INVENTORY_ACTIVE:
2176 LOG (GNUNET_ERROR_TYPE_DEBUG,
2177 "got DONE (as active partner), waiting to finish\n");
2178 /* All demands of the other peer are satisfied,
2179 * and we processed all offers, thus we know
2180 * exactly what our demands must be.
2181 *
2182 * We'll close the channel
2183 * to the other peer once our demands are met.
2184 */op->state->phase = PHASE_FINISH_CLOSING;
2185 GNUNET_CADET_receive_done (op->channel);
2186 maybe_finish (op);
2187 return;
2188
2189 default:
2190 GNUNET_break_op (0);
2191 fail_union_operation (op);
2192 return;
2193 }
2194}
2195
2196
2197void
2198handle_union_p2p_over (void *cls,
2199 const struct GNUNET_MessageHeader *mh)
2200{
2201 send_client_done (cls);
2202}
2203
2204
2205/**
2206 * Initiate operation to evaluate a set union with a remote peer.
2207 *
2208 * @param op operation to perform (to be initialized)
2209 * @param opaque_context message to be transmitted to the listener
2210 * to convince it to accept, may be NULL
2211 */
2212static struct OperationState *
2213union_evaluate (struct Operation *op,
2214 const struct GNUNET_MessageHeader *opaque_context)
2215{
2216 struct OperationState *state;
2217 struct GNUNET_MQ_Envelope *ev;
2218 struct OperationRequestMessage *msg;
2219
2220 ev = GNUNET_MQ_msg_nested_mh (msg,
2221 GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST,
2222 opaque_context);
2223 if (NULL == ev)
2224 {
2225 /* the context message is too large */
2226 GNUNET_break (0);
2227 return NULL;
2228 }
2229 state = GNUNET_new (struct OperationState);
2230 state->demanded_hashes = GNUNET_CONTAINER_multihashmap_create (32,
2231 GNUNET_NO);
2232 /* copy the current generation's strata estimator for this operation */
2233 state->se = strata_estimator_dup (op->set->state->se);
2234 /* we started the operation, thus we have to send the operation request */
2235 state->phase = PHASE_EXPECT_SE;
2236 state->salt_receive = state->salt_send = 42; // FIXME?????
2237 LOG (GNUNET_ERROR_TYPE_DEBUG,
2238 "Initiating union operation evaluation\n");
2239 GNUNET_STATISTICS_update (_GSS_statistics,
2240 "# of total union operations",
2241 1,
2242 GNUNET_NO);
2243 GNUNET_STATISTICS_update (_GSS_statistics,
2244 "# of initiated union operations",
2245 1,
2246 GNUNET_NO);
2247 msg->operation = htonl (GNUNET_SET_OPERATION_UNION);
2248 GNUNET_MQ_send (op->mq,
2249 ev);
2250
2251 if (NULL != opaque_context)
2252 LOG (GNUNET_ERROR_TYPE_DEBUG,
2253 "sent op request with context message\n");
2254 else
2255 LOG (GNUNET_ERROR_TYPE_DEBUG,
2256 "sent op request without context message\n");
2257
2258 op->state = state;
2259 initialize_key_to_element (op);
2260 state->initial_size = GNUNET_CONTAINER_multihashmap32_size (
2261 state->key_to_element);
2262 return state;
2263}
2264
2265
2266/**
2267 * Accept an union operation request from a remote peer.
2268 * Only initializes the private operation state.
2269 *
2270 * @param op operation that will be accepted as a union operation
2271 */
2272static struct OperationState *
2273union_accept (struct Operation *op)
2274{
2275 struct OperationState *state;
2276 const struct StrataEstimator *se;
2277 struct GNUNET_MQ_Envelope *ev;
2278 struct StrataEstimatorMessage *strata_msg;
2279 char *buf;
2280 size_t len;
2281 uint16_t type;
2282
2283 LOG (GNUNET_ERROR_TYPE_DEBUG,
2284 "accepting set union operation\n");
2285 GNUNET_STATISTICS_update (_GSS_statistics,
2286 "# of accepted union operations",
2287 1,
2288 GNUNET_NO);
2289 GNUNET_STATISTICS_update (_GSS_statistics,
2290 "# of total union operations",
2291 1,
2292 GNUNET_NO);
2293
2294 state = GNUNET_new (struct OperationState);
2295 state->se = strata_estimator_dup (op->set->state->se);
2296 state->demanded_hashes = GNUNET_CONTAINER_multihashmap_create (32,
2297 GNUNET_NO);
2298 state->salt_receive = state->salt_send = 42; // FIXME?????
2299 op->state = state;
2300 initialize_key_to_element (op);
2301 state->initial_size = GNUNET_CONTAINER_multihashmap32_size (
2302 state->key_to_element);
2303
2304 /* kick off the operation */
2305 se = state->se;
2306 buf = GNUNET_malloc (se->strata_count * IBF_BUCKET_SIZE * se->ibf_size);
2307 len = strata_estimator_write (se,
2308 buf);
2309 if (len < se->strata_count * IBF_BUCKET_SIZE * se->ibf_size)
2310 type = GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SEC;
2311 else
2312 type = GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SE;
2313 ev = GNUNET_MQ_msg_extra (strata_msg,
2314 len,
2315 type);
2316 GNUNET_memcpy (&strata_msg[1],
2317 buf,
2318 len);
2319 GNUNET_free (buf);
2320 strata_msg->set_size
2321 = GNUNET_htonll (GNUNET_CONTAINER_multihashmap_size (
2322 op->set->content->elements));
2323 GNUNET_MQ_send (op->mq,
2324 ev);
2325 state->phase = PHASE_EXPECT_IBF;
2326 return state;
2327}
2328
2329
2330/**
2331 * Create a new set supporting the union operation
2332 *
2333 * We maintain one strata estimator per set and then manipulate it over the
2334 * lifetime of the set, as recreating a strata estimator would be expensive.
2335 *
2336 * @return the newly created set, NULL on error
2337 */
2338static struct SetState *
2339union_set_create (void)
2340{
2341 struct SetState *set_state;
2342
2343 LOG (GNUNET_ERROR_TYPE_DEBUG,
2344 "union set created\n");
2345 set_state = GNUNET_new (struct SetState);
2346 set_state->se = strata_estimator_create (SE_STRATA_COUNT,
2347 SE_IBF_SIZE, SE_IBF_HASH_NUM);
2348 if (NULL == set_state->se)
2349 {
2350 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2351 "Failed to allocate strata estimator\n");
2352 GNUNET_free (set_state);
2353 return NULL;
2354 }
2355 return set_state;
2356}
2357
2358
2359/**
2360 * Add the element from the given element message to the set.
2361 *
2362 * @param set_state state of the set want to add to
2363 * @param ee the element to add to the set
2364 */
2365static void
2366union_add (struct SetState *set_state,
2367 struct ElementEntry *ee)
2368{
2369 strata_estimator_insert (set_state->se,
2370 get_ibf_key (&ee->element_hash));
2371}
2372
2373
2374/**
2375 * Remove the element given in the element message from the set.
2376 * Only marks the element as removed, so that older set operations can still exchange it.
2377 *
2378 * @param set_state state of the set to remove from
2379 * @param ee set element to remove
2380 */
2381static void
2382union_remove (struct SetState *set_state,
2383 struct ElementEntry *ee)
2384{
2385 strata_estimator_remove (set_state->se,
2386 get_ibf_key (&ee->element_hash));
2387}
2388
2389
2390/**
2391 * Destroy a set that supports the union operation.
2392 *
2393 * @param set_state the set to destroy
2394 */
2395static void
2396union_set_destroy (struct SetState *set_state)
2397{
2398 if (NULL != set_state->se)
2399 {
2400 strata_estimator_destroy (set_state->se);
2401 set_state->se = NULL;
2402 }
2403 GNUNET_free (set_state);
2404}
2405
2406
2407/**
2408 * Copy union-specific set state.
2409 *
2410 * @param state source state for copying the union state
2411 * @return a copy of the union-specific set state
2412 */
2413static struct SetState *
2414union_copy_state (struct SetState *state)
2415{
2416 struct SetState *new_state;
2417
2418 GNUNET_assert ((NULL != state) &&
2419 (NULL != state->se));
2420 new_state = GNUNET_new (struct SetState);
2421 new_state->se = strata_estimator_dup (state->se);
2422
2423 return new_state;
2424}
2425
2426
2427/**
2428 * Handle case where channel went down for an operation.
2429 *
2430 * @param op operation that lost the channel
2431 */
2432static void
2433union_channel_death (struct Operation *op)
2434{
2435 send_client_done (op);
2436 _GSS_operation_destroy (op,
2437 GNUNET_YES);
2438}
2439
2440
2441/**
2442 * Get the table with implementing functions for
2443 * set union.
2444 *
2445 * @return the operation specific VTable
2446 */
2447const struct SetVT *
2448_GSS_union_vt ()
2449{
2450 static const struct SetVT union_vt = {
2451 .create = &union_set_create,
2452 .add = &union_add,
2453 .remove = &union_remove,
2454 .destroy_set = &union_set_destroy,
2455 .evaluate = &union_evaluate,
2456 .accept = &union_accept,
2457 .cancel = &union_op_cancel,
2458 .copy_state = &union_copy_state,
2459 .channel_death = &union_channel_death
2460 };
2461
2462 return &union_vt;
2463}
diff --git a/src/contrib/service/set/gnunet-service-set_union.h b/src/contrib/service/set/gnunet-service-set_union.h
new file mode 100644
index 000000000..68301c96b
--- /dev/null
+++ b/src/contrib/service/set/gnunet-service-set_union.h
@@ -0,0 +1,246 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013-2017 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file set/gnunet-service-set_union.h
22 * @brief two-peer set operations
23 * @author Florian Dold
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_SET_UNION_H
27#define GNUNET_SERVICE_SET_UNION_H
28
29#include "gnunet-service-set.h"
30#include "gnunet-service-set_protocol.h"
31
32
33/**
34 * Handle a strata estimator from a remote peer
35 *
36 * @param cls the union operation
37 * @param msg the message
38 */
39int
40check_union_p2p_strata_estimator (void *cls,
41 const struct StrataEstimatorMessage *msg);
42
43
44/**
45 * Handle a strata estimator from a remote peer
46 *
47 * @param cls the union operation
48 * @param msg the message
49 */
50void
51handle_union_p2p_strata_estimator (void *cls,
52 const struct StrataEstimatorMessage *msg);
53
54
55/**
56 * Check an IBF message from a remote peer.
57 *
58 * Reassemble the IBF from multiple pieces, and
59 * process the whole IBF once possible.
60 *
61 * @param cls the union operation
62 * @param msg the header of the message
63 * @return #GNUNET_OK if @a msg is well-formed
64 */
65int
66check_union_p2p_ibf (void *cls,
67 const struct IBFMessage *msg);
68
69
70/**
71 * Handle an IBF message from a remote peer.
72 *
73 * Reassemble the IBF from multiple pieces, and
74 * process the whole IBF once possible.
75 *
76 * @param cls the union operation
77 * @param msg the header of the message
78 */
79void
80handle_union_p2p_ibf (void *cls,
81 const struct IBFMessage *msg);
82
83
84/**
85 * Check an element message from a remote peer.
86 *
87 * @param cls the union operation
88 * @param emsg the message
89 */
90int
91check_union_p2p_elements (void *cls,
92 const struct GNUNET_SET_ElementMessage *emsg);
93
94
95/**
96 * Handle an element message from a remote peer.
97 * Sent by the other peer either because we decoded an IBF and placed a demand,
98 * or because the other peer switched to full set transmission.
99 *
100 * @param cls the union operation
101 * @param emsg the message
102 */
103void
104handle_union_p2p_elements (void *cls,
105 const struct GNUNET_SET_ElementMessage *emsg);
106
107
108/**
109 * Check a full element message from a remote peer.
110 *
111 * @param cls the union operation
112 * @param emsg the message
113 */
114int
115check_union_p2p_full_element (void *cls,
116 const struct GNUNET_SET_ElementMessage *emsg);
117
118
119/**
120 * Handle an element message from a remote peer.
121 *
122 * @param cls the union operation
123 * @param emsg the message
124 */
125void
126handle_union_p2p_full_element (void *cls,
127 const struct GNUNET_SET_ElementMessage *emsg);
128
129
130/**
131 * Send offers (for GNUNET_Hash-es) in response
132 * to inquiries (for IBF_Key-s).
133 *
134 * @param cls the union operation
135 * @param msg the message
136 */
137int
138check_union_p2p_inquiry (void *cls,
139 const struct InquiryMessage *msg);
140
141
142/**
143 * Send offers (for GNUNET_Hash-es) in response
144 * to inquiries (for IBF_Key-s).
145 *
146 * @param cls the union operation
147 * @param msg the message
148 */
149void
150handle_union_p2p_inquiry (void *cls,
151 const struct InquiryMessage *msg);
152
153
154/**
155 * Handle a request for full set transmission.
156 *
157 * @param cls closure, a set union operation
158 * @param mh the demand message
159 */
160void
161handle_union_p2p_request_full (void *cls,
162 const struct GNUNET_MessageHeader *mh);
163
164
165/**
166 * Handle a "full done" message.
167 *
168 * @param cls closure, a set union operation
169 * @param mh the demand message
170 */
171void
172handle_union_p2p_full_done (void *cls,
173 const struct GNUNET_MessageHeader *mh);
174
175
176/**
177 * Check a demand by the other peer for elements based on a list
178 * of `struct GNUNET_HashCode`s.
179 *
180 * @param cls closure, a set union operation
181 * @param mh the demand message
182 * @return #GNUNET_OK if @a mh is well-formed
183 */
184int
185check_union_p2p_demand (void *cls,
186 const struct GNUNET_MessageHeader *mh);
187
188
189/**
190 * Handle a demand by the other peer for elements based on a list
191 * of `struct GNUNET_HashCode`s.
192 *
193 * @param cls closure, a set union operation
194 * @param mh the demand message
195 */
196void
197handle_union_p2p_demand (void *cls,
198 const struct GNUNET_MessageHeader *mh);
199
200
201/**
202 * Check offer (of `struct GNUNET_HashCode`s).
203 *
204 * @param cls the union operation
205 * @param mh the message
206 * @return #GNUNET_OK if @a mh is well-formed
207 */
208int
209check_union_p2p_offer (void *cls,
210 const struct GNUNET_MessageHeader *mh);
211
212
213/**
214 * Handle offers (of `struct GNUNET_HashCode`s) and
215 * respond with demands (of `struct GNUNET_HashCode`s).
216 *
217 * @param cls the union operation
218 * @param mh the message
219 */
220void
221handle_union_p2p_offer (void *cls,
222 const struct GNUNET_MessageHeader *mh);
223
224
225/**
226 * Handle a done message from a remote peer
227 *
228 * @param cls the union operation
229 * @param mh the message
230 */
231void
232handle_union_p2p_done (void *cls,
233 const struct GNUNET_MessageHeader *mh);
234
235/**
236 * Handle an over message from a remote peer
237 *
238 * @param cls the union operation
239 * @param mh the message
240 */
241void
242handle_union_p2p_over (void *cls,
243 const struct GNUNET_MessageHeader *mh);
244
245
246#endif
diff --git a/src/contrib/service/set/gnunet-service-set_union_strata_estimator.c b/src/contrib/service/set/gnunet-service-set_union_strata_estimator.c
new file mode 100644
index 000000000..6de9fb5eb
--- /dev/null
+++ b/src/contrib/service/set/gnunet-service-set_union_strata_estimator.c
@@ -0,0 +1,297 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file set/gnunet-service-set_union_strata_estimator.c
22 * @brief invertible bloom filter
23 * @author Florian Dold
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "ibf.h"
29#include "gnunet-service-set_union_strata_estimator.h"
30
31
32/**
33 * Should we try compressing the strata estimator? This will
34 * break compatibility with the 0.10.1-network.
35 */
36#define FAIL_10_1_COMPATIBILTIY 1
37
38
39/**
40 * Write the given strata estimator to the buffer.
41 *
42 * @param se strata estimator to serialize
43 * @param[out] buf buffer to write to, must be of appropriate size
44 * @return number of bytes written to @a buf
45 */
46size_t
47strata_estimator_write (const struct StrataEstimator *se,
48 void *buf)
49{
50 char *sbuf = buf;
51 unsigned int i;
52 size_t osize;
53
54 GNUNET_assert (NULL != se);
55 for (i = 0; i < se->strata_count; i++)
56 {
57 ibf_write_slice (se->strata[i],
58 0,
59 se->ibf_size,
60 &sbuf[se->ibf_size * IBF_BUCKET_SIZE * i]);
61 }
62 osize = se->ibf_size * IBF_BUCKET_SIZE * se->strata_count;
63#if FAIL_10_1_COMPATIBILTIY
64 {
65 char *cbuf;
66 size_t nsize;
67
68 if (GNUNET_YES ==
69 GNUNET_try_compression (buf,
70 osize,
71 &cbuf,
72 &nsize))
73 {
74 GNUNET_memcpy (buf, cbuf, nsize);
75 osize = nsize;
76 GNUNET_free (cbuf);
77 }
78 }
79#endif
80 return osize;
81}
82
83
84/**
85 * Read strata from the buffer into the given strata
86 * estimator. The strata estimator must already be allocated.
87 *
88 * @param buf buffer to read from
89 * @param buf_len number of bytes in @a buf
90 * @param is_compressed is the data compressed?
91 * @param[out] se strata estimator to write to
92 * @return #GNUNET_OK on success
93 */
94int
95strata_estimator_read (const void *buf,
96 size_t buf_len,
97 int is_compressed,
98 struct StrataEstimator *se)
99{
100 unsigned int i;
101 size_t osize;
102 char *dbuf;
103
104 dbuf = NULL;
105 if (GNUNET_YES == is_compressed)
106 {
107 osize = se->ibf_size * IBF_BUCKET_SIZE * se->strata_count;
108 dbuf = GNUNET_decompress (buf,
109 buf_len,
110 osize);
111 if (NULL == dbuf)
112 {
113 GNUNET_break_op (0); /* bad compressed input data */
114 return GNUNET_SYSERR;
115 }
116 buf = dbuf;
117 buf_len = osize;
118 }
119
120 if (buf_len != se->strata_count * se->ibf_size * IBF_BUCKET_SIZE)
121 {
122 GNUNET_break (0); /* very odd error */
123 GNUNET_free (dbuf);
124 return GNUNET_SYSERR;
125 }
126
127 for (i = 0; i < se->strata_count; i++)
128 {
129 ibf_read_slice (buf, 0, se->ibf_size, se->strata[i]);
130 buf += se->ibf_size * IBF_BUCKET_SIZE;
131 }
132 GNUNET_free (dbuf);
133 return GNUNET_OK;
134}
135
136
137/**
138 * Add a key to the strata estimator.
139 *
140 * @param se strata estimator to add the key to
141 * @param key key to add
142 */
143void
144strata_estimator_insert (struct StrataEstimator *se,
145 struct IBF_Key key)
146{
147 uint64_t v;
148 unsigned int i;
149
150 v = key.key_val;
151 /* count trailing '1'-bits of v */
152 for (i = 0; v & 1; v >>= 1, i++)
153 /* empty */;
154 ibf_insert (se->strata[i], key);
155}
156
157
158void
159strata_estimator_remove (struct StrataEstimator *se,
160 struct IBF_Key key)
161{
162 uint64_t v;
163 unsigned int i;
164
165 v = key.key_val;
166 /* count trailing '1'-bits of v */
167 for (i = 0; v & 1; v >>= 1, i++)
168 /* empty */;
169 ibf_remove (se->strata[i], key);
170}
171
172
173/**
174 * Create a new strata estimator with the given parameters.
175 *
176 * @param strata_count number of stratas, that is, number of ibfs in the estimator
177 * @param ibf_size size of each ibf stratum
178 * @param ibf_hashnum hashnum parameter of each ibf
179 * @return a freshly allocated, empty strata estimator, NULL on error
180 */
181struct StrataEstimator *
182strata_estimator_create (unsigned int strata_count,
183 uint32_t ibf_size,
184 uint8_t ibf_hashnum)
185{
186 struct StrataEstimator *se;
187 unsigned int i;
188 unsigned int j;
189
190 se = GNUNET_new (struct StrataEstimator);
191 se->strata_count = strata_count;
192 se->ibf_size = ibf_size;
193 se->strata = GNUNET_new_array (strata_count,
194 struct InvertibleBloomFilter *);
195 for (i = 0; i < strata_count; i++)
196 {
197 se->strata[i] = ibf_create (ibf_size, ibf_hashnum);
198 if (NULL == se->strata[i])
199 {
200 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
201 "Failed to allocate memory for strata estimator\n");
202 for (j = 0; j < i; j++)
203 ibf_destroy (se->strata[i]);
204 GNUNET_free (se);
205 return NULL;
206 }
207 }
208 return se;
209}
210
211
212/**
213 * Estimate set difference with two strata estimators,
214 * i.e. arrays of IBFs.
215 * Does not not modify its arguments.
216 *
217 * @param se1 first strata estimator
218 * @param se2 second strata estimator
219 * @return the estimated difference
220 */
221unsigned int
222strata_estimator_difference (const struct StrataEstimator *se1,
223 const struct StrataEstimator *se2)
224{
225 unsigned int count;
226
227 GNUNET_assert (se1->strata_count == se2->strata_count);
228 count = 0;
229 for (int i = se1->strata_count - 1; i >= 0; i--)
230 {
231 struct InvertibleBloomFilter *diff;
232 /* number of keys decoded from the ibf */
233
234 /* FIXME: implement this without always allocating new IBFs */
235 diff = ibf_dup (se1->strata[i]);
236 ibf_subtract (diff, se2->strata[i]);
237 for (int ibf_count = 0; GNUNET_YES; ibf_count++)
238 {
239 int more;
240
241 more = ibf_decode (diff, NULL, NULL);
242 if (GNUNET_NO == more)
243 {
244 count += ibf_count;
245 break;
246 }
247 /* Estimate if decoding fails or would not terminate */
248 if ((GNUNET_SYSERR == more) || (ibf_count > diff->size))
249 {
250 ibf_destroy (diff);
251 return count * (1 << (i + 1));
252 }
253 }
254 ibf_destroy (diff);
255 }
256 return count;
257}
258
259
260/**
261 * Make a copy of a strata estimator.
262 *
263 * @param se the strata estimator to copy
264 * @return the copy
265 */
266struct StrataEstimator *
267strata_estimator_dup (struct StrataEstimator *se)
268{
269 struct StrataEstimator *c;
270 unsigned int i;
271
272 c = GNUNET_new (struct StrataEstimator);
273 c->strata_count = se->strata_count;
274 c->ibf_size = se->ibf_size;
275 c->strata = GNUNET_new_array (se->strata_count,
276 struct InvertibleBloomFilter *);
277 for (i = 0; i < se->strata_count; i++)
278 c->strata[i] = ibf_dup (se->strata[i]);
279 return c;
280}
281
282
283/**
284 * Destroy a strata estimator, free all of its resources.
285 *
286 * @param se strata estimator to destroy.
287 */
288void
289strata_estimator_destroy (struct StrataEstimator *se)
290{
291 unsigned int i;
292
293 for (i = 0; i < se->strata_count; i++)
294 ibf_destroy (se->strata[i]);
295 GNUNET_free (se->strata);
296 GNUNET_free (se);
297}
diff --git a/src/contrib/service/set/gnunet-service-set_union_strata_estimator.h b/src/contrib/service/set/gnunet-service-set_union_strata_estimator.h
new file mode 100644
index 000000000..94267a2dc
--- /dev/null
+++ b/src/contrib/service/set/gnunet-service-set_union_strata_estimator.h
@@ -0,0 +1,169 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file set/gnunet-service-set_union_strata_estimator.h
23 * @brief estimator of set difference
24 * @author Florian Dold
25 */
26
27#ifndef GNUNET_CONSENSUS_STRATA_ESTIMATOR_H
28#define GNUNET_CONSENSUS_STRATA_ESTIMATOR_H
29
30#include "platform.h"
31#include "gnunet_common.h"
32#include "gnunet_util_lib.h"
33
34#ifdef __cplusplus
35extern "C"
36{
37#if 0 /* keep Emacsens' auto-indent happy */
38}
39#endif
40#endif
41
42
43/**
44 * A handle to a strata estimator.
45 */
46struct StrataEstimator
47{
48 /**
49 * The IBFs of this strata estimator.
50 */
51 struct InvertibleBloomFilter **strata;
52
53 /**
54 * Size of the IBF array in @e strata
55 */
56 unsigned int strata_count;
57
58 /**
59 * Size of each IBF stratum (in bytes)
60 */
61 unsigned int ibf_size;
62};
63
64
65/**
66 * Write the given strata estimator to the buffer.
67 *
68 * @param se strata estimator to serialize
69 * @param[out] buf buffer to write to, must be of appropriate size
70 * @return number of bytes written to @a buf
71 */
72size_t
73strata_estimator_write (const struct StrataEstimator *se,
74 void *buf);
75
76
77/**
78 * Read strata from the buffer into the given strata
79 * estimator. The strata estimator must already be allocated.
80 *
81 * @param buf buffer to read from
82 * @param buf_len number of bytes in @a buf
83 * @param is_compressed is the data compressed?
84 * @param[out] se strata estimator to write to
85 * @return #GNUNET_OK on success
86 */
87int
88strata_estimator_read (const void *buf,
89 size_t buf_len,
90 int is_compressed,
91 struct StrataEstimator *se);
92
93
94/**
95 * Create a new strata estimator with the given parameters.
96 *
97 * @param strata_count number of stratas, that is, number of ibfs in the estimator
98 * @param ibf_size size of each ibf stratum
99 * @param ibf_hashnum hashnum parameter of each ibf
100 * @return a freshly allocated, empty strata estimator, NULL on error
101 */
102struct StrataEstimator *
103strata_estimator_create (unsigned int strata_count,
104 uint32_t ibf_size,
105 uint8_t ibf_hashnum);
106
107
108/**
109 * Get an estimation of the symmetric difference of the elements
110 * contained in both strata estimators.
111 *
112 * @param se1 first strata estimator
113 * @param se2 second strata estimator
114 * @return abs(|se1| - |se2|)
115 */
116unsigned int
117strata_estimator_difference (const struct StrataEstimator *se1,
118 const struct StrataEstimator *se2);
119
120
121/**
122 * Add a key to the strata estimator.
123 *
124 * @param se strata estimator to add the key to
125 * @param key key to add
126 */
127void
128strata_estimator_insert (struct StrataEstimator *se,
129 struct IBF_Key key);
130
131
132/**
133 * Remove a key from the strata estimator.
134 *
135 * @param se strata estimator to remove the key from
136 * @param key key to remove
137 */
138void
139strata_estimator_remove (struct StrataEstimator *se,
140 struct IBF_Key key);
141
142
143/**
144 * Destroy a strata estimator, free all of its resources.
145 *
146 * @param se strata estimator to destroy.
147 */
148void
149strata_estimator_destroy (struct StrataEstimator *se);
150
151
152/**
153 * Make a copy of a strata estimator.
154 *
155 * @param se the strata estimator to copy
156 * @return the copy
157 */
158struct StrataEstimator *
159strata_estimator_dup (struct StrataEstimator *se);
160
161
162#if 0 /* keep Emacsens' auto-indent happy */
163{
164#endif
165#ifdef __cplusplus
166}
167#endif
168
169#endif
diff --git a/src/contrib/service/set/gnunet-set-ibf-profiler.c b/src/contrib/service/set/gnunet-set-ibf-profiler.c
new file mode 100644
index 000000000..6465b15b8
--- /dev/null
+++ b/src/contrib/service/set/gnunet-set-ibf-profiler.c
@@ -0,0 +1,308 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file set/gnunet-set-ibf-profiler.c
23 * @brief tool for profiling the invertible bloom filter implementation
24 * @author Florian Dold
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30#include "ibf.h"
31
32static unsigned int asize = 10;
33static unsigned int bsize = 10;
34static unsigned int csize = 10;
35static unsigned int hash_num = 4;
36static unsigned int ibf_size = 80;
37
38/* FIXME: add parameter for this */
39static enum GNUNET_CRYPTO_Quality random_quality = GNUNET_CRYPTO_QUALITY_WEAK;
40
41static struct GNUNET_CONTAINER_MultiHashMap *set_a;
42static struct GNUNET_CONTAINER_MultiHashMap *set_b;
43/* common elements in a and b */
44static struct GNUNET_CONTAINER_MultiHashMap *set_c;
45
46static struct GNUNET_CONTAINER_MultiHashMap *key_to_hashcode;
47
48static struct InvertibleBloomFilter *ibf_a;
49static struct InvertibleBloomFilter *ibf_b;
50
51
52static void
53register_hashcode (struct GNUNET_HashCode *hash)
54{
55 struct GNUNET_HashCode replicated;
56 struct IBF_Key key;
57
58 key = ibf_key_from_hashcode (hash);
59 ibf_hashcode_from_key (key, &replicated);
60 (void) GNUNET_CONTAINER_multihashmap_put (
61 key_to_hashcode,
62 &replicated,
63 GNUNET_memdup (hash, sizeof *hash),
64 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
65}
66
67
68static void
69iter_hashcodes (struct IBF_Key key,
70 GNUNET_CONTAINER_MultiHashMapIteratorCallback iter,
71 void *cls)
72{
73 struct GNUNET_HashCode replicated;
74
75 ibf_hashcode_from_key (key, &replicated);
76 GNUNET_CONTAINER_multihashmap_get_multiple (key_to_hashcode,
77 &replicated,
78 iter,
79 cls);
80}
81
82
83static int
84insert_iterator (void *cls, const struct GNUNET_HashCode *key, void *value)
85{
86 struct InvertibleBloomFilter *ibf = cls;
87
88 ibf_insert (ibf, ibf_key_from_hashcode (key));
89 return GNUNET_YES;
90}
91
92
93static int
94remove_iterator (void *cls, const struct GNUNET_HashCode *key, void *value)
95{
96 struct GNUNET_CONTAINER_MultiHashMap *hashmap = cls;
97
98 /* if remove fails, there just was a collision with another key */
99 (void) GNUNET_CONTAINER_multihashmap_remove (hashmap, value, NULL);
100 return GNUNET_YES;
101}
102
103
104static void
105run (void *cls,
106 char *const *args,
107 const char *cfgfile,
108 const struct GNUNET_CONFIGURATION_Handle *cfg)
109{
110 struct GNUNET_HashCode id;
111 struct IBF_Key ibf_key;
112 int i;
113 int side;
114 int res;
115 struct GNUNET_TIME_Absolute start_time;
116 struct GNUNET_TIME_Relative delta_time;
117
118 set_a =
119 GNUNET_CONTAINER_multihashmap_create (((asize == 0) ? 1 : (asize + csize)),
120 GNUNET_NO);
121 set_b =
122 GNUNET_CONTAINER_multihashmap_create (((bsize == 0) ? 1 : (bsize + csize)),
123 GNUNET_NO);
124 set_c = GNUNET_CONTAINER_multihashmap_create (((csize == 0) ? 1 : csize),
125 GNUNET_NO);
126
127 key_to_hashcode =
128 GNUNET_CONTAINER_multihashmap_create (((asize + bsize + csize == 0)
129 ? 1
130 : (asize + bsize + csize)),
131 GNUNET_NO);
132
133 printf ("hash-num=%u, size=%u, #(A-B)=%u, #(B-A)=%u, #(A&B)=%u\n",
134 hash_num,
135 ibf_size,
136 asize,
137 bsize,
138 csize);
139
140 i = 0;
141 while (i < asize)
142 {
143 GNUNET_CRYPTO_hash_create_random (random_quality, &id);
144 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (set_a, &id))
145 continue;
146 GNUNET_break (GNUNET_OK ==
147 GNUNET_CONTAINER_multihashmap_put (
148 set_a,
149 &id,
150 NULL,
151 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
152 register_hashcode (&id);
153 i++;
154 }
155 i = 0;
156 while (i < bsize)
157 {
158 GNUNET_CRYPTO_hash_create_random (random_quality, &id);
159 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (set_a, &id))
160 continue;
161 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (set_b, &id))
162 continue;
163 GNUNET_break (GNUNET_OK ==
164 GNUNET_CONTAINER_multihashmap_put (
165 set_b,
166 &id,
167 NULL,
168 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
169 register_hashcode (&id);
170 i++;
171 }
172 i = 0;
173 while (i < csize)
174 {
175 GNUNET_CRYPTO_hash_create_random (random_quality, &id);
176 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (set_a, &id))
177 continue;
178 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (set_b, &id))
179 continue;
180 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (set_c, &id))
181 continue;
182 GNUNET_break (GNUNET_OK ==
183 GNUNET_CONTAINER_multihashmap_put (
184 set_c,
185 &id,
186 NULL,
187 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
188 register_hashcode (&id);
189 i++;
190 }
191
192 ibf_a = ibf_create (ibf_size, hash_num);
193 ibf_b = ibf_create (ibf_size, hash_num);
194 if ((NULL == ibf_a) || (NULL == ibf_b))
195 {
196 /* insufficient memory */
197 GNUNET_break (0);
198 GNUNET_SCHEDULER_shutdown ();
199 return;
200 }
201
202
203 printf ("generated sets\n");
204
205 start_time = GNUNET_TIME_absolute_get ();
206
207 GNUNET_CONTAINER_multihashmap_iterate (set_a, &insert_iterator, ibf_a);
208 GNUNET_CONTAINER_multihashmap_iterate (set_b, &insert_iterator, ibf_b);
209 GNUNET_CONTAINER_multihashmap_iterate (set_c, &insert_iterator, ibf_a);
210 GNUNET_CONTAINER_multihashmap_iterate (set_c, &insert_iterator, ibf_b);
211
212 delta_time = GNUNET_TIME_absolute_get_duration (start_time);
213
214 printf ("encoded in: %s\n",
215 GNUNET_STRINGS_relative_time_to_string (delta_time, GNUNET_NO));
216
217 ibf_subtract (ibf_a, ibf_b);
218
219
220 start_time = GNUNET_TIME_absolute_get ();
221
222 for (i = 0; i <= asize + bsize; i++)
223 {
224 res = ibf_decode (ibf_a, &side, &ibf_key);
225 if (GNUNET_SYSERR == res)
226 {
227 printf ("decode failed, %u/%u elements left\n",
228 GNUNET_CONTAINER_multihashmap_size (set_a)
229 + GNUNET_CONTAINER_multihashmap_size (set_b),
230 asize + bsize);
231 return;
232 }
233 if (GNUNET_NO == res)
234 {
235 if ((0 == GNUNET_CONTAINER_multihashmap_size (set_b)) &&
236 (0 == GNUNET_CONTAINER_multihashmap_size (set_a)))
237 {
238 delta_time = GNUNET_TIME_absolute_get_duration (start_time);
239 printf ("decoded successfully in: %s\n",
240 GNUNET_STRINGS_relative_time_to_string (delta_time, GNUNET_NO));
241 }
242 else
243 {
244 printf ("decode missed elements (should never happen)\n");
245 }
246 return;
247 }
248
249 if (side == 1)
250 iter_hashcodes (ibf_key, remove_iterator, set_a);
251 if (side == -1)
252 iter_hashcodes (ibf_key, remove_iterator, set_b);
253 }
254 printf ("cyclic IBF, %u/%u elements left\n",
255 GNUNET_CONTAINER_multihashmap_size (set_a)
256 + GNUNET_CONTAINER_multihashmap_size (set_b),
257 asize + bsize);
258}
259
260
261int
262main (int argc, char **argv)
263{
264 struct GNUNET_GETOPT_CommandLineOption options[] = {
265 GNUNET_GETOPT_option_uint ('A',
266 "asize",
267 NULL,
268 gettext_noop ("number of element in set A-B"),
269 &asize),
270
271 GNUNET_GETOPT_option_uint ('B',
272 "bsize",
273 NULL,
274 gettext_noop ("number of element in set B-A"),
275 &bsize),
276
277 GNUNET_GETOPT_option_uint ('C',
278 "csize",
279 NULL,
280 gettext_noop (
281 "number of common elements in A and B"),
282 &csize),
283
284 GNUNET_GETOPT_option_uint ('k',
285 "hash-num",
286 NULL,
287 gettext_noop ("hash num"),
288 &hash_num),
289
290 GNUNET_GETOPT_option_uint ('s',
291 "ibf-size",
292 NULL,
293 gettext_noop ("ibf size"),
294 &ibf_size),
295
296 GNUNET_GETOPT_OPTION_END
297 };
298
299 GNUNET_PROGRAM_run2 (argc,
300 argv,
301 "gnunet-consensus-ibf",
302 "help",
303 options,
304 &run,
305 NULL,
306 GNUNET_YES);
307 return 0;
308}
diff --git a/src/contrib/service/set/gnunet-set-profiler.c b/src/contrib/service/set/gnunet-set-profiler.c
new file mode 100644
index 000000000..3014861a6
--- /dev/null
+++ b/src/contrib/service/set/gnunet-set-profiler.c
@@ -0,0 +1,508 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file set/gnunet-set-profiler.c
23 * @brief profiling tool for set
24 * @author Florian Dold
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_statistics_service.h"
29#include "gnunet_set_service.h"
30#include "gnunet_testbed_service.h"
31
32
33static int ret;
34
35static unsigned int num_a = 5;
36static unsigned int num_b = 5;
37static unsigned int num_c = 20;
38
39static char *op_str = "union";
40
41const static struct GNUNET_CONFIGURATION_Handle *config;
42
43struct SetInfo
44{
45 char *id;
46 struct GNUNET_SET_Handle *set;
47 struct GNUNET_SET_OperationHandle *oh;
48 struct GNUNET_CONTAINER_MultiHashMap *sent;
49 struct GNUNET_CONTAINER_MultiHashMap *received;
50 int done;
51} info1, info2;
52
53static struct GNUNET_CONTAINER_MultiHashMap *common_sent;
54
55static struct GNUNET_HashCode app_id;
56
57static struct GNUNET_PeerIdentity local_peer;
58
59static struct GNUNET_SET_ListenHandle *set_listener;
60
61static int byzantine;
62static unsigned int force_delta;
63static unsigned int force_full;
64static unsigned int element_size = 32;
65
66/**
67 * Handle to the statistics service.
68 */
69static struct GNUNET_STATISTICS_Handle *statistics;
70
71/**
72 * The profiler will write statistics
73 * for all peers to the file with this name.
74 */
75static char *statistics_filename;
76
77/**
78 * The profiler will write statistics
79 * for all peers to this file.
80 */
81static FILE *statistics_file;
82
83
84static int
85map_remove_iterator (void *cls,
86 const struct GNUNET_HashCode *key,
87 void *value)
88{
89 struct GNUNET_CONTAINER_MultiHashMap *m = cls;
90 int ret;
91
92 GNUNET_assert (NULL != key);
93
94 ret = GNUNET_CONTAINER_multihashmap_remove_all (m, key);
95 if (GNUNET_OK != ret)
96 printf ("spurious element\n");
97 return GNUNET_YES;
98}
99
100
101/**
102 * Callback function to process statistic values.
103 *
104 * @param cls closure
105 * @param subsystem name of subsystem that created the statistic
106 * @param name the name of the datum
107 * @param value the current value
108 * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not
109 * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
110 */
111static int
112statistics_result (void *cls,
113 const char *subsystem,
114 const char *name,
115 uint64_t value,
116 int is_persistent)
117{
118 if (NULL != statistics_file)
119 {
120 fprintf (statistics_file, "%s\t%s\t%lu\n", subsystem, name, (unsigned
121 long) value);
122 }
123 return GNUNET_OK;
124}
125
126
127static void
128statistics_done (void *cls,
129 int success)
130{
131 GNUNET_assert (GNUNET_YES == success);
132 if (NULL != statistics_file)
133 fclose (statistics_file);
134 GNUNET_SCHEDULER_shutdown ();
135}
136
137
138static void
139check_all_done (void)
140{
141 if ((info1.done == GNUNET_NO) || (info2.done == GNUNET_NO))
142 return;
143
144 GNUNET_CONTAINER_multihashmap_iterate (info1.received, map_remove_iterator,
145 info2.sent);
146 GNUNET_CONTAINER_multihashmap_iterate (info2.received, map_remove_iterator,
147 info1.sent);
148
149 printf ("set a: %d missing elements\n", GNUNET_CONTAINER_multihashmap_size (
150 info1.sent));
151 printf ("set b: %d missing elements\n", GNUNET_CONTAINER_multihashmap_size (
152 info2.sent));
153
154 if (NULL == statistics_filename)
155 {
156 GNUNET_SCHEDULER_shutdown ();
157 return;
158 }
159
160 statistics_file = fopen (statistics_filename, "w");
161 GNUNET_STATISTICS_get (statistics, NULL, NULL,
162 &statistics_done,
163 &statistics_result, NULL);
164}
165
166
167static void
168set_result_cb (void *cls,
169 const struct GNUNET_SET_Element *element,
170 uint64_t current_size,
171 enum GNUNET_SET_Status status)
172{
173 struct SetInfo *info = cls;
174 struct GNUNET_HashCode hash;
175
176 GNUNET_assert (GNUNET_NO == info->done);
177 switch (status)
178 {
179 case GNUNET_SET_STATUS_DONE:
180 case GNUNET_SET_STATUS_HALF_DONE:
181 info->done = GNUNET_YES;
182 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set %s done\n", info->id);
183 check_all_done ();
184 info->oh = NULL;
185 return;
186
187 case GNUNET_SET_STATUS_FAILURE:
188 info->oh = NULL;
189 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "failure\n");
190 GNUNET_SCHEDULER_shutdown ();
191 return;
192
193 case GNUNET_SET_STATUS_ADD_LOCAL:
194 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set %s: local element\n", info->id);
195 break;
196
197 case GNUNET_SET_STATUS_ADD_REMOTE:
198 GNUNET_CRYPTO_hash (element->data, element->size, &hash);
199 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set %s: remote element %s\n", info->id,
200 GNUNET_h2s (&hash));
201 // XXX: record and check
202 return;
203
204 default:
205 GNUNET_assert (0);
206 }
207
208 if (element->size != element_size)
209 {
210 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
211 "wrong element size: %u, expected %u\n",
212 element->size,
213 (unsigned int) sizeof(struct GNUNET_HashCode));
214 GNUNET_assert (0);
215 }
216
217 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set %s: got element (%s)\n",
218 info->id, GNUNET_h2s (element->data));
219 GNUNET_assert (NULL != element->data);
220 struct GNUNET_HashCode data_hash;
221 GNUNET_CRYPTO_hash (element->data, element_size, &data_hash);
222 GNUNET_CONTAINER_multihashmap_put (info->received,
223 &data_hash, NULL,
224 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
225}
226
227
228static void
229set_listen_cb (void *cls,
230 const struct GNUNET_PeerIdentity *other_peer,
231 const struct GNUNET_MessageHeader *context_msg,
232 struct GNUNET_SET_Request *request)
233{
234 /* max. 2 options plus terminator */
235 struct GNUNET_SET_Option opts[3] = { { 0 } };
236 unsigned int n_opts = 0;
237
238 if (NULL == request)
239 {
240 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
241 "listener failed\n");
242 return;
243 }
244 GNUNET_assert (NULL == info2.oh);
245 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
246 "set listen cb called\n");
247 if (byzantine)
248 {
249 opts[n_opts++] = (struct GNUNET_SET_Option) { .type =
250 GNUNET_SET_OPTION_BYZANTINE };
251 }
252 GNUNET_assert (! (force_full && force_delta));
253 if (force_full)
254 {
255 opts[n_opts++] = (struct GNUNET_SET_Option) { .type =
256 GNUNET_SET_OPTION_FORCE_FULL };
257 }
258 if (force_delta)
259 {
260 opts[n_opts++] = (struct GNUNET_SET_Option) { .type =
261 GNUNET_SET_OPTION_FORCE_DELTA };
262 }
263
264 opts[n_opts].type = 0;
265 info2.oh = GNUNET_SET_accept (request, GNUNET_SET_RESULT_SYMMETRIC,
266 opts,
267 set_result_cb, &info2);
268 GNUNET_SET_commit (info2.oh, info2.set);
269}
270
271
272static int
273set_insert_iterator (void *cls,
274 const struct GNUNET_HashCode *key,
275 void *value)
276{
277 struct GNUNET_SET_Handle *set = cls;
278 struct GNUNET_SET_Element el;
279
280 el.element_type = 0;
281 el.data = value;
282 el.size = element_size;
283 GNUNET_SET_add_element (set, &el, NULL, NULL);
284 return GNUNET_YES;
285}
286
287
288static void
289handle_shutdown (void *cls)
290{
291 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
292 "Shutting down set profiler\n");
293 if (NULL != set_listener)
294 {
295 GNUNET_SET_listen_cancel (set_listener);
296 set_listener = NULL;
297 }
298 if (NULL != info1.oh)
299 {
300 GNUNET_SET_operation_cancel (info1.oh);
301 info1.oh = NULL;
302 }
303 if (NULL != info2.oh)
304 {
305 GNUNET_SET_operation_cancel (info2.oh);
306 info2.oh = NULL;
307 }
308 if (NULL != info1.set)
309 {
310 GNUNET_SET_destroy (info1.set);
311 info1.set = NULL;
312 }
313 if (NULL != info2.set)
314 {
315 GNUNET_SET_destroy (info2.set);
316 info2.set = NULL;
317 }
318 GNUNET_STATISTICS_destroy (statistics, GNUNET_NO);
319}
320
321
322static void
323run (void *cls,
324 const struct GNUNET_CONFIGURATION_Handle *cfg,
325 struct GNUNET_TESTING_Peer *peer)
326{
327 unsigned int i;
328 struct GNUNET_HashCode hash;
329 /* max. 2 options plus terminator */
330 struct GNUNET_SET_Option opts[3] = { { 0 } };
331 unsigned int n_opts = 0;
332
333 config = cfg;
334
335 GNUNET_assert (element_size > 0);
336
337 if (GNUNET_OK != GNUNET_CRYPTO_get_peer_identity (cfg, &local_peer))
338 {
339 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "could not retrieve host identity\n");
340 ret = 0;
341 return;
342 }
343
344 statistics = GNUNET_STATISTICS_create ("set-profiler", cfg);
345
346 GNUNET_SCHEDULER_add_shutdown (&handle_shutdown, NULL);
347
348 info1.id = "a";
349 info2.id = "b";
350
351 info1.sent = GNUNET_CONTAINER_multihashmap_create (num_a + 1, GNUNET_NO);
352 info2.sent = GNUNET_CONTAINER_multihashmap_create (num_b + 1, GNUNET_NO);
353 common_sent = GNUNET_CONTAINER_multihashmap_create (num_c + 1, GNUNET_NO);
354
355 info1.received = GNUNET_CONTAINER_multihashmap_create (num_a + 1, GNUNET_NO);
356 info2.received = GNUNET_CONTAINER_multihashmap_create (num_b + 1, GNUNET_NO);
357
358 for (i = 0; i < num_a; i++)
359 {
360 char *data = GNUNET_malloc (element_size);
361 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, data, element_size);
362 GNUNET_CRYPTO_hash (data, element_size, &hash);
363 GNUNET_CONTAINER_multihashmap_put (info1.sent, &hash, data,
364 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
365 }
366
367 for (i = 0; i < num_b; i++)
368 {
369 char *data = GNUNET_malloc (element_size);
370 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, data, element_size);
371 GNUNET_CRYPTO_hash (data, element_size, &hash);
372 GNUNET_CONTAINER_multihashmap_put (info2.sent, &hash, data,
373 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
374 }
375
376 for (i = 0; i < num_c; i++)
377 {
378 char *data = GNUNET_malloc (element_size);
379 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, data, element_size);
380 GNUNET_CRYPTO_hash (data, element_size, &hash);
381 GNUNET_CONTAINER_multihashmap_put (common_sent, &hash, data,
382 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
383 }
384
385 GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_STRONG, &app_id);
386
387 /* FIXME: also implement intersection etc. */
388 info1.set = GNUNET_SET_create (config, GNUNET_SET_OPERATION_UNION);
389 info2.set = GNUNET_SET_create (config, GNUNET_SET_OPERATION_UNION);
390
391 GNUNET_CONTAINER_multihashmap_iterate (info1.sent, set_insert_iterator,
392 info1.set);
393 GNUNET_CONTAINER_multihashmap_iterate (info2.sent, set_insert_iterator,
394 info2.set);
395 GNUNET_CONTAINER_multihashmap_iterate (common_sent, set_insert_iterator,
396 info1.set);
397 GNUNET_CONTAINER_multihashmap_iterate (common_sent, set_insert_iterator,
398 info2.set);
399
400 set_listener = GNUNET_SET_listen (config, GNUNET_SET_OPERATION_UNION,
401 &app_id, set_listen_cb, NULL);
402
403
404 if (byzantine)
405 {
406 opts[n_opts++] = (struct GNUNET_SET_Option) { .type =
407 GNUNET_SET_OPTION_BYZANTINE };
408 }
409 GNUNET_assert (! (force_full && force_delta));
410 if (force_full)
411 {
412 opts[n_opts++] = (struct GNUNET_SET_Option) { .type =
413 GNUNET_SET_OPTION_FORCE_FULL };
414 }
415 if (force_delta)
416 {
417 opts[n_opts++] = (struct GNUNET_SET_Option) { .type =
418 GNUNET_SET_OPTION_FORCE_DELTA };
419 }
420
421 opts[n_opts].type = 0;
422
423 info1.oh = GNUNET_SET_prepare (&local_peer, &app_id, NULL,
424 GNUNET_SET_RESULT_SYMMETRIC,
425 opts,
426 set_result_cb, &info1);
427 GNUNET_SET_commit (info1.oh, info1.set);
428 GNUNET_SET_destroy (info1.set);
429 info1.set = NULL;
430}
431
432
433static void
434pre_run (void *cls, char *const *args, const char *cfgfile,
435 const struct GNUNET_CONFIGURATION_Handle *cfg)
436{
437 if (0 != GNUNET_TESTING_peer_run ("set-profiler",
438 cfgfile,
439 &run, NULL))
440 ret = 2;
441}
442
443
444int
445main (int argc, char **argv)
446{
447 struct GNUNET_GETOPT_CommandLineOption options[] = {
448 GNUNET_GETOPT_option_uint ('A',
449 "num-first",
450 NULL,
451 gettext_noop ("number of values"),
452 &num_a),
453
454 GNUNET_GETOPT_option_uint ('B',
455 "num-second",
456 NULL,
457 gettext_noop ("number of values"),
458 &num_b),
459
460 GNUNET_GETOPT_option_flag ('b',
461 "byzantine",
462 gettext_noop ("use byzantine mode"),
463 &byzantine),
464
465 GNUNET_GETOPT_option_uint ('f',
466 "force-full",
467 NULL,
468 gettext_noop ("force sending full set"),
469 &force_full),
470
471 GNUNET_GETOPT_option_uint ('d',
472 "force-delta",
473 NULL,
474 gettext_noop ("number delta operation"),
475 &force_delta),
476
477 GNUNET_GETOPT_option_uint ('C',
478 "num-common",
479 NULL,
480 gettext_noop ("number of values"),
481 &num_c),
482
483 GNUNET_GETOPT_option_string ('x',
484 "operation",
485 NULL,
486 gettext_noop ("operation to execute"),
487 &op_str),
488
489 GNUNET_GETOPT_option_uint ('w',
490 "element-size",
491 NULL,
492 gettext_noop ("element size"),
493 &element_size),
494
495 GNUNET_GETOPT_option_filename ('s',
496 "statistics",
497 "FILENAME",
498 gettext_noop ("write statistics to file"),
499 &statistics_filename),
500
501 GNUNET_GETOPT_OPTION_END
502 };
503
504 GNUNET_PROGRAM_run2 (argc, argv, "gnunet-set-profiler",
505 "help",
506 options, &pre_run, NULL, GNUNET_YES);
507 return ret;
508}
diff --git a/src/contrib/service/set/ibf.c b/src/contrib/service/set/ibf.c
new file mode 100644
index 000000000..b6fb52b6b
--- /dev/null
+++ b/src/contrib/service/set/ibf.c
@@ -0,0 +1,410 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file set/ibf.c
23 * @brief implementation of the invertible bloom filter
24 * @author Florian Dold
25 */
26
27#include "platform.h"
28#include "ibf.h"
29
30/**
31 * Compute the key's hash from the key.
32 * Redefine to use a different hash function.
33 */
34#define IBF_KEY_HASH_VAL(k) (GNUNET_CRYPTO_crc32_n (&(k), sizeof(struct \
35 IBF_KeyHash)))
36
37/**
38 * Create a key from a hashcode.
39 *
40 * @param hash the hashcode
41 * @return a key
42 */
43struct IBF_Key
44ibf_key_from_hashcode (const struct GNUNET_HashCode *hash)
45{
46 return *(struct IBF_Key *) hash;
47}
48
49
50/**
51 * Create a hashcode from a key, by replicating the key
52 * until the hascode is filled
53 *
54 * @param key the key
55 * @param dst hashcode to store the result in
56 */
57void
58ibf_hashcode_from_key (struct IBF_Key key,
59 struct GNUNET_HashCode *dst)
60{
61 struct IBF_Key *p;
62 unsigned int i;
63 const unsigned int keys_per_hashcode = sizeof(struct GNUNET_HashCode)
64 / sizeof(struct IBF_Key);
65
66 p = (struct IBF_Key *) dst;
67 for (i = 0; i < keys_per_hashcode; i++)
68 *p++ = key;
69}
70
71
72/**
73 * Create an invertible bloom filter.
74 *
75 * @param size number of IBF buckets
76 * @param hash_num number of buckets one element is hashed in
77 * @return the newly created invertible bloom filter, NULL on error
78 */
79struct InvertibleBloomFilter *
80ibf_create (uint32_t size, uint8_t hash_num)
81{
82 struct InvertibleBloomFilter *ibf;
83
84 GNUNET_assert (0 != size);
85
86 ibf = GNUNET_new (struct InvertibleBloomFilter);
87 ibf->count = GNUNET_malloc_large (size * sizeof(uint8_t));
88 if (NULL == ibf->count)
89 {
90 GNUNET_free (ibf);
91 return NULL;
92 }
93 ibf->key_sum = GNUNET_malloc_large (size * sizeof(struct IBF_Key));
94 if (NULL == ibf->key_sum)
95 {
96 GNUNET_free (ibf->count);
97 GNUNET_free (ibf);
98 return NULL;
99 }
100 ibf->key_hash_sum = GNUNET_malloc_large (size * sizeof(struct IBF_KeyHash));
101 if (NULL == ibf->key_hash_sum)
102 {
103 GNUNET_free (ibf->key_sum);
104 GNUNET_free (ibf->count);
105 GNUNET_free (ibf);
106 return NULL;
107 }
108 ibf->size = size;
109 ibf->hash_num = hash_num;
110
111 return ibf;
112}
113
114
115/**
116 * Store unique bucket indices for the specified key in dst.
117 */
118static void
119ibf_get_indices (const struct InvertibleBloomFilter *ibf,
120 struct IBF_Key key,
121 int *dst)
122{
123 uint32_t filled;
124 uint32_t i;
125 uint32_t bucket;
126
127 bucket = GNUNET_CRYPTO_crc32_n (&key, sizeof key);
128 for (i = 0, filled = 0; filled < ibf->hash_num; i++)
129 {
130 unsigned int j;
131 uint64_t x;
132 for (j = 0; j < filled; j++)
133 if (dst[j] == bucket)
134 goto try_next;
135 dst[filled++] = bucket % ibf->size;
136try_next:;
137 x = ((uint64_t) bucket << 32) | i;
138 bucket = GNUNET_CRYPTO_crc32_n (&x, sizeof x);
139 }
140}
141
142
143static void
144ibf_insert_into (struct InvertibleBloomFilter *ibf,
145 struct IBF_Key key,
146 const int *buckets, int side)
147{
148 int i;
149
150 for (i = 0; i < ibf->hash_num; i++)
151 {
152 const int bucket = buckets[i];
153 ibf->count[bucket].count_val += side;
154 ibf->key_sum[bucket].key_val ^= key.key_val;
155 ibf->key_hash_sum[bucket].key_hash_val
156 ^= IBF_KEY_HASH_VAL (key);
157 }
158}
159
160
161/**
162 * Insert a key into an IBF.
163 *
164 * @param ibf the IBF
165 * @param key the element's hash code
166 */
167void
168ibf_insert (struct InvertibleBloomFilter *ibf, struct IBF_Key key)
169{
170 int buckets[ibf->hash_num];
171
172 GNUNET_assert (ibf->hash_num <= ibf->size);
173 ibf_get_indices (ibf, key, buckets);
174 ibf_insert_into (ibf, key, buckets, 1);
175}
176
177
178/**
179 * Remove a key from an IBF.
180 *
181 * @param ibf the IBF
182 * @param key the element's hash code
183 */
184void
185ibf_remove (struct InvertibleBloomFilter *ibf, struct IBF_Key key)
186{
187 int buckets[ibf->hash_num];
188
189 GNUNET_assert (ibf->hash_num <= ibf->size);
190 ibf_get_indices (ibf, key, buckets);
191 ibf_insert_into (ibf, key, buckets, -1);
192}
193
194
195/**
196 * Test is the IBF is empty, i.e. all counts, keys and key hashes are zero.
197 */
198static int
199ibf_is_empty (struct InvertibleBloomFilter *ibf)
200{
201 int i;
202
203 for (i = 0; i < ibf->size; i++)
204 {
205 if (0 != ibf->count[i].count_val)
206 return GNUNET_NO;
207 if (0 != ibf->key_hash_sum[i].key_hash_val)
208 return GNUNET_NO;
209 if (0 != ibf->key_sum[i].key_val)
210 return GNUNET_NO;
211 }
212 return GNUNET_YES;
213}
214
215
216/**
217 * Decode and remove an element from the IBF, if possible.
218 *
219 * @param ibf the invertible bloom filter to decode
220 * @param ret_side sign of the cell's count where the decoded element came from.
221 * A negative sign indicates that the element was recovered
222 * resides in an IBF that was previously subtracted from.
223 * @param ret_id receives the hash code of the decoded element, if successful
224 * @return GNUNET_YES if decoding an element was successful,
225 * GNUNET_NO if the IBF is empty,
226 * GNUNET_SYSERR if the decoding has failed
227 */
228int
229ibf_decode (struct InvertibleBloomFilter *ibf,
230 int *ret_side, struct IBF_Key *ret_id)
231{
232 struct IBF_KeyHash hash;
233 int i;
234 int buckets[ibf->hash_num];
235
236 GNUNET_assert (NULL != ibf);
237
238 for (i = 0; i < ibf->size; i++)
239 {
240 int j;
241 int hit;
242
243 /* we can only decode from pure buckets */
244 if ((1 != ibf->count[i].count_val) && (-1 != ibf->count[i].count_val))
245 continue;
246
247 hash.key_hash_val = IBF_KEY_HASH_VAL (ibf->key_sum[i]);
248
249 /* test if the hash matches the key */
250 if (hash.key_hash_val != ibf->key_hash_sum[i].key_hash_val)
251 continue;
252
253 /* test if key in bucket hits its own location,
254 * if not, the key hash was subject to collision */
255 hit = GNUNET_NO;
256 ibf_get_indices (ibf, ibf->key_sum[i], buckets);
257 for (j = 0; j < ibf->hash_num; j++)
258 if (buckets[j] == i)
259 hit = GNUNET_YES;
260
261 if (GNUNET_NO == hit)
262 continue;
263
264 if (NULL != ret_side)
265 *ret_side = ibf->count[i].count_val;
266 if (NULL != ret_id)
267 *ret_id = ibf->key_sum[i];
268
269 /* insert on the opposite side, effectively removing the element */
270 ibf_insert_into (ibf, ibf->key_sum[i], buckets, -ibf->count[i].count_val);
271
272 return GNUNET_YES;
273 }
274
275 if (GNUNET_YES == ibf_is_empty (ibf))
276 return GNUNET_NO;
277 return GNUNET_SYSERR;
278}
279
280
281/**
282 * Write buckets from an ibf to a buffer.
283 * Exactly (IBF_BUCKET_SIZE*ibf->size) bytes are written to buf.
284 *
285 * @param ibf the ibf to write
286 * @param start with which bucket to start
287 * @param count how many buckets to write
288 * @param buf buffer to write the data to
289 */
290void
291ibf_write_slice (const struct InvertibleBloomFilter *ibf, uint32_t start,
292 uint32_t count, void *buf)
293{
294 struct IBF_Key *key_dst;
295 struct IBF_KeyHash *key_hash_dst;
296 struct IBF_Count *count_dst;
297
298 GNUNET_assert (start + count <= ibf->size);
299
300 /* copy keys */
301 key_dst = (struct IBF_Key *) buf;
302 GNUNET_memcpy (key_dst, ibf->key_sum + start, count * sizeof *key_dst);
303 key_dst += count;
304 /* copy key hashes */
305 key_hash_dst = (struct IBF_KeyHash *) key_dst;
306 GNUNET_memcpy (key_hash_dst, ibf->key_hash_sum + start, count
307 * sizeof *key_hash_dst);
308 key_hash_dst += count;
309 /* copy counts */
310 count_dst = (struct IBF_Count *) key_hash_dst;
311 GNUNET_memcpy (count_dst, ibf->count + start, count * sizeof *count_dst);
312}
313
314
315/**
316 * Read buckets from a buffer into an ibf.
317 *
318 * @param buf pointer to the buffer to read from
319 * @param start which bucket to start at
320 * @param count how many buckets to read
321 * @param ibf the ibf to read from
322 */
323void
324ibf_read_slice (const void *buf, uint32_t start, uint32_t count, struct
325 InvertibleBloomFilter *ibf)
326{
327 struct IBF_Key *key_src;
328 struct IBF_KeyHash *key_hash_src;
329 struct IBF_Count *count_src;
330
331 GNUNET_assert (count > 0);
332 GNUNET_assert (start + count <= ibf->size);
333
334 /* copy keys */
335 key_src = (struct IBF_Key *) buf;
336 GNUNET_memcpy (ibf->key_sum + start, key_src, count * sizeof *key_src);
337 key_src += count;
338 /* copy key hashes */
339 key_hash_src = (struct IBF_KeyHash *) key_src;
340 GNUNET_memcpy (ibf->key_hash_sum + start, key_hash_src, count
341 * sizeof *key_hash_src);
342 key_hash_src += count;
343 /* copy counts */
344 count_src = (struct IBF_Count *) key_hash_src;
345 GNUNET_memcpy (ibf->count + start, count_src, count * sizeof *count_src);
346}
347
348
349/**
350 * Subtract ibf2 from ibf1, storing the result in ibf1.
351 * The two IBF's must have the same parameters size and hash_num.
352 *
353 * @param ibf1 IBF that is subtracted from
354 * @param ibf2 IBF that will be subtracted from ibf1
355 */
356void
357ibf_subtract (struct InvertibleBloomFilter *ibf1, const struct
358 InvertibleBloomFilter *ibf2)
359{
360 int i;
361
362 GNUNET_assert (ibf1->size == ibf2->size);
363 GNUNET_assert (ibf1->hash_num == ibf2->hash_num);
364
365 for (i = 0; i < ibf1->size; i++)
366 {
367 ibf1->count[i].count_val -= ibf2->count[i].count_val;
368 ibf1->key_hash_sum[i].key_hash_val ^= ibf2->key_hash_sum[i].key_hash_val;
369 ibf1->key_sum[i].key_val ^= ibf2->key_sum[i].key_val;
370 }
371}
372
373
374/**
375 * Create a copy of an IBF, the copy has to be destroyed properly.
376 *
377 * @param ibf the IBF to copy
378 */
379struct InvertibleBloomFilter *
380ibf_dup (const struct InvertibleBloomFilter *ibf)
381{
382 struct InvertibleBloomFilter *copy;
383
384 copy = GNUNET_malloc (sizeof *copy);
385 copy->hash_num = ibf->hash_num;
386 copy->size = ibf->size;
387 copy->key_hash_sum = GNUNET_memdup (ibf->key_hash_sum, ibf->size
388 * sizeof(struct IBF_KeyHash));
389 copy->key_sum = GNUNET_memdup (ibf->key_sum, ibf->size * sizeof(struct
390 IBF_Key));
391 copy->count = GNUNET_memdup (ibf->count, ibf->size * sizeof(struct
392 IBF_Count));
393 return copy;
394}
395
396
397/**
398 * Destroy all resources associated with the invertible bloom filter.
399 * No more ibf_*-functions may be called on ibf after calling destroy.
400 *
401 * @param ibf the intertible bloom filter to destroy
402 */
403void
404ibf_destroy (struct InvertibleBloomFilter *ibf)
405{
406 GNUNET_free (ibf->key_sum);
407 GNUNET_free (ibf->key_hash_sum);
408 GNUNET_free (ibf->count);
409 GNUNET_free (ibf);
410}
diff --git a/src/contrib/service/set/ibf.h b/src/contrib/service/set/ibf.h
new file mode 100644
index 000000000..334a797ef
--- /dev/null
+++ b/src/contrib/service/set/ibf.h
@@ -0,0 +1,256 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file set/ibf.h
23 * @brief invertible bloom filter
24 * @author Florian Dold
25 */
26
27#ifndef GNUNET_CONSENSUS_IBF_H
28#define GNUNET_CONSENSUS_IBF_H
29
30#include "platform.h"
31#include "gnunet_util_lib.h"
32
33#ifdef __cplusplus
34extern "C"
35{
36#if 0 /* keep Emacsens' auto-indent happy */
37}
38#endif
39#endif
40
41
42/**
43 * Keys that can be inserted into and removed from an IBF.
44 */
45struct IBF_Key
46{
47 uint64_t key_val;
48};
49
50
51/**
52 * Hash of an IBF key.
53 */
54struct IBF_KeyHash
55{
56 uint32_t key_hash_val;
57};
58
59
60/**
61 * Type of the count field of IBF buckets.
62 */
63struct IBF_Count
64{
65 int8_t count_val;
66};
67
68
69/**
70 * Size of one ibf bucket in bytes
71 */
72#define IBF_BUCKET_SIZE (sizeof(struct IBF_Count) + sizeof(struct IBF_Key) \
73 + sizeof(struct IBF_KeyHash))
74
75
76/**
77 * Invertible bloom filter (IBF).
78 *
79 * An IBF is a counting bloom filter that has the ability to restore
80 * the hashes of its stored elements with high probability.
81 */
82struct InvertibleBloomFilter
83{
84 /**
85 * How many cells does this IBF have?
86 */
87 uint32_t size;
88
89 /**
90 * In how many cells do we hash one element?
91 * Usually 4 or 3.
92 */
93 uint8_t hash_num;
94
95 /**
96 * Xor sums of the elements' keys, used to identify the elements.
97 * Array of 'size' elements.
98 */
99 struct IBF_Key *key_sum;
100
101 /**
102 * Xor sums of the hashes of the keys of inserted elements.
103 * Array of 'size' elements.
104 */
105 struct IBF_KeyHash *key_hash_sum;
106
107 /**
108 * How many times has a bucket been hit?
109 * Can be negative, as a result of IBF subtraction.
110 * Array of 'size' elements.
111 */
112 struct IBF_Count *count;
113};
114
115
116/**
117 * Write buckets from an ibf to a buffer.
118 * Exactly (IBF_BUCKET_SIZE*ibf->size) bytes are written to buf.
119 *
120 * @param ibf the ibf to write
121 * @param start with which bucket to start
122 * @param count how many buckets to write
123 * @param buf buffer to write the data to
124 */
125void
126ibf_write_slice (const struct InvertibleBloomFilter *ibf,
127 uint32_t start,
128 uint32_t count,
129 void *buf);
130
131
132/**
133 * Read buckets from a buffer into an ibf.
134 *
135 * @param buf pointer to the buffer to read from
136 * @param start which bucket to start at
137 * @param count how many buckets to read
138 * @param ibf the ibf to write to
139 */
140void
141ibf_read_slice (const void *buf,
142 uint32_t start,
143 uint32_t count,
144 struct InvertibleBloomFilter *ibf);
145
146
147/**
148 * Create a key from a hashcode.
149 *
150 * @param hash the hashcode
151 * @return a key
152 */
153struct IBF_Key
154ibf_key_from_hashcode (const struct GNUNET_HashCode *hash);
155
156
157/**
158 * Create a hashcode from a key, by replicating the key
159 * until the hascode is filled
160 *
161 * @param key the key
162 * @param dst hashcode to store the result in
163 */
164void
165ibf_hashcode_from_key (struct IBF_Key key, struct GNUNET_HashCode *dst);
166
167
168/**
169 * Create an invertible bloom filter.
170 *
171 * @param size number of IBF buckets
172 * @param hash_num number of buckets one element is hashed in, usually 3 or 4
173 * @return the newly created invertible bloom filter, NULL on error
174 */
175struct InvertibleBloomFilter *
176ibf_create (uint32_t size, uint8_t hash_num);
177
178
179/**
180 * Insert a key into an IBF.
181 *
182 * @param ibf the IBF
183 * @param key the element's hash code
184 */
185void
186ibf_insert (struct InvertibleBloomFilter *ibf, struct IBF_Key key);
187
188
189/**
190 * Remove a key from an IBF.
191 *
192 * @param ibf the IBF
193 * @param key the element's hash code
194 */
195void
196ibf_remove (struct InvertibleBloomFilter *ibf, struct IBF_Key key);
197
198
199/**
200 * Subtract ibf2 from ibf1, storing the result in ibf1.
201 * The two IBF's must have the same parameters size and hash_num.
202 *
203 * @param ibf1 IBF that is subtracted from
204 * @param ibf2 IBF that will be subtracted from ibf1
205 */
206void
207ibf_subtract (struct InvertibleBloomFilter *ibf1,
208 const struct InvertibleBloomFilter *ibf2);
209
210
211/**
212 * Decode and remove an element from the IBF, if possible.
213 *
214 * @param ibf the invertible bloom filter to decode
215 * @param ret_side sign of the cell's count where the decoded element came from.
216 * A negative sign indicates that the element was recovered
217 * resides in an IBF that was previously subtracted from.
218 * @param ret_id receives the hash code of the decoded element, if successful
219 * @return #GNUNET_YES if decoding an element was successful,
220 * #GNUNET_NO if the IBF is empty,
221 * #GNUNET_SYSERR if the decoding has failed
222 */
223int
224ibf_decode (struct InvertibleBloomFilter *ibf,
225 int *ret_side,
226 struct IBF_Key *ret_id);
227
228
229/**
230 * Create a copy of an IBF, the copy has to be destroyed properly.
231 *
232 * @param ibf the IBF to copy
233 */
234struct InvertibleBloomFilter *
235ibf_dup (const struct InvertibleBloomFilter *ibf);
236
237
238/**
239 * Destroy all resources associated with the invertible bloom filter.
240 * No more ibf_*-functions may be called on ibf after calling destroy.
241 *
242 * @param ibf the intertible bloom filter to destroy
243 */
244void
245ibf_destroy (struct InvertibleBloomFilter *ibf);
246
247
248
249#if 0 /* keep Emacsens' auto-indent happy */
250{
251#endif
252#ifdef __cplusplus
253}
254#endif
255
256#endif
diff --git a/src/contrib/service/set/ibf_sim.c b/src/contrib/service/set/ibf_sim.c
new file mode 100644
index 000000000..563ed0fb8
--- /dev/null
+++ b/src/contrib/service/set/ibf_sim.c
@@ -0,0 +1,143 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file set/ibf_sim.c
23 * @brief implementation of simulation for invertible bloom filter
24 * @author Florian Dold
25 *
26 * This code was used for some internal experiments, it is not
27 * build or shipped as part of the GNUnet system.
28 */
29#include "platform.h"
30#include <stdlib.h>
31#include <stdio.h>
32#include <string.h>
33
34#define MAX_IBF_DECODE 16
35
36/* report average over how many rounds? */
37#define ROUNDS 100000
38
39/* enable one of the three below */
40// simple fix
41#define FIX1 0
42// possibly slightly better fix for large IBF_DECODE values
43#define FIX2 1
44
45// SIGCOMM algorithm
46#define STRATA 0
47
48// print each value?
49#define VERBOSE 0
50// avoid assembly? (ASM is about 50% faster)
51#define SLOW 0
52
53int
54main (int argc, char **argv)
55{
56 unsigned int round;
57 unsigned int buckets[31]; // max is 2^31 as 'random' returns only between 0 and 2^31
58 unsigned int i;
59 int j;
60 unsigned int r;
61 unsigned int ret;
62 unsigned long long total;
63 unsigned int want;
64 double predict;
65
66 srandom (time (NULL));
67 total = 0;
68 want = atoi (argv[1]);
69 for (round = 0; round < ROUNDS; round++)
70 {
71 memset (buckets, 0, sizeof(buckets));
72 for (i = 0; i < want; i++)
73 {
74 /* FIXME: might want to use 'better' PRNG to avoid
75 PRNG-induced biases */
76 r = random ();
77 if (0 == r)
78 continue;
79#if SLOW
80 for (j = 0; (j < 31) && (0 == (r & (1 << j))); j++)
81 ;
82#else
83 /* use assembly / gcc */
84 j = __builtin_ffs (r) - 1;
85#endif
86 buckets[j]++;
87 }
88 ret = 0;
89 predict = 0.0;
90 for (j = 31; j >= 0; j--)
91 {
92#if FIX1
93 /* improved algorithm, for 1000 elements with IBF-DECODE 8, I
94 get 990/1000 elements on average over 1 million runs; key
95 idea being to stop short of the 'last' possible IBF as
96 otherwise a "lowball" per-chance would unduely influence the
97 result */if ((j > 0) &&
98 (buckets[j - 1] > MAX_IBF_DECODE))
99 {
100 ret *= (1 << (j + 1));
101 break;
102 }
103#endif
104#if FIX2
105 /* another improvement: don't just always cut off the last one,
106 but rather try to predict based on all previous values where
107 that "last" one is; additional prediction can only really
108 work if MAX_IBF_DECODE is sufficiently high */
109 if ((j > 0) &&
110 ((buckets[j - 1] > MAX_IBF_DECODE) ||
111 (predict > MAX_IBF_DECODE)))
112 {
113 ret *= (1 << (j + 1));
114 break;
115 }
116#endif
117#if STRATA
118 /* original algorithm, for 1000 elements with IBF-DECODE 8,
119 I get 920/1000 elements on average over 1 million runs */
120 if (buckets[j] > MAX_IBF_DECODE)
121 {
122 ret *= (1 << (j + 1));
123 break;
124 }
125#endif
126 ret += buckets[j];
127 predict = (buckets[j] + 2.0 * predict) / 2.0;
128 }
129#if VERBOSE
130 fprintf (stderr, "%u ", ret);
131#endif
132 total += ret;
133 }
134 fprintf (stderr, "\n");
135 fprintf (stdout, "average %llu\n", total / ROUNDS);
136 return 0;
137}
138
139
140/* TODO: should calculate stddev of the results to also be able to
141 say something about the stability of the results, outside of
142 large-scale averages -- gaining 8% precision at the expense of
143 50% additional variance might not be worth it... */
diff --git a/src/contrib/service/set/meson.build b/src/contrib/service/set/meson.build
new file mode 100644
index 000000000..fe4934cb9
--- /dev/null
+++ b/src/contrib/service/set/meson.build
@@ -0,0 +1,52 @@
1libgnunetset_src = ['set_api.c']
2
3gnunetserviceset_src = ['gnunet-service-set.c',
4 'gnunet-service-set_union.c',
5 'gnunet-service-set_intersection.c',
6 'gnunet-service-set_union_strata_estimator.c',
7 'ibf.c']
8
9configure_file(input : 'set.conf.in',
10 output : 'set.conf',
11 configuration : cdata,
12 install: true,
13 install_dir: pkgcfgdir)
14
15
16if get_option('monolith')
17 #foreach p : libgnunetset_src + gnunetserviceset_src
18 # gnunet_src += 'set/' + p
19 #endforeach
20 subdir_done()
21endif
22
23libgnunetset = library('gnunetset',
24 libgnunetset_src,
25 soversion: '0',
26 version: '0.0.0',
27 dependencies: libgnunetutil_dep,
28 include_directories: [incdir, configuration_inc],
29 install: true,
30 install_dir: get_option('libdir'))
31pkg.generate(libgnunetset, url: 'https://www.gnunet.org',
32 description : 'Provides API for accessing the set service')
33libgnunetset_dep = declare_dependency(link_with : libgnunetset)
34shared_module('gnunet_plugin_block_set_test',
35 ['plugin_block_set_test.c'],
36 dependencies: libgnunetutil_dep,
37 include_directories: [incdir, configuration_inc],
38 install:true,
39 install_dir: get_option('libdir')/'gnunet')
40executable ('gnunet-service-set',
41 gnunetserviceset_src,
42 dependencies: [libgnunetset_dep,
43 libgnunetutil_dep,
44 m_dep,
45 libgnunetstatistics_dep,
46 libgnunetcore_dep,
47 libgnunetcadet_dep,
48 libgnunetblock_dep],
49 include_directories: [incdir, configuration_inc],
50 install: true,
51 install_dir: get_option('libdir') / 'gnunet' / 'libexec')
52
diff --git a/src/contrib/service/set/plugin_block_set_test.c b/src/contrib/service/set/plugin_block_set_test.c
new file mode 100644
index 000000000..cb5cef5ad
--- /dev/null
+++ b/src/contrib/service/set/plugin_block_set_test.c
@@ -0,0 +1,167 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2017 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file set/plugin_block_set_test.c
23 * @brief set test block, recognizes elements with non-zero first byte as invalid
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_block_plugin.h"
29#include "gnunet_block_group_lib.h"
30
31
32/**
33 * Function called to validate a query.
34 *
35 * @param cls closure
36 * @param type block type
37 * @param query original query (hash)
38 * @param xquery extrended query data (can be NULL, depending on type)
39 * @param xquery_size number of bytes in @a xquery
40 * @return #GNUNET_OK if the query is fine, #GNUNET_NO if not
41 */
42static enum GNUNET_GenericReturnValue
43block_plugin_set_test_check_query (void *cls,
44 enum GNUNET_BLOCK_Type type,
45 const struct GNUNET_HashCode *query,
46 const void *xquery,
47 size_t xquery_size)
48{
49 return GNUNET_OK;
50}
51
52
53/**
54 * Function called to validate a block for storage.
55 *
56 * @param cls closure
57 * @param type block type
58 * @param block block data to validate
59 * @param block_size number of bytes in @a block
60 * @return #GNUNET_OK if the block is fine, #GNUNET_NO if not
61 */
62static enum GNUNET_GenericReturnValue
63block_plugin_set_test_check_block (void *cls,
64 enum GNUNET_BLOCK_Type type,
65 const void *block,
66 size_t block_size)
67{
68 if ((NULL == block) ||
69 (0 == block_size) ||
70 (0 != ((char *) block)[0]))
71 return GNUNET_SYSERR;
72 return GNUNET_OK;
73}
74
75
76/**
77 * Function called to validate a reply to a request. Note that it is assumed
78 * that the reply has already been matched to the key (and signatures checked)
79 * as it would be done with the GetKeyFunction and the
80 * BlockEvaluationFunction.
81 *
82 * @param cls closure
83 * @param type block type
84 * @param group which block group to use for evaluation
85 * @param query original query (hash)
86 * @param xquery extrended query data (can be NULL, depending on type)
87 * @param xquery_size number of bytes in @a xquery
88 * @param reply_block response to validate
89 * @param reply_block_size number of bytes in @a reply_block
90 * @return characterization of result
91 */
92static enum GNUNET_BLOCK_ReplyEvaluationResult
93block_plugin_set_test_check_reply (void *cls,
94 enum GNUNET_BLOCK_Type type,
95 struct GNUNET_BLOCK_Group *group,
96 const struct GNUNET_HashCode *query,
97 const void *xquery,
98 size_t xquery_size,
99 const void *reply_block,
100 size_t reply_block_size)
101{
102 if ((NULL == reply_block) ||
103 (0 == reply_block_size) ||
104 (0 != ((char *) reply_block)[0]))
105 GNUNET_assert (0);
106 return GNUNET_BLOCK_REPLY_OK_MORE;
107}
108
109
110/**
111 * Function called to obtain the key for a block.
112 *
113 * @param cls closure
114 * @param type block type
115 * @param block block to get the key for
116 * @param block_size number of bytes in block
117 * @param key set to the key (query) for the given block
118 * @return #GNUNET_OK on success, #GNUNET_SYSERR if type not supported
119 * (or if extracting a key from a block of this type does not work)
120 */
121static enum GNUNET_GenericReturnValue
122block_plugin_set_test_get_key (void *cls,
123 enum GNUNET_BLOCK_Type type,
124 const void *block,
125 size_t block_size,
126 struct GNUNET_HashCode *key)
127{
128 return GNUNET_NO;
129}
130
131
132/**
133 * Entry point for the plugin.
134 */
135void *
136libgnunet_plugin_block_set_test_init (void *cls)
137{
138 static const enum GNUNET_BLOCK_Type types[] = {
139 GNUNET_BLOCK_TYPE_SET_TEST,
140 GNUNET_BLOCK_TYPE_ANY /* end of list */
141 };
142 struct GNUNET_BLOCK_PluginFunctions *api;
143
144 api = GNUNET_new (struct GNUNET_BLOCK_PluginFunctions);
145 api->get_key = &block_plugin_set_test_get_key;
146 api->check_query = &block_plugin_set_test_check_query;
147 api->check_block = &block_plugin_set_test_check_block;
148 api->check_reply = &block_plugin_set_test_check_reply;
149 api->types = types;
150 return api;
151}
152
153
154/**
155 * Exit point from the plugin.
156 */
157void *
158libgnunet_plugin_block_set_test_done (void *cls)
159{
160 struct GNUNET_BLOCK_PluginFunctions *api = cls;
161
162 GNUNET_free (api);
163 return NULL;
164}
165
166
167/* end of plugin_block_set_test.c */
diff --git a/src/contrib/service/set/set.conf.in b/src/contrib/service/set/set.conf.in
new file mode 100644
index 000000000..66bcfa169
--- /dev/null
+++ b/src/contrib/service/set/set.conf.in
@@ -0,0 +1,12 @@
1[set]
2START_ON_DEMAND = @START_ON_DEMAND@
3@UNIXONLY@PORT = 2106
4HOSTNAME = localhost
5BINARY = gnunet-service-set
6ACCEPT_FROM = 127.0.0.1;
7ACCEPT_FROM6 = ::1;
8UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-set.sock
9UNIX_MATCH_UID = YES
10UNIX_MATCH_GID = YES
11
12#PREFIX = valgrind
diff --git a/src/contrib/service/set/set.h b/src/contrib/service/set/set.h
new file mode 100644
index 000000000..e9d10ea22
--- /dev/null
+++ b/src/contrib/service/set/set.h
@@ -0,0 +1,400 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file set/set.h
22 * @brief messages used for the set api
23 * @author Florian Dold
24 * @author Christian Grothoff
25 */
26#ifndef SET_H
27#define SET_H
28
29#include "platform.h"
30#include "gnunet_common.h"
31#include "gnunet_set_service.h"
32
33GNUNET_NETWORK_STRUCT_BEGIN
34
35/**
36 * Message sent by the client to the service to ask starting
37 * a new set to perform operations with. Includes the desired
38 * set operation type.
39 */
40struct GNUNET_SET_CreateMessage
41{
42 /**
43 * Type: #GNUNET_MESSAGE_TYPE_SET_CREATE
44 */
45 struct GNUNET_MessageHeader header;
46
47 /**
48 * Operation type, values of `enum GNUNET_SET_OperationType`
49 */
50 uint32_t operation GNUNET_PACKED;
51};
52
53
54/**
55 * Message sent by the client to the service to start listening for
56 * incoming requests to perform a certain type of set operation for a
57 * certain type of application.
58 */
59struct GNUNET_SET_ListenMessage
60{
61 /**
62 * Type: #GNUNET_MESSAGE_TYPE_SET_LISTEN
63 */
64 struct GNUNET_MessageHeader header;
65
66 /**
67 * Operation type, values of `enum GNUNET_SET_OperationType`
68 */
69 uint32_t operation GNUNET_PACKED;
70
71 /**
72 * application id
73 */
74 struct GNUNET_HashCode app_id;
75};
76
77
78/**
79 * Message sent by a listening client to the service to accept
80 * performing the operation with the other peer.
81 */
82struct GNUNET_SET_AcceptMessage
83{
84 /**
85 * Type: #GNUNET_MESSAGE_TYPE_SET_ACCEPT
86 */
87 struct GNUNET_MessageHeader header;
88
89 /**
90 * ID of the incoming request we want to accept.
91 */
92 uint32_t accept_reject_id GNUNET_PACKED;
93
94 /**
95 * Request ID to identify responses.
96 */
97 uint32_t request_id GNUNET_PACKED;
98
99 /**
100 * How should results be sent to us?
101 * See `enum GNUNET_SET_ResultMode`.
102 */
103 uint32_t result_mode GNUNET_PACKED;
104
105 /**
106 * Always use delta operation instead of sending full sets,
107 * even it it's less efficient.
108 */
109 uint8_t force_delta;
110
111 /**
112 * Always send full sets, even if delta operations would
113 * be more efficient.
114 */
115 uint8_t force_full;
116
117 /**
118 * #GNUNET_YES to fail operations where Byzantine faults
119 * are suspected
120 */
121 uint8_t byzantine;
122
123 /**
124 * Lower bound for the set size, used only when
125 * byzantine mode is enabled.
126 */
127 uint8_t byzantine_lower_bound;
128};
129
130
131/**
132 * Message sent by a listening client to the service to reject
133 * performing the operation with the other peer.
134 */
135struct GNUNET_SET_RejectMessage
136{
137 /**
138 * Type: #GNUNET_MESSAGE_TYPE_SET_REJECT
139 */
140 struct GNUNET_MessageHeader header;
141
142 /**
143 * ID of the incoming request we want to reject.
144 */
145 uint32_t accept_reject_id GNUNET_PACKED;
146};
147
148
149/**
150 * A request for an operation with another client.
151 */
152struct GNUNET_SET_RequestMessage
153{
154 /**
155 * Type: #GNUNET_MESSAGE_TYPE_SET_REQUEST.
156 */
157 struct GNUNET_MessageHeader header;
158
159 /**
160 * ID of the to identify the request when accepting or
161 * rejecting it.
162 */
163 uint32_t accept_id GNUNET_PACKED;
164
165 /**
166 * Identity of the requesting peer.
167 */
168 struct GNUNET_PeerIdentity peer_id;
169
170 /* rest: context message, that is, application-specific
171 message to convince listener to pick up */
172};
173
174
175/**
176 * Message sent by client to service to initiate a set operation as a
177 * client (not as listener). A set (which determines the operation
178 * type) must already exist in association with this client.
179 */
180struct GNUNET_SET_EvaluateMessage
181{
182 /**
183 * Type: #GNUNET_MESSAGE_TYPE_SET_EVALUATE
184 */
185 struct GNUNET_MessageHeader header;
186
187 /**
188 * How should results be sent to us?
189 * See `enum GNUNET_SET_ResultMode`.
190 */
191 uint32_t result_mode GNUNET_PACKED;
192
193 /**
194 * Peer to evaluate the operation with
195 */
196 struct GNUNET_PeerIdentity target_peer;
197
198 /**
199 * Application id
200 */
201 struct GNUNET_HashCode app_id;
202
203 /**
204 * Id of our set to evaluate, chosen implicitly by the client when it
205 * calls #GNUNET_SET_commit().
206 */
207 uint32_t request_id GNUNET_PACKED;
208
209 /**
210 * Always use delta operation instead of sending full sets,
211 * even it it's less efficient.
212 */
213 uint8_t force_delta;
214
215 /**
216 * Always send full sets, even if delta operations would
217 * be more efficient.
218 */
219 uint8_t force_full;
220
221 /**
222 * #GNUNET_YES to fail operations where Byzantine faults
223 * are suspected
224 */
225 uint8_t byzantine;
226
227 /**
228 * Lower bound for the set size, used only when
229 * byzantine mode is enabled.
230 */
231 uint8_t byzantine_lower_bound;
232
233 /* rest: context message, that is, application-specific
234 message to convince listener to pick up */
235};
236
237
238/**
239 * Message sent by the service to the client to indicate an
240 * element that is removed (set intersection) or added
241 * (set union) or part of the final result, depending on
242 * options specified for the operation.
243 */
244struct GNUNET_SET_ResultMessage
245{
246 /**
247 * Type: #GNUNET_MESSAGE_TYPE_SET_RESULT
248 */
249 struct GNUNET_MessageHeader header;
250
251 /**
252 * Current set size.
253 */
254 uint64_t current_size;
255
256 /**
257 * id the result belongs to
258 */
259 uint32_t request_id GNUNET_PACKED;
260
261 /**
262 * Was the evaluation successful? Contains
263 * an `enum GNUNET_SET_Status` in NBO.
264 */
265 uint16_t result_status GNUNET_PACKED;
266
267 /**
268 * Type of the element attached to the message, if any.
269 */
270 uint16_t element_type GNUNET_PACKED;
271
272 /* rest: the actual element */
273};
274
275
276/**
277 * Message sent by client to the service to add or remove
278 * an element to/from the set.
279 */
280struct GNUNET_SET_ElementMessage
281{
282 /**
283 * Type: #GNUNET_MESSAGE_TYPE_SET_ADD or
284 * #GNUNET_MESSAGE_TYPE_SET_REMOVE
285 */
286 struct GNUNET_MessageHeader header;
287
288 /**
289 * Type of the element to add or remove.
290 */
291 uint16_t element_type GNUNET_PACKED;
292
293 /**
294 * For alignment, always zero.
295 */
296 uint16_t reserved GNUNET_PACKED;
297
298 /* rest: the actual element */
299};
300
301
302/**
303 * Sent to the service by the client
304 * in order to cancel a set operation.
305 */
306struct GNUNET_SET_CancelMessage
307{
308 /**
309 * Type: #GNUNET_MESSAGE_TYPE_SET_CANCEL
310 */
311 struct GNUNET_MessageHeader header;
312
313 /**
314 * ID of the request we want to cancel.
315 */
316 uint32_t request_id GNUNET_PACKED;
317};
318
319
320/**
321 * Set element transmitted by service to client in response to a set
322 * iteration request.
323 */
324struct GNUNET_SET_IterResponseMessage
325{
326 /**
327 * Type: #GNUNET_MESSAGE_TYPE_SET_ITER_ELEMENT
328 */
329 struct GNUNET_MessageHeader header;
330
331 /**
332 * To which set iteration does this response belong to? First
333 * iteration (per client) has counter zero. Wraps around.
334 */
335 uint16_t iteration_id GNUNET_PACKED;
336
337 /**
338 * Type of the element attached to the message,
339 * if any.
340 */
341 uint16_t element_type GNUNET_PACKED;
342
343 /* rest: element */
344};
345
346
347/**
348 * Client acknowledges receiving element in iteration.
349 */
350struct GNUNET_SET_IterAckMessage
351{
352 /**
353 * Type: #GNUNET_MESSAGE_TYPE_SET_ITER_ACK
354 */
355 struct GNUNET_MessageHeader header;
356
357 /**
358 * Non-zero if the service should continue sending elements.
359 */
360 uint32_t send_more;
361};
362
363
364/**
365 * Server responds to a lazy copy request.
366 */
367struct GNUNET_SET_CopyLazyResponseMessage
368{
369 /**
370 * Type: #GNUNET_MESSAGE_TYPE_SET_COPY_LAZY_RESPONSE
371 */
372 struct GNUNET_MessageHeader header;
373
374 /**
375 * Temporary name for the copied set.
376 */
377 uint32_t cookie;
378};
379
380
381/**
382 * Client connects to a lazily copied set.
383 */
384struct GNUNET_SET_CopyLazyConnectMessage
385{
386 /**
387 * Type: #GNUNET_MESSAGE_TYPE_SET_COPY_LAZY_CONNECT
388 */
389 struct GNUNET_MessageHeader header;
390
391 /**
392 * Temporary name for the copied set.
393 */
394 uint32_t cookie;
395};
396
397
398GNUNET_NETWORK_STRUCT_END
399
400#endif
diff --git a/src/contrib/service/set/set_api.c b/src/contrib/service/set/set_api.c
new file mode 100644
index 000000000..4f73ff06c
--- /dev/null
+++ b/src/contrib/service/set/set_api.c
@@ -0,0 +1,1199 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file set/set_api.c
22 * @brief api for the set service
23 * @author Florian Dold
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_protocols.h"
29#include "gnunet_set_service.h"
30#include "set.h"
31
32
33#define LOG(kind, ...) GNUNET_log_from (kind, "set-api", __VA_ARGS__)
34
35struct SetCopyRequest
36{
37 struct SetCopyRequest *next;
38
39 struct SetCopyRequest *prev;
40
41 void *cls;
42
43 GNUNET_SET_CopyReadyCallback cb;
44};
45
46/**
47 * Opaque handle to a set.
48 */
49struct GNUNET_SET_Handle
50{
51 /**
52 * Message queue for @e client.
53 */
54 struct GNUNET_MQ_Handle *mq;
55
56 /**
57 * Linked list of operations on the set.
58 */
59 struct GNUNET_SET_OperationHandle *ops_head;
60
61 /**
62 * Linked list of operations on the set.
63 */
64 struct GNUNET_SET_OperationHandle *ops_tail;
65
66 /**
67 * Callback for the current iteration over the set,
68 * NULL if no iterator is active.
69 */
70 GNUNET_SET_ElementIterator iterator;
71
72 /**
73 * Closure for @e iterator
74 */
75 void *iterator_cls;
76
77 /**
78 * Should the set be destroyed once all operations are gone?
79 * #GNUNET_SYSERR if #GNUNET_SET_destroy() must raise this flag,
80 * #GNUNET_YES if #GNUNET_SET_destroy() did raise this flag.
81 */
82 int destroy_requested;
83
84 /**
85 * Has the set become invalid (e.g. service died)?
86 */
87 int invalid;
88
89 /**
90 * Both client and service count the number of iterators
91 * created so far to match replies with iterators.
92 */
93 uint16_t iteration_id;
94
95 /**
96 * Configuration, needed when creating (lazy) copies.
97 */
98 const struct GNUNET_CONFIGURATION_Handle *cfg;
99
100 /**
101 * Doubly linked list of copy requests.
102 */
103 struct SetCopyRequest *copy_req_head;
104
105 /**
106 * Doubly linked list of copy requests.
107 */
108 struct SetCopyRequest *copy_req_tail;
109};
110
111
112/**
113 * Handle for a set operation request from another peer.
114 */
115struct GNUNET_SET_Request
116{
117 /**
118 * Id of the request, used to identify the request when
119 * accepting/rejecting it.
120 */
121 uint32_t accept_id;
122
123 /**
124 * Has the request been accepted already?
125 * #GNUNET_YES/#GNUNET_NO
126 */
127 int accepted;
128};
129
130
131/**
132 * Handle to an operation. Only known to the service after committing
133 * the handle with a set.
134 */
135struct GNUNET_SET_OperationHandle
136{
137 /**
138 * Function to be called when we have a result,
139 * or an error.
140 */
141 GNUNET_SET_ResultIterator result_cb;
142
143 /**
144 * Closure for @e result_cb.
145 */
146 void *result_cls;
147
148 /**
149 * Local set used for the operation,
150 * NULL if no set has been provided by conclude yet.
151 */
152 struct GNUNET_SET_Handle *set;
153
154 /**
155 * Message sent to the server on calling conclude,
156 * NULL if conclude has been called.
157 */
158 struct GNUNET_MQ_Envelope *conclude_mqm;
159
160 /**
161 * Address of the request if in the conclude message,
162 * used to patch the request id into the message when the set is known.
163 */
164 uint32_t *request_id_addr;
165
166 /**
167 * Handles are kept in a linked list.
168 */
169 struct GNUNET_SET_OperationHandle *prev;
170
171 /**
172 * Handles are kept in a linked list.
173 */
174 struct GNUNET_SET_OperationHandle *next;
175
176 /**
177 * Request ID to identify the operation within the set.
178 */
179 uint32_t request_id;
180};
181
182
183/**
184 * Opaque handle to a listen operation.
185 */
186struct GNUNET_SET_ListenHandle
187{
188 /**
189 * Message queue for the client.
190 */
191 struct GNUNET_MQ_Handle*mq;
192
193 /**
194 * Configuration handle for the listener, stored
195 * here to be able to reconnect transparently on
196 * connection failure.
197 */
198 const struct GNUNET_CONFIGURATION_Handle *cfg;
199
200 /**
201 * Function to call on a new incoming request,
202 * or on error.
203 */
204 GNUNET_SET_ListenCallback listen_cb;
205
206 /**
207 * Closure for @e listen_cb.
208 */
209 void *listen_cls;
210
211 /**
212 * Application ID we listen for.
213 */
214 struct GNUNET_HashCode app_id;
215
216 /**
217 * Time to wait until we try to reconnect on failure.
218 */
219 struct GNUNET_TIME_Relative reconnect_backoff;
220
221 /**
222 * Task for reconnecting when the listener fails.
223 */
224 struct GNUNET_SCHEDULER_Task *reconnect_task;
225
226 /**
227 * Operation we listen for.
228 */
229 enum GNUNET_SET_OperationType operation;
230};
231
232
233/* mutual recursion with handle_copy_lazy */
234static struct GNUNET_SET_Handle *
235create_internal (const struct GNUNET_CONFIGURATION_Handle *cfg,
236 enum GNUNET_SET_OperationType op,
237 const uint32_t *cookie);
238
239
240/**
241 * Handle element for iteration over the set. Notifies the
242 * iterator and sends an acknowledgement to the service.
243 *
244 * @param cls the `struct GNUNET_SET_Handle *`
245 * @param msg the message
246 */
247static void
248handle_copy_lazy (void *cls,
249 const struct GNUNET_SET_CopyLazyResponseMessage *msg)
250{
251 struct GNUNET_SET_Handle *set = cls;
252 struct SetCopyRequest *req;
253 struct GNUNET_SET_Handle *new_set;
254
255 req = set->copy_req_head;
256 if (NULL == req)
257 {
258 /* Service sent us unsolicited lazy copy response */
259 GNUNET_break (0);
260 return;
261 }
262
263 LOG (GNUNET_ERROR_TYPE_DEBUG,
264 "Handling response to lazy copy\n");
265 GNUNET_CONTAINER_DLL_remove (set->copy_req_head,
266 set->copy_req_tail,
267 req);
268 // We pass none as operation here, since it doesn't matter when
269 // cloning.
270 new_set = create_internal (set->cfg,
271 GNUNET_SET_OPERATION_NONE,
272 &msg->cookie);
273 req->cb (req->cls, new_set);
274 GNUNET_free (req);
275}
276
277
278/**
279 * Check that the given @a msg is well-formed.
280 *
281 * @param cls closure
282 * @param msg message to check
283 * @return #GNUNET_OK if message is well-formed
284 */
285static int
286check_iter_element (void *cls,
287 const struct GNUNET_SET_IterResponseMessage *msg)
288{
289 /* minimum size was already checked, everything else is OK! */
290 return GNUNET_OK;
291}
292
293
294/**
295 * Handle element for iteration over the set. Notifies the
296 * iterator and sends an acknowledgement to the service.
297 *
298 * @param cls the `struct GNUNET_SET_Handle *`
299 * @param msg the message
300 */
301static void
302handle_iter_element (void *cls,
303 const struct GNUNET_SET_IterResponseMessage *msg)
304{
305 struct GNUNET_SET_Handle *set = cls;
306 GNUNET_SET_ElementIterator iter = set->iterator;
307 struct GNUNET_SET_Element element;
308 struct GNUNET_SET_IterAckMessage *ack_msg;
309 struct GNUNET_MQ_Envelope *ev;
310 uint16_t msize;
311
312 LOG (GNUNET_ERROR_TYPE_DEBUG,
313 "Received element in set iteration\n");
314 msize = ntohs (msg->header.size);
315 if (set->iteration_id != ntohs (msg->iteration_id))
316 {
317 /* element from a previous iteration, skip! */
318 iter = NULL;
319 }
320 if (NULL != iter)
321 {
322 element.size = msize - sizeof(struct GNUNET_SET_IterResponseMessage);
323 element.element_type = ntohs (msg->element_type);
324 element.data = &msg[1];
325 iter (set->iterator_cls,
326 &element);
327 }
328 ev = GNUNET_MQ_msg (ack_msg,
329 GNUNET_MESSAGE_TYPE_SET_ITER_ACK);
330 ack_msg->send_more = htonl ((NULL != iter));
331 GNUNET_MQ_send (set->mq, ev);
332}
333
334
335/**
336 * Handle message signalling conclusion of iteration over the set.
337 * Notifies the iterator that we are done.
338 *
339 * @param cls the set
340 * @param mh the message
341 */
342static void
343handle_iter_done (void *cls,
344 const struct GNUNET_MessageHeader *mh)
345{
346 struct GNUNET_SET_Handle *set = cls;
347 GNUNET_SET_ElementIterator iter = set->iterator;
348
349 if (NULL == iter)
350 {
351 /* FIXME: if this is true, could cancel+start a fresh one
352 cause elements to go to the wrong iteration? */
353 LOG (GNUNET_ERROR_TYPE_INFO,
354 "Service completed set iteration that was already cancelled\n");
355 return;
356 }
357 LOG (GNUNET_ERROR_TYPE_DEBUG,
358 "Set iteration completed\n");
359 set->destroy_requested = GNUNET_SYSERR;
360 set->iterator = NULL;
361 set->iteration_id++;
362 iter (set->iterator_cls,
363 NULL);
364 if (GNUNET_SYSERR == set->destroy_requested)
365 set->destroy_requested = GNUNET_NO;
366 if (GNUNET_YES == set->destroy_requested)
367 GNUNET_SET_destroy (set);
368}
369
370
371/**
372 * Check that the given @a msg is well-formed.
373 *
374 * @param cls closure
375 * @param msg message to check
376 * @return #GNUNET_OK if message is well-formed
377 */
378static int
379check_result (void *cls,
380 const struct GNUNET_SET_ResultMessage *msg)
381{
382 /* minimum size was already checked, everything else is OK! */
383 return GNUNET_OK;
384}
385
386
387/**
388 * Handle result message for a set operation.
389 *
390 * @param cls the set
391 * @param mh the message
392 */
393static void
394handle_result (void *cls,
395 const struct GNUNET_SET_ResultMessage *msg)
396{
397 struct GNUNET_SET_Handle *set = cls;
398 struct GNUNET_SET_OperationHandle *oh;
399 struct GNUNET_SET_Element e;
400 enum GNUNET_SET_Status result_status;
401 int destroy_set;
402
403 GNUNET_assert (NULL != set->mq);
404 result_status = (enum GNUNET_SET_Status) ntohs (msg->result_status);
405 LOG (GNUNET_ERROR_TYPE_DEBUG,
406 "Got result message with status %d\n",
407 result_status);
408
409 oh = GNUNET_MQ_assoc_get (set->mq,
410 ntohl (msg->request_id));
411 if (NULL == oh)
412 {
413 /* 'oh' can be NULL if we canceled the operation, but the service
414 did not get the cancel message yet. */
415 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
416 "Ignoring result from canceled operation\n");
417 return;
418 }
419
420 switch (result_status)
421 {
422 case GNUNET_SET_STATUS_OK:
423 case GNUNET_SET_STATUS_ADD_LOCAL:
424 case GNUNET_SET_STATUS_ADD_REMOTE:
425 goto do_element;
426
427 case GNUNET_SET_STATUS_FAILURE:
428 case GNUNET_SET_STATUS_DONE:
429 goto do_final;
430
431 case GNUNET_SET_STATUS_HALF_DONE:
432 /* not used anymore */
433 GNUNET_assert (0);
434 }
435
436do_final:
437 LOG (GNUNET_ERROR_TYPE_DEBUG,
438 "Treating result as final status\n");
439 GNUNET_MQ_assoc_remove (set->mq,
440 ntohl (msg->request_id));
441 GNUNET_CONTAINER_DLL_remove (set->ops_head,
442 set->ops_tail,
443 oh);
444 /* Need to do this calculation _before_ the result callback,
445 as IF the application still has a valid set handle, it
446 may trigger destruction of the set during the callback. */
447 destroy_set = (GNUNET_YES == set->destroy_requested) &&
448 (NULL == set->ops_head);
449 if (NULL != oh->result_cb)
450 {
451 oh->result_cb (oh->result_cls,
452 NULL,
453 GNUNET_ntohll (msg->current_size),
454 result_status);
455 }
456 else
457 {
458 LOG (GNUNET_ERROR_TYPE_DEBUG,
459 "No callback for final status\n");
460 }
461 if (destroy_set)
462 GNUNET_SET_destroy (set);
463 GNUNET_free (oh);
464 return;
465
466do_element:
467 LOG (GNUNET_ERROR_TYPE_DEBUG,
468 "Treating result as element\n");
469 e.data = &msg[1];
470 e.size = ntohs (msg->header.size) - sizeof(struct GNUNET_SET_ResultMessage);
471 e.element_type = ntohs (msg->element_type);
472 if (NULL != oh->result_cb)
473 oh->result_cb (oh->result_cls,
474 &e,
475 GNUNET_ntohll (msg->current_size),
476 result_status);
477}
478
479
480/**
481 * Destroy the given set operation.
482 *
483 * @param oh set operation to destroy
484 */
485static void
486set_operation_destroy (struct GNUNET_SET_OperationHandle *oh)
487{
488 struct GNUNET_SET_Handle *set = oh->set;
489 struct GNUNET_SET_OperationHandle *h_assoc;
490
491 if (NULL != oh->conclude_mqm)
492 GNUNET_MQ_discard (oh->conclude_mqm);
493 /* is the operation already committed? */
494 if (NULL != set)
495 {
496 GNUNET_CONTAINER_DLL_remove (set->ops_head,
497 set->ops_tail,
498 oh);
499 h_assoc = GNUNET_MQ_assoc_remove (set->mq,
500 oh->request_id);
501 GNUNET_assert ((NULL == h_assoc) ||
502 (h_assoc == oh));
503 }
504 GNUNET_free (oh);
505}
506
507
508/**
509 * Cancel the given set operation. We need to send an explicit cancel
510 * message, as all operations one one set communicate using one
511 * handle.
512 *
513 * @param oh set operation to cancel
514 */
515void
516GNUNET_SET_operation_cancel (struct GNUNET_SET_OperationHandle *oh)
517{
518 struct GNUNET_SET_Handle *set = oh->set;
519 struct GNUNET_SET_CancelMessage *m;
520 struct GNUNET_MQ_Envelope *mqm;
521
522 LOG (GNUNET_ERROR_TYPE_DEBUG,
523 "Cancelling SET operation\n");
524 if (NULL != set)
525 {
526 mqm = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_SET_CANCEL);
527 m->request_id = htonl (oh->request_id);
528 GNUNET_MQ_send (set->mq, mqm);
529 }
530 set_operation_destroy (oh);
531 if ((NULL != set) &&
532 (GNUNET_YES == set->destroy_requested) &&
533 (NULL == set->ops_head))
534 {
535 LOG (GNUNET_ERROR_TYPE_DEBUG,
536 "Destroying set after operation cancel\n");
537 GNUNET_SET_destroy (set);
538 }
539}
540
541
542/**
543 * We encountered an error communicating with the set service while
544 * performing a set operation. Report to the application.
545 *
546 * @param cls the `struct GNUNET_SET_Handle`
547 * @param error error code
548 */
549static void
550handle_client_set_error (void *cls,
551 enum GNUNET_MQ_Error error)
552{
553 struct GNUNET_SET_Handle *set = cls;
554 GNUNET_SET_ElementIterator iter = set->iterator;
555
556 LOG (GNUNET_ERROR_TYPE_ERROR,
557 "Handling client set error %d\n",
558 error);
559 while (NULL != set->ops_head)
560 {
561 if ((NULL != set->ops_head->result_cb) &&
562 (GNUNET_NO == set->destroy_requested))
563 set->ops_head->result_cb (set->ops_head->result_cls,
564 NULL,
565 0,
566 GNUNET_SET_STATUS_FAILURE);
567 set_operation_destroy (set->ops_head);
568 }
569 set->iterator = NULL;
570 set->iteration_id++;
571 set->invalid = GNUNET_YES;
572 if (NULL != iter)
573 iter (set->iterator_cls,
574 NULL);
575}
576
577
578/**
579 * FIXME.
580 */
581static struct GNUNET_SET_Handle *
582create_internal (const struct GNUNET_CONFIGURATION_Handle *cfg,
583 enum GNUNET_SET_OperationType op,
584 const uint32_t *cookie)
585{
586 struct GNUNET_SET_Handle *set = GNUNET_new (struct GNUNET_SET_Handle);
587 struct GNUNET_MQ_MessageHandler mq_handlers[] = {
588 GNUNET_MQ_hd_var_size (result,
589 GNUNET_MESSAGE_TYPE_SET_RESULT,
590 struct GNUNET_SET_ResultMessage,
591 set),
592 GNUNET_MQ_hd_var_size (iter_element,
593 GNUNET_MESSAGE_TYPE_SET_ITER_ELEMENT,
594 struct GNUNET_SET_IterResponseMessage,
595 set),
596 GNUNET_MQ_hd_fixed_size (iter_done,
597 GNUNET_MESSAGE_TYPE_SET_ITER_DONE,
598 struct GNUNET_MessageHeader,
599 set),
600 GNUNET_MQ_hd_fixed_size (copy_lazy,
601 GNUNET_MESSAGE_TYPE_SET_COPY_LAZY_RESPONSE,
602 struct GNUNET_SET_CopyLazyResponseMessage,
603 set),
604 GNUNET_MQ_handler_end ()
605 };
606 struct GNUNET_MQ_Envelope *mqm;
607 struct GNUNET_SET_CreateMessage *create_msg;
608 struct GNUNET_SET_CopyLazyConnectMessage *copy_msg;
609
610 set->cfg = cfg;
611 set->mq = GNUNET_CLIENT_connect (cfg,
612 "set",
613 mq_handlers,
614 &handle_client_set_error,
615 set);
616 if (NULL == set->mq)
617 {
618 GNUNET_free (set);
619 return NULL;
620 }
621 if (NULL == cookie)
622 {
623 LOG (GNUNET_ERROR_TYPE_DEBUG,
624 "Creating new set (operation %u)\n",
625 op);
626 mqm = GNUNET_MQ_msg (create_msg,
627 GNUNET_MESSAGE_TYPE_SET_CREATE);
628 create_msg->operation = htonl (op);
629 }
630 else
631 {
632 LOG (GNUNET_ERROR_TYPE_DEBUG,
633 "Creating new set (lazy copy)\n");
634 mqm = GNUNET_MQ_msg (copy_msg,
635 GNUNET_MESSAGE_TYPE_SET_COPY_LAZY_CONNECT);
636 copy_msg->cookie = *cookie;
637 }
638 GNUNET_MQ_send (set->mq,
639 mqm);
640 return set;
641}
642
643
644/**
645 * Create an empty set, supporting the specified operation.
646 *
647 * @param cfg configuration to use for connecting to the
648 * set service
649 * @param op operation supported by the set
650 * Note that the operation has to be specified
651 * beforehand, as certain set operations need to maintain
652 * data structures specific to the operation
653 * @return a handle to the set
654 */
655struct GNUNET_SET_Handle *
656GNUNET_SET_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
657 enum GNUNET_SET_OperationType op)
658{
659 struct GNUNET_SET_Handle *set;
660
661 set = create_internal (cfg,
662 op,
663 NULL);
664 LOG (GNUNET_ERROR_TYPE_DEBUG,
665 "Creating set %p for operation %d\n",
666 set,
667 op);
668 return set;
669}
670
671
672int
673GNUNET_SET_add_element (struct GNUNET_SET_Handle *set,
674 const struct GNUNET_SET_Element *element,
675 GNUNET_SET_Continuation cont,
676 void *cont_cls)
677{
678 struct GNUNET_MQ_Envelope *mqm;
679 struct GNUNET_SET_ElementMessage *msg;
680
681 LOG (GNUNET_ERROR_TYPE_DEBUG,
682 "adding element of type %u to set %p\n",
683 (unsigned int) element->element_type,
684 set);
685 GNUNET_assert (NULL != set);
686 if (GNUNET_YES == set->invalid)
687 {
688 if (NULL != cont)
689 cont (cont_cls);
690 return GNUNET_SYSERR;
691 }
692 mqm = GNUNET_MQ_msg_extra (msg,
693 element->size,
694 GNUNET_MESSAGE_TYPE_SET_ADD);
695 msg->element_type = htons (element->element_type);
696 GNUNET_memcpy (&msg[1],
697 element->data,
698 element->size);
699 GNUNET_MQ_notify_sent (mqm,
700 cont, cont_cls);
701 GNUNET_MQ_send (set->mq, mqm);
702 return GNUNET_OK;
703}
704
705
706int
707GNUNET_SET_remove_element (struct GNUNET_SET_Handle *set,
708 const struct GNUNET_SET_Element *element,
709 GNUNET_SET_Continuation cont,
710 void *cont_cls)
711{
712 struct GNUNET_MQ_Envelope *mqm;
713 struct GNUNET_SET_ElementMessage *msg;
714
715 LOG (GNUNET_ERROR_TYPE_DEBUG,
716 "Removing element from set %p\n",
717 set);
718 if (GNUNET_YES == set->invalid)
719 {
720 if (NULL != cont)
721 cont (cont_cls);
722 return GNUNET_SYSERR;
723 }
724 mqm = GNUNET_MQ_msg_extra (msg,
725 element->size,
726 GNUNET_MESSAGE_TYPE_SET_REMOVE);
727 msg->element_type = htons (element->element_type);
728 GNUNET_memcpy (&msg[1],
729 element->data,
730 element->size);
731 GNUNET_MQ_notify_sent (mqm,
732 cont, cont_cls);
733 GNUNET_MQ_send (set->mq, mqm);
734 return GNUNET_OK;
735}
736
737
738/**
739 * Destroy the set handle if no operations are left, mark the set
740 * for destruction otherwise.
741 *
742 * @param set set handle to destroy
743 */
744void
745GNUNET_SET_destroy (struct GNUNET_SET_Handle *set)
746{
747 /* destroying set while iterator is active is currently
748 not supported; we should expand the API to allow
749 clients to explicitly cancel the iteration! */
750 GNUNET_assert (NULL != set);
751 if ((NULL != set->ops_head) ||
752 (NULL != set->iterator) ||
753 (GNUNET_SYSERR == set->destroy_requested))
754 {
755 LOG (GNUNET_ERROR_TYPE_DEBUG,
756 "Set operations are pending, delaying set destruction\n");
757 set->destroy_requested = GNUNET_YES;
758 return;
759 }
760 LOG (GNUNET_ERROR_TYPE_DEBUG,
761 "Really destroying set\n");
762 if (NULL != set->mq)
763 {
764 GNUNET_MQ_destroy (set->mq);
765 set->mq = NULL;
766 }
767 GNUNET_free (set);
768}
769
770
771struct GNUNET_SET_OperationHandle *
772GNUNET_SET_prepare (const struct GNUNET_PeerIdentity *other_peer,
773 const struct GNUNET_HashCode *app_id,
774 const struct GNUNET_MessageHeader *context_msg,
775 enum GNUNET_SET_ResultMode result_mode,
776 struct GNUNET_SET_Option options[],
777 GNUNET_SET_ResultIterator result_cb,
778 void *result_cls)
779{
780 struct GNUNET_MQ_Envelope *mqm;
781 struct GNUNET_SET_OperationHandle *oh;
782 struct GNUNET_SET_EvaluateMessage *msg;
783 struct GNUNET_SET_Option *opt;
784
785 LOG (GNUNET_ERROR_TYPE_DEBUG,
786 "Client prepares set operation (%d)\n",
787 result_mode);
788 oh = GNUNET_new (struct GNUNET_SET_OperationHandle);
789 oh->result_cb = result_cb;
790 oh->result_cls = result_cls;
791 mqm = GNUNET_MQ_msg_nested_mh (msg,
792 GNUNET_MESSAGE_TYPE_SET_EVALUATE,
793 context_msg);
794 msg->app_id = *app_id;
795 msg->result_mode = htonl (result_mode);
796 msg->target_peer = *other_peer;
797 for (opt = options; opt->type != 0; opt++)
798 {
799 switch (opt->type)
800 {
801 case GNUNET_SET_OPTION_BYZANTINE:
802 msg->byzantine = GNUNET_YES;
803 msg->byzantine_lower_bound = opt->v.num;
804 break;
805
806 case GNUNET_SET_OPTION_FORCE_FULL:
807 msg->force_full = GNUNET_YES;
808 break;
809
810 case GNUNET_SET_OPTION_FORCE_DELTA:
811 msg->force_delta = GNUNET_YES;
812 break;
813
814 default:
815 LOG (GNUNET_ERROR_TYPE_ERROR,
816 "Option with type %d not recognized\n", (int) opt->type);
817 }
818 }
819 oh->conclude_mqm = mqm;
820 oh->request_id_addr = &msg->request_id;
821
822 return oh;
823}
824
825
826/**
827 * Connect to the set service in order to listen for requests.
828 *
829 * @param cls the `struct GNUNET_SET_ListenHandle *` to connect
830 */
831static void
832listen_connect (void *cls);
833
834
835/**
836 * Check validity of request message for a listen operation
837 *
838 * @param cls the listen handle
839 * @param msg the message
840 * @return #GNUNET_OK if the message is well-formed
841 */
842static int
843check_request (void *cls,
844 const struct GNUNET_SET_RequestMessage *msg)
845{
846 const struct GNUNET_MessageHeader *context_msg;
847
848 if (ntohs (msg->header.size) == sizeof(*msg))
849 return GNUNET_OK; /* no context message is OK */
850 context_msg = GNUNET_MQ_extract_nested_mh (msg);
851 if (NULL == context_msg)
852 {
853 /* malformed context message is NOT ok */
854 GNUNET_break_op (0);
855 return GNUNET_SYSERR;
856 }
857 return GNUNET_OK;
858}
859
860
861/**
862 * Handle request message for a listen operation
863 *
864 * @param cls the listen handle
865 * @param msg the message
866 */
867static void
868handle_request (void *cls,
869 const struct GNUNET_SET_RequestMessage *msg)
870{
871 struct GNUNET_SET_ListenHandle *lh = cls;
872 struct GNUNET_SET_Request req;
873 const struct GNUNET_MessageHeader *context_msg;
874 struct GNUNET_MQ_Envelope *mqm;
875 struct GNUNET_SET_RejectMessage *rmsg;
876
877 LOG (GNUNET_ERROR_TYPE_DEBUG,
878 "Processing incoming operation request with id %u\n",
879 ntohl (msg->accept_id));
880 /* we got another valid request => reset the backoff */
881 lh->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
882 req.accept_id = ntohl (msg->accept_id);
883 req.accepted = GNUNET_NO;
884 context_msg = GNUNET_MQ_extract_nested_mh (msg);
885 /* calling #GNUNET_SET_accept() in the listen cb will set req->accepted */
886 lh->listen_cb (lh->listen_cls,
887 &msg->peer_id,
888 context_msg,
889 &req);
890 if (GNUNET_YES == req.accepted)
891 return; /* the accept-case is handled in #GNUNET_SET_accept() */
892 LOG (GNUNET_ERROR_TYPE_DEBUG,
893 "Rejected request %u\n",
894 ntohl (msg->accept_id));
895 mqm = GNUNET_MQ_msg (rmsg,
896 GNUNET_MESSAGE_TYPE_SET_REJECT);
897 rmsg->accept_reject_id = msg->accept_id;
898 GNUNET_MQ_send (lh->mq, mqm);
899}
900
901
902/**
903 * Our connection with the set service encountered an error,
904 * re-initialize with exponential back-off.
905 *
906 * @param cls the `struct GNUNET_SET_ListenHandle *`
907 * @param error reason for the disconnect
908 */
909static void
910handle_client_listener_error (void *cls,
911 enum GNUNET_MQ_Error error)
912{
913 struct GNUNET_SET_ListenHandle *lh = cls;
914
915 LOG (GNUNET_ERROR_TYPE_DEBUG,
916 "Listener broke down (%d), re-connecting\n",
917 (int) error);
918 GNUNET_MQ_destroy (lh->mq);
919 lh->mq = NULL;
920 lh->reconnect_task = GNUNET_SCHEDULER_add_delayed (lh->reconnect_backoff,
921 &listen_connect,
922 lh);
923 lh->reconnect_backoff = GNUNET_TIME_STD_BACKOFF (lh->reconnect_backoff);
924}
925
926
927/**
928 * Connect to the set service in order to listen for requests.
929 *
930 * @param cls the `struct GNUNET_SET_ListenHandle *` to connect
931 */
932static void
933listen_connect (void *cls)
934{
935 struct GNUNET_SET_ListenHandle *lh = cls;
936 struct GNUNET_MQ_MessageHandler mq_handlers[] = {
937 GNUNET_MQ_hd_var_size (request,
938 GNUNET_MESSAGE_TYPE_SET_REQUEST,
939 struct GNUNET_SET_RequestMessage,
940 lh),
941 GNUNET_MQ_handler_end ()
942 };
943 struct GNUNET_MQ_Envelope *mqm;
944 struct GNUNET_SET_ListenMessage *msg;
945
946 lh->reconnect_task = NULL;
947 GNUNET_assert (NULL == lh->mq);
948 lh->mq = GNUNET_CLIENT_connect (lh->cfg,
949 "set",
950 mq_handlers,
951 &handle_client_listener_error,
952 lh);
953 if (NULL == lh->mq)
954 return;
955 mqm = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_LISTEN);
956 msg->operation = htonl (lh->operation);
957 msg->app_id = lh->app_id;
958 GNUNET_MQ_send (lh->mq,
959 mqm);
960}
961
962
963/**
964 * Wait for set operation requests for the given application id
965 *
966 * @param cfg configuration to use for connecting to
967 * the set service, needs to be valid for the lifetime of the listen handle
968 * @param operation operation we want to listen for
969 * @param app_id id of the application that handles set operation requests
970 * @param listen_cb called for each incoming request matching the operation
971 * and application id
972 * @param listen_cls handle for @a listen_cb
973 * @return a handle that can be used to cancel the listen operation
974 */
975struct GNUNET_SET_ListenHandle *
976GNUNET_SET_listen (const struct GNUNET_CONFIGURATION_Handle *cfg,
977 enum GNUNET_SET_OperationType operation,
978 const struct GNUNET_HashCode *app_id,
979 GNUNET_SET_ListenCallback listen_cb,
980 void *listen_cls)
981{
982 struct GNUNET_SET_ListenHandle *lh;
983
984 LOG (GNUNET_ERROR_TYPE_DEBUG,
985 "Starting listener for app %s\n",
986 GNUNET_h2s (app_id));
987 lh = GNUNET_new (struct GNUNET_SET_ListenHandle);
988 lh->listen_cb = listen_cb;
989 lh->listen_cls = listen_cls;
990 lh->cfg = cfg;
991 lh->operation = operation;
992 lh->app_id = *app_id;
993 lh->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
994 listen_connect (lh);
995 if (NULL == lh->mq)
996 {
997 GNUNET_free (lh);
998 return NULL;
999 }
1000 return lh;
1001}
1002
1003
1004/**
1005 * Cancel the given listen operation.
1006 *
1007 * @param lh handle for the listen operation
1008 */
1009void
1010GNUNET_SET_listen_cancel (struct GNUNET_SET_ListenHandle *lh)
1011{
1012 LOG (GNUNET_ERROR_TYPE_DEBUG,
1013 "Canceling listener %s\n",
1014 GNUNET_h2s (&lh->app_id));
1015 if (NULL != lh->mq)
1016 {
1017 GNUNET_MQ_destroy (lh->mq);
1018 lh->mq = NULL;
1019 }
1020 if (NULL != lh->reconnect_task)
1021 {
1022 GNUNET_SCHEDULER_cancel (lh->reconnect_task);
1023 lh->reconnect_task = NULL;
1024 }
1025 GNUNET_free (lh);
1026}
1027
1028
1029struct GNUNET_SET_OperationHandle *
1030GNUNET_SET_accept (struct GNUNET_SET_Request *request,
1031 enum GNUNET_SET_ResultMode result_mode,
1032 struct GNUNET_SET_Option options[],
1033 GNUNET_SET_ResultIterator result_cb,
1034 void *result_cls)
1035{
1036 struct GNUNET_MQ_Envelope *mqm;
1037 struct GNUNET_SET_OperationHandle *oh;
1038 struct GNUNET_SET_AcceptMessage *msg;
1039
1040 GNUNET_assert (GNUNET_NO == request->accepted);
1041 LOG (GNUNET_ERROR_TYPE_DEBUG,
1042 "Client accepts set operation (%d) with id %u\n",
1043 result_mode,
1044 request->accept_id);
1045 request->accepted = GNUNET_YES;
1046 mqm = GNUNET_MQ_msg (msg,
1047 GNUNET_MESSAGE_TYPE_SET_ACCEPT);
1048 msg->accept_reject_id = htonl (request->accept_id);
1049 msg->result_mode = htonl (result_mode);
1050 oh = GNUNET_new (struct GNUNET_SET_OperationHandle);
1051 oh->result_cb = result_cb;
1052 oh->result_cls = result_cls;
1053 oh->conclude_mqm = mqm;
1054 oh->request_id_addr = &msg->request_id;
1055 return oh;
1056}
1057
1058
1059/**
1060 * Commit a set to be used with a set operation.
1061 * This function is called once we have fully constructed
1062 * the set that we want to use for the operation. At this
1063 * time, the P2P protocol can then begin to exchange the
1064 * set information and call the result callback with the
1065 * result information.
1066 *
1067 * @param oh handle to the set operation
1068 * @param set the set to use for the operation
1069 * @return #GNUNET_OK on success, #GNUNET_SYSERR if the
1070 * set is invalid (e.g. the set service crashed)
1071 */
1072int
1073GNUNET_SET_commit (struct GNUNET_SET_OperationHandle *oh,
1074 struct GNUNET_SET_Handle *set)
1075{
1076 if (NULL != oh->set)
1077 {
1078 /* Some other set was already committed for this
1079 * operation, there is a logic bug in the client of this API */
1080 GNUNET_break (0);
1081 return GNUNET_OK;
1082 }
1083 GNUNET_assert (NULL != set);
1084 if (GNUNET_YES == set->invalid)
1085 return GNUNET_SYSERR;
1086 LOG (GNUNET_ERROR_TYPE_DEBUG,
1087 "Client commits to SET\n");
1088 GNUNET_assert (NULL != oh->conclude_mqm);
1089 oh->set = set;
1090 GNUNET_CONTAINER_DLL_insert (set->ops_head,
1091 set->ops_tail,
1092 oh);
1093 oh->request_id = GNUNET_MQ_assoc_add (set->mq,
1094 oh);
1095 *oh->request_id_addr = htonl (oh->request_id);
1096 GNUNET_MQ_send (set->mq,
1097 oh->conclude_mqm);
1098 oh->conclude_mqm = NULL;
1099 oh->request_id_addr = NULL;
1100 return GNUNET_OK;
1101}
1102
1103
1104/**
1105 * Iterate over all elements in the given set. Note that this
1106 * operation involves transferring every element of the set from the
1107 * service to the client, and is thus costly.
1108 *
1109 * @param set the set to iterate over
1110 * @param iter the iterator to call for each element
1111 * @param iter_cls closure for @a iter
1112 * @return #GNUNET_YES if the iteration started successfully,
1113 * #GNUNET_NO if another iteration is active
1114 * #GNUNET_SYSERR if the set is invalid (e.g. the server crashed, disconnected)
1115 */
1116int
1117GNUNET_SET_iterate (struct GNUNET_SET_Handle *set,
1118 GNUNET_SET_ElementIterator iter,
1119 void *iter_cls)
1120{
1121 struct GNUNET_MQ_Envelope *ev;
1122
1123 GNUNET_assert (NULL != iter);
1124 if (GNUNET_YES == set->invalid)
1125 return GNUNET_SYSERR;
1126 if (NULL != set->iterator)
1127 return GNUNET_NO;
1128 LOG (GNUNET_ERROR_TYPE_DEBUG,
1129 "Iterating over set\n");
1130 set->iterator = iter;
1131 set->iterator_cls = iter_cls;
1132 ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_ITER_REQUEST);
1133 GNUNET_MQ_send (set->mq, ev);
1134 return GNUNET_YES;
1135}
1136
1137
1138void
1139GNUNET_SET_copy_lazy (struct GNUNET_SET_Handle *set,
1140 GNUNET_SET_CopyReadyCallback cb,
1141 void *cls)
1142{
1143 struct GNUNET_MQ_Envelope *ev;
1144 struct SetCopyRequest *req;
1145
1146 LOG (GNUNET_ERROR_TYPE_DEBUG,
1147 "Creating lazy copy of set\n");
1148 ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_COPY_LAZY_PREPARE);
1149 GNUNET_MQ_send (set->mq, ev);
1150
1151 req = GNUNET_new (struct SetCopyRequest);
1152 req->cb = cb;
1153 req->cls = cls;
1154 GNUNET_CONTAINER_DLL_insert (set->copy_req_head,
1155 set->copy_req_tail,
1156 req);
1157}
1158
1159
1160/**
1161 * Create a copy of an element. The copy
1162 * must be GNUNET_free-d by the caller.
1163 *
1164 * @param element the element to copy
1165 * @return the copied element
1166 */
1167struct GNUNET_SET_Element *
1168GNUNET_SET_element_dup (const struct GNUNET_SET_Element *element)
1169{
1170 struct GNUNET_SET_Element *copy;
1171
1172 copy = GNUNET_malloc (element->size + sizeof(struct GNUNET_SET_Element));
1173 copy->size = element->size;
1174 copy->element_type = element->element_type;
1175 copy->data = &copy[1];
1176 GNUNET_memcpy (&copy[1],
1177 element->data,
1178 copy->size);
1179 return copy;
1180}
1181
1182
1183void
1184GNUNET_SET_element_hash (const struct GNUNET_SET_Element *element,
1185 struct GNUNET_HashCode *ret_hash)
1186{
1187 struct GNUNET_HashContext *ctx = GNUNET_CRYPTO_hash_context_start ();
1188
1189 /* It's not guaranteed that the element data is always after the element header,
1190 so we need to hash the chunks separately. */
1191 GNUNET_CRYPTO_hash_context_read (ctx, &element->size, sizeof(uint16_t));
1192 GNUNET_CRYPTO_hash_context_read (ctx, &element->element_type,
1193 sizeof(uint16_t));
1194 GNUNET_CRYPTO_hash_context_read (ctx, element->data, element->size);
1195 GNUNET_CRYPTO_hash_context_finish (ctx, ret_hash);
1196}
1197
1198
1199/* end of set_api.c */
diff --git a/src/contrib/service/set/test_set.conf b/src/contrib/service/set/test_set.conf
new file mode 100644
index 000000000..f9b4547e9
--- /dev/null
+++ b/src/contrib/service/set/test_set.conf
@@ -0,0 +1,33 @@
1@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf
2
3[PATHS]
4GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-set/
5
6[set]
7START_ON_DEMAND = YES
8#PREFIX = valgrind --leak-check=full
9#PREFIX = gdbserver :1234
10OPTIONS = -L INFO
11
12[transport]
13PLUGINS = unix
14OPTIONS = -LERROR
15
16[nat]
17RETURN_LOCAL_ADDRESSES = YES
18DISABLEV6 = YES
19USE_LOCALADDR = YES
20
21[peerinfo]
22NO_IO = YES
23
24[nat]
25# Use addresses from the local network interfaces (including loopback, but also others)
26USE_LOCALADDR = YES
27
28# Disable IPv6 support
29DISABLEV6 = NO
30
31# Do we use addresses from localhost address ranges? (::1, 127.0.0.0/8)
32RETURN_LOCAL_ADDRESSES = YES
33
diff --git a/src/contrib/service/set/test_set_api.c b/src/contrib/service/set/test_set_api.c
new file mode 100644
index 000000000..d1afdd354
--- /dev/null
+++ b/src/contrib/service/set/test_set_api.c
@@ -0,0 +1,403 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file set/test_set_api.c
23 * @brief testcase for set_api.c
24 * @author Florian Dold
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_testing_lib.h"
29#include "gnunet_set_service.h"
30
31
32static struct GNUNET_PeerIdentity local_id;
33
34static struct GNUNET_HashCode app_id;
35
36static struct GNUNET_SET_Handle *set1;
37
38static struct GNUNET_SET_Handle *set2;
39
40static struct GNUNET_SET_ListenHandle *listen_handle;
41
42static struct GNUNET_SET_OperationHandle *oh1;
43
44static struct GNUNET_SET_OperationHandle *oh2;
45
46static const struct GNUNET_CONFIGURATION_Handle *config;
47
48static unsigned int iter_count;
49
50static int ret;
51
52static struct GNUNET_SCHEDULER_Task *tt;
53
54
55static void
56result_cb_set1 (void *cls,
57 const struct GNUNET_SET_Element *element,
58 uint64_t size,
59 enum GNUNET_SET_Status status)
60{
61 switch (status)
62 {
63 case GNUNET_SET_STATUS_OK:
64 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "set 1: got element\n");
65 break;
66
67 case GNUNET_SET_STATUS_FAILURE:
68 GNUNET_break (0);
69 oh1 = NULL;
70 fprintf (stderr, "set 1: received failure status!\n");
71 ret = 1;
72 if (NULL != tt)
73 {
74 GNUNET_SCHEDULER_cancel (tt);
75 tt = NULL;
76 }
77 GNUNET_SCHEDULER_shutdown ();
78 break;
79
80 case GNUNET_SET_STATUS_DONE:
81 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "set 1: done\n");
82 oh1 = NULL;
83 if (NULL != set1)
84 {
85 GNUNET_SET_destroy (set1);
86 set1 = NULL;
87 }
88 if (NULL == set2)
89 {
90 GNUNET_SCHEDULER_cancel (tt);
91 tt = NULL;
92 GNUNET_SCHEDULER_shutdown ();
93 }
94 break;
95
96 default:
97 GNUNET_assert (0);
98 }
99}
100
101
102static void
103result_cb_set2 (void *cls,
104 const struct GNUNET_SET_Element *element,
105 uint64_t size,
106 enum GNUNET_SET_Status status)
107{
108 switch (status)
109 {
110 case GNUNET_SET_STATUS_OK:
111 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "set 2: got element\n");
112 break;
113
114 case GNUNET_SET_STATUS_FAILURE:
115 GNUNET_break (0);
116 oh2 = NULL;
117 fprintf (stderr, "set 2: received failure status\n");
118 GNUNET_SCHEDULER_shutdown ();
119 ret = 1;
120 break;
121
122 case GNUNET_SET_STATUS_DONE:
123 oh2 = NULL;
124 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "set 2: done\n");
125 GNUNET_SET_destroy (set2);
126 set2 = NULL;
127 if (NULL == set1)
128 {
129 GNUNET_SCHEDULER_cancel (tt);
130 tt = NULL;
131 GNUNET_SCHEDULER_shutdown ();
132 }
133 break;
134
135 default:
136 GNUNET_assert (0);
137 }
138}
139
140
141static void
142listen_cb (void *cls,
143 const struct GNUNET_PeerIdentity *other_peer,
144 const struct GNUNET_MessageHeader *context_msg,
145 struct GNUNET_SET_Request *request)
146{
147 GNUNET_assert (NULL != context_msg);
148 GNUNET_assert (ntohs (context_msg->type) == GNUNET_MESSAGE_TYPE_DUMMY);
149 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "listen cb called\n");
150 oh2 = GNUNET_SET_accept (request,
151 GNUNET_SET_RESULT_ADDED,
152 (struct GNUNET_SET_Option[]){ 0 },
153 &result_cb_set2,
154 NULL);
155 GNUNET_SET_commit (oh2, set2);
156}
157
158
159/**
160 * Start the set operation.
161 *
162 * @param cls closure, unused
163 */
164static void
165start (void *cls)
166{
167 struct GNUNET_MessageHeader context_msg;
168
169 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting reconciliation\n");
170 context_msg.size = htons (sizeof context_msg);
171 context_msg.type = htons (GNUNET_MESSAGE_TYPE_DUMMY);
172 listen_handle = GNUNET_SET_listen (config,
173 GNUNET_SET_OPERATION_UNION,
174 &app_id,
175 &listen_cb,
176 NULL);
177 oh1 = GNUNET_SET_prepare (&local_id,
178 &app_id,
179 &context_msg,
180 GNUNET_SET_RESULT_ADDED,
181 (struct GNUNET_SET_Option[]){ 0 },
182 &result_cb_set1,
183 NULL);
184 GNUNET_SET_commit (oh1, set1);
185}
186
187
188/**
189 * Initialize the second set, continue
190 *
191 * @param cls closure, unused
192 */
193static void
194init_set2 (void *cls)
195{
196 struct GNUNET_SET_Element element;
197
198 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "initializing set 2\n");
199
200 element.element_type = 0;
201 element.data = "hello";
202 element.size = strlen (element.data);
203 GNUNET_SET_add_element (set2, &element, NULL, NULL);
204 element.data = "quux";
205 element.size = strlen (element.data);
206 GNUNET_SET_add_element (set2, &element, NULL, NULL);
207 element.data = "baz";
208 element.size = strlen (element.data);
209 GNUNET_SET_add_element (set2, &element, &start, NULL);
210}
211
212
213/**
214 * Initialize the first set, continue.
215 */
216static void
217init_set1 (void)
218{
219 struct GNUNET_SET_Element element;
220
221 element.element_type = 0;
222 element.data = "hello";
223 element.size = strlen (element.data);
224 GNUNET_SET_add_element (set1, &element, NULL, NULL);
225 element.data = "bar";
226 element.size = strlen (element.data);
227 GNUNET_SET_add_element (set1, &element, &init_set2, NULL);
228 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "initialized set 1\n");
229}
230
231
232static int
233iter_cb (void *cls, const struct GNUNET_SET_Element *element)
234{
235 struct GNUNET_SET_Handle *set = cls;
236
237 if (NULL == element)
238 {
239 GNUNET_assert (3 == iter_count);
240 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
241 "Iteration finished, destroying set %p\n",
242 set);
243 GNUNET_SET_destroy (set);
244 return GNUNET_YES;
245 }
246 iter_count++;
247 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "iter: got element %u\n", iter_count);
248 return GNUNET_YES;
249}
250
251
252static void
253test_iter ()
254{
255 struct GNUNET_SET_Element element;
256 struct GNUNET_SET_Handle *iter_set;
257
258 iter_set = GNUNET_SET_create (config, GNUNET_SET_OPERATION_UNION);
259 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
260 "Testing iteration over 3 elements on set %p\n",
261 iter_set);
262 element.element_type = 0;
263
264 element.data = "hello";
265 element.size = strlen (element.data);
266 GNUNET_SET_add_element (iter_set, &element, NULL, NULL);
267 element.data = "bar";
268 element.size = strlen (element.data);
269 GNUNET_SET_add_element (iter_set, &element, NULL, NULL);
270 element.data = "quux";
271 element.size = strlen (element.data);
272 GNUNET_SET_add_element (iter_set, &element, NULL, NULL);
273 GNUNET_SET_iterate (iter_set, &iter_cb, iter_set);
274}
275
276
277/**
278 * Function run on timeout.
279 *
280 * @param cls closure
281 */
282static void
283timeout_fail (void *cls)
284{
285 tt = NULL;
286 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Testcase failed with timeout\n");
287 GNUNET_SCHEDULER_shutdown ();
288 ret = 1;
289}
290
291
292/**
293 * Function run on shutdown.
294 *
295 * @param cls closure
296 */
297static void
298do_shutdown (void *cls)
299{
300 if (NULL != tt)
301 {
302 GNUNET_SCHEDULER_cancel (tt);
303 tt = NULL;
304 }
305 if (NULL != oh1)
306 {
307 GNUNET_SET_operation_cancel (oh1);
308 oh1 = NULL;
309 }
310 if (NULL != oh2)
311 {
312 GNUNET_SET_operation_cancel (oh2);
313 oh2 = NULL;
314 }
315 if (NULL != set1)
316 {
317 GNUNET_SET_destroy (set1);
318 set1 = NULL;
319 }
320 if (NULL != set2)
321 {
322 GNUNET_SET_destroy (set2);
323 set2 = NULL;
324 }
325 if (NULL != listen_handle)
326 {
327 GNUNET_SET_listen_cancel (listen_handle);
328 listen_handle = NULL;
329 }
330}
331
332
333/**
334 * Signature of the 'main' function for a (single-peer) testcase that
335 * is run using 'GNUNET_TESTING_peer_run'.
336 *
337 * @param cls closure
338 * @param cfg configuration of the peer that was started
339 * @param peer identity of the peer that was created
340 */
341static void
342run (void *cls,
343 const struct GNUNET_CONFIGURATION_Handle *cfg,
344 struct GNUNET_TESTING_Peer *peer)
345{
346 struct GNUNET_SET_OperationHandle *my_oh;
347
348 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Running preparatory tests\n");
349 tt = GNUNET_SCHEDULER_add_delayed (
350 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5),
351 &timeout_fail,
352 NULL);
353 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
354
355 config = cfg;
356 GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_get_peer_identity (cfg, &local_id));
357 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
358 "my id (from CRYPTO): %s\n",
359 GNUNET_i2s (&local_id));
360 GNUNET_TESTING_peer_get_identity (peer, &local_id);
361 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
362 "my id (from TESTING): %s\n",
363 GNUNET_i2s (&local_id));
364 test_iter ();
365
366 set1 = GNUNET_SET_create (cfg, GNUNET_SET_OPERATION_UNION);
367 set2 = GNUNET_SET_create (cfg, GNUNET_SET_OPERATION_UNION);
368 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
369 "Created sets %p and %p for union operation\n",
370 set1,
371 set2);
372 GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, &app_id);
373
374 /* test if canceling an uncommitted request works! */
375 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
376 "Launching and instantly stopping set operation\n");
377 my_oh = GNUNET_SET_prepare (&local_id,
378 &app_id,
379 NULL,
380 GNUNET_SET_RESULT_ADDED,
381 (struct GNUNET_SET_Option[]){ 0 },
382 NULL,
383 NULL);
384 GNUNET_SET_operation_cancel (my_oh);
385
386 /* test the real set reconciliation */
387 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Running real set-reconciliation\n");
388 init_set1 ();
389}
390
391
392int
393main (int argc, char **argv)
394{
395 GNUNET_log_setup ("test_set_api", "WARNING", NULL);
396 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Launching peer\n");
397 if (0 !=
398 GNUNET_TESTING_peer_run ("test_set_api", "test_set.conf", &run, NULL))
399 {
400 return 1;
401 }
402 return ret;
403}
diff --git a/src/contrib/service/set/test_set_intersection_result_full.c b/src/contrib/service/set/test_set_intersection_result_full.c
new file mode 100644
index 000000000..42dedb846
--- /dev/null
+++ b/src/contrib/service/set/test_set_intersection_result_full.c
@@ -0,0 +1,393 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file set/test_set_intersection_result_full.c
23 * @brief testcase for full result mode of the intersection set operation
24 * @author Christian Fuchs
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_testing_lib.h"
30#include "gnunet_set_service.h"
31
32
33static int ret;
34
35static struct GNUNET_PeerIdentity local_id;
36
37static struct GNUNET_HashCode app_id;
38
39static struct GNUNET_SET_Handle *set1;
40
41static struct GNUNET_SET_Handle *set2;
42
43static struct GNUNET_SET_ListenHandle *listen_handle;
44
45static const struct GNUNET_CONFIGURATION_Handle *config;
46
47static int iter_count;
48
49static struct GNUNET_SCHEDULER_Task *tt;
50
51static struct GNUNET_SET_OperationHandle *oh1;
52
53static struct GNUNET_SET_OperationHandle *oh2;
54
55
56static void
57result_cb_set1 (void *cls,
58 const struct GNUNET_SET_Element *element,
59 uint64_t current_size,
60 enum GNUNET_SET_Status status)
61{
62 static int count;
63
64 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
65 "Processing result set 1 (%d)\n",
66 status);
67 switch (status)
68 {
69 case GNUNET_SET_STATUS_OK:
70 count++;
71 break;
72
73 case GNUNET_SET_STATUS_FAILURE:
74 oh1 = NULL;
75 ret = 1;
76 break;
77
78 case GNUNET_SET_STATUS_DONE:
79 oh1 = NULL;
80 GNUNET_assert (1 == count);
81 GNUNET_SET_destroy (set1);
82 set1 = NULL;
83 if (NULL == set2)
84 GNUNET_SCHEDULER_shutdown ();
85 break;
86
87 default:
88 GNUNET_assert (0);
89 }
90}
91
92
93static void
94result_cb_set2 (void *cls,
95 const struct GNUNET_SET_Element *element,
96 uint64_t current_size,
97 enum GNUNET_SET_Status status)
98{
99 static int count;
100
101 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
102 "Processing result set 2 (%d)\n",
103 status);
104 switch (status)
105 {
106 case GNUNET_SET_STATUS_OK:
107 count++;
108 break;
109
110 case GNUNET_SET_STATUS_FAILURE:
111 oh2 = NULL;
112 ret = 1;
113 break;
114
115 case GNUNET_SET_STATUS_DONE:
116 oh2 = NULL;
117 GNUNET_assert (1 == count);
118 GNUNET_SET_destroy (set2);
119 set2 = NULL;
120 if (NULL == set1)
121 GNUNET_SCHEDULER_shutdown ();
122 break;
123
124 default:
125 GNUNET_assert (0);
126 }
127}
128
129
130static void
131listen_cb (void *cls,
132 const struct GNUNET_PeerIdentity *other_peer,
133 const struct GNUNET_MessageHeader *context_msg,
134 struct GNUNET_SET_Request *request)
135{
136 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
137 "starting intersection by accepting and committing\n");
138 GNUNET_assert (NULL != context_msg);
139 GNUNET_assert (ntohs (context_msg->type) == GNUNET_MESSAGE_TYPE_DUMMY);
140 oh2 = GNUNET_SET_accept (request,
141 GNUNET_SET_RESULT_FULL,
142 (struct GNUNET_SET_Option[]) { 0 },
143 &result_cb_set2,
144 NULL);
145 GNUNET_SET_commit (oh2,
146 set2);
147}
148
149
150/**
151 * Start the set operation.
152 *
153 * @param cls closure, unused
154 */
155static void
156start (void *cls)
157{
158 struct GNUNET_MessageHeader context_msg;
159
160 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
161 "starting listener\n");
162 context_msg.size = htons (sizeof context_msg);
163 context_msg.type = htons (GNUNET_MESSAGE_TYPE_DUMMY);
164 listen_handle = GNUNET_SET_listen (config,
165 GNUNET_SET_OPERATION_INTERSECTION,
166 &app_id,
167 &listen_cb,
168 NULL);
169 oh1 = GNUNET_SET_prepare (&local_id,
170 &app_id,
171 &context_msg,
172 GNUNET_SET_RESULT_FULL,
173 (struct GNUNET_SET_Option[]) { 0 },
174 &result_cb_set1,
175 NULL);
176 GNUNET_SET_commit (oh1,
177 set1);
178}
179
180
181/**
182 * Initialize the second set, continue
183 *
184 * @param cls closure, unused
185 */
186static void
187init_set2 (void *cls)
188{
189 struct GNUNET_SET_Element element;
190
191 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
192 "initializing set 2\n");
193 element.element_type = 0;
194 element.data = "hello";
195 element.size = strlen (element.data);
196 GNUNET_SET_add_element (set2,
197 &element,
198 NULL,
199 NULL);
200 element.data = "quux";
201 element.size = strlen (element.data);
202 GNUNET_SET_add_element (set2,
203 &element,
204 NULL,
205 NULL);
206 element.data = "baz";
207 element.size = strlen (element.data);
208 GNUNET_SET_add_element (set2,
209 &element,
210 &start,
211 NULL);
212}
213
214
215/**
216 * Initialize the first set, continue.
217 */
218static void
219init_set1 (void)
220{
221 struct GNUNET_SET_Element element;
222
223 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
224 "initializing set 1\n");
225 element.element_type = 0;
226 element.data = "hello";
227 element.size = strlen (element.data);
228 GNUNET_SET_add_element (set1,
229 &element,
230 NULL,
231 NULL);
232 element.data = "bar";
233 element.size = strlen (element.data);
234 GNUNET_SET_add_element (set1,
235 &element,
236 &init_set2,
237 NULL);
238}
239
240
241static int
242iter_cb (void *cls,
243 const struct GNUNET_SET_Element *element)
244{
245 if (NULL == element)
246 {
247 GNUNET_assert (iter_count == 3);
248 GNUNET_SET_destroy (cls);
249 return GNUNET_YES;
250 }
251 iter_count++;
252 return GNUNET_YES;
253}
254
255
256static void
257test_iter ()
258{
259 struct GNUNET_SET_Element element;
260 struct GNUNET_SET_Handle *iter_set;
261
262 iter_set = GNUNET_SET_create (config,
263 GNUNET_SET_OPERATION_INTERSECTION);
264 element.element_type = 0;
265 element.data = "hello";
266 element.size = strlen (element.data);
267 GNUNET_SET_add_element (iter_set,
268 &element,
269 NULL,
270 NULL);
271 element.data = "bar";
272 element.size = strlen (element.data);
273 GNUNET_SET_add_element (iter_set,
274 &element,
275 NULL,
276 NULL);
277 element.data = "quux";
278 element.size = strlen (element.data);
279 GNUNET_SET_add_element (iter_set,
280 &element,
281 NULL,
282 NULL);
283 GNUNET_SET_iterate (iter_set,
284 &iter_cb,
285 iter_set);
286}
287
288
289/**
290 * Function run on shutdown.
291 *
292 * @param cls closure
293 */
294static void
295do_shutdown (void *cls)
296{
297 if (NULL != tt)
298 {
299 GNUNET_SCHEDULER_cancel (tt);
300 tt = NULL;
301 }
302 if (NULL != oh1)
303 {
304 GNUNET_SET_operation_cancel (oh1);
305 oh1 = NULL;
306 }
307 if (NULL != oh2)
308 {
309 GNUNET_SET_operation_cancel (oh2);
310 oh2 = NULL;
311 }
312 if (NULL != set1)
313 {
314 GNUNET_SET_destroy (set1);
315 set1 = NULL;
316 }
317 if (NULL != set2)
318 {
319 GNUNET_SET_destroy (set2);
320 set2 = NULL;
321 }
322 if (NULL != listen_handle)
323 {
324 GNUNET_SET_listen_cancel (listen_handle);
325 listen_handle = NULL;
326 }
327}
328
329
330/**
331 * Function run on timeout.
332 *
333 * @param cls closure
334 */
335static void
336timeout_fail (void *cls)
337{
338 tt = NULL;
339 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
340 "Testcase failed with timeout\n");
341 GNUNET_SCHEDULER_shutdown ();
342 ret = 1;
343}
344
345
346/**
347 * Signature of the 'main' function for a (single-peer) testcase that
348 * is run using 'GNUNET_TESTING_peer_run'.
349 *
350 * @param cls closure
351 * @param cfg configuration of the peer that was started
352 * @param peer identity of the peer that was created
353 */
354static void
355run (void *cls,
356 const struct GNUNET_CONFIGURATION_Handle *cfg,
357 struct GNUNET_TESTING_Peer *peer)
358{
359 config = cfg;
360 GNUNET_TESTING_peer_get_identity (peer,
361 &local_id);
362 if (0)
363 test_iter ();
364
365 tt = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (
366 GNUNET_TIME_UNIT_SECONDS, 5),
367 &timeout_fail,
368 NULL);
369 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
370 NULL);
371
372 set1 = GNUNET_SET_create (cfg,
373 GNUNET_SET_OPERATION_INTERSECTION);
374 set2 = GNUNET_SET_create (cfg,
375 GNUNET_SET_OPERATION_INTERSECTION);
376 GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK,
377 &app_id);
378
379 /* test the real set reconciliation */
380 init_set1 ();
381}
382
383
384int
385main (int argc,
386 char **argv)
387{
388 if (0 != GNUNET_TESTING_peer_run ("test_set_intersection_result_full",
389 "test_set.conf",
390 &run, NULL))
391 return 1;
392 return ret;
393}
diff --git a/src/contrib/service/set/test_set_union_copy.c b/src/contrib/service/set/test_set_union_copy.c
new file mode 100644
index 000000000..908527017
--- /dev/null
+++ b/src/contrib/service/set/test_set_union_copy.c
@@ -0,0 +1,311 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2015, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file set/test_set_union_copy.c
23 * @brief testcase for lazy copying of union sets
24 * @author Florian Dold
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_common.h"
29#include "gnunet_testing_lib.h"
30#include "gnunet_set_service.h"
31
32
33/**
34 * Value to return from #main().
35 */
36static int ret;
37
38static struct GNUNET_PeerIdentity local_id;
39
40static struct GNUNET_SET_Handle *set1;
41
42static struct GNUNET_SET_Handle *set2;
43
44static const struct GNUNET_CONFIGURATION_Handle *config;
45
46static struct GNUNET_SCHEDULER_Task *tt;
47
48
49static void
50add_element_str (struct GNUNET_SET_Handle *set,
51 char *str)
52{
53 struct GNUNET_SET_Element element;
54
55 element.element_type = 0;
56 element.data = str;
57 element.size = strlen (str);
58 GNUNET_SET_add_element (set,
59 &element,
60 NULL,
61 NULL);
62}
63
64
65static void
66remove_element_str (struct GNUNET_SET_Handle *set,
67 char *str)
68{
69 struct GNUNET_SET_Element element;
70
71 element.element_type = 0;
72 element.data = str;
73 element.size = strlen (str);
74 GNUNET_SET_remove_element (set,
75 &element,
76 NULL,
77 NULL);
78}
79
80
81/**
82 * Signature of the main function of a task.
83 *
84 * @param cls closure
85 */
86static void
87timeout_fail (void *cls)
88{
89 tt = NULL;
90 GNUNET_SCHEDULER_shutdown ();
91 ret = 1;
92}
93
94
95struct CountIterClosure
96{
97 unsigned int expected_count;
98 unsigned int ongoing_count;
99 GNUNET_SCHEDULER_TaskCallback cont;
100 void *cont_cls;
101 char *what;
102};
103
104
105static int
106check_count_iter (void *cls,
107 const struct GNUNET_SET_Element *element)
108{
109 struct CountIterClosure *ci_cls = cls;
110
111 if (NULL == element)
112 {
113 if (ci_cls->expected_count != ci_cls->ongoing_count)
114 {
115 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
116 "Expected count (what: %s) to be %u, but it's actually %u\n",
117 ci_cls->what,
118 ci_cls->expected_count,
119 ci_cls->ongoing_count);
120 ret = 1;
121 GNUNET_SCHEDULER_shutdown ();
122 return GNUNET_NO;
123 }
124 ci_cls->cont (ci_cls->cont_cls);
125 GNUNET_free (ci_cls);
126 return GNUNET_NO;
127 }
128 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
129 "Set `%s' has element %.*s\n",
130 ci_cls->what,
131 (int) element->size,
132 (const char *) element->data);
133
134 ci_cls->ongoing_count++;
135 return GNUNET_YES;
136}
137
138
139static void
140check_count (struct GNUNET_SET_Handle *set,
141 char *what,
142 unsigned int expected_count,
143 GNUNET_SCHEDULER_TaskCallback cont,
144 void *cont_cls)
145{
146 struct CountIterClosure *ci_cls = GNUNET_new (struct CountIterClosure);
147
148 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
149 "Checking count of %s\n",
150 what);
151
152 ci_cls->expected_count = expected_count;
153 ci_cls->ongoing_count = 0;
154 ci_cls->cont = cont;
155 ci_cls->cont_cls = cont_cls;
156 ci_cls->what = what;
157
158 GNUNET_assert (GNUNET_YES ==
159 GNUNET_SET_iterate (set,
160 &check_count_iter,
161 ci_cls));
162}
163
164
165static void
166test_done (void *cls)
167{
168 GNUNET_SCHEDULER_shutdown ();
169}
170
171
172static void
173check_new_set_count (void *cls)
174{
175 check_count (set2,
176 "new set",
177 3,
178 &test_done,
179 NULL);
180}
181
182
183static void
184copy_done (void *cls,
185 struct GNUNET_SET_Handle *new_set)
186{
187 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
188 "copy done\n");
189 set2 = new_set;
190 remove_element_str (set2,
191 "k5555");
192 add_element_str (set2,
193 "n66666");
194 add_element_str (set2,
195 "new2butremoved");
196 remove_element_str (set2,
197 "new2butremoved");
198 remove_element_str (set2,
199 "new3justremoved");
200 // Check that set1 didn't change.
201 check_count (set1,
202 "old set",
203 3,
204 &check_new_set_count,
205 NULL);
206}
207
208
209static void
210test_copy (void *cls)
211{
212 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
213 "about to copy\n");
214 GNUNET_SET_copy_lazy (set1,
215 &copy_done,
216 NULL);
217}
218
219
220/**
221 * Function run on shutdown.
222 *
223 * @param cls closure
224 */
225static void
226do_shutdown (void *cls)
227{
228 if (NULL != tt)
229 {
230 GNUNET_SCHEDULER_cancel (tt);
231 tt = NULL;
232 }
233 if (NULL != set1)
234 {
235 GNUNET_SET_destroy (set1);
236 set1 = NULL;
237 }
238 if (NULL != set2)
239 {
240 GNUNET_SET_destroy (set2);
241 set2 = NULL;
242 }
243}
244
245
246/**
247 * Signature of the 'main' function for a (single-peer) testcase that
248 * is run using #GNUNET_TESTING_peer_run().
249 *
250 * @param cls closure
251 * @param cfg configuration of the peer that was started
252 * @param peer identity of the peer that was created
253 */
254static void
255run (void *cls,
256 const struct GNUNET_CONFIGURATION_Handle *cfg,
257 struct GNUNET_TESTING_Peer *peer)
258{
259 tt = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (
260 GNUNET_TIME_UNIT_SECONDS, 5),
261 &timeout_fail,
262 NULL);
263 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
264 NULL);
265 config = cfg;
266 GNUNET_TESTING_peer_get_identity (peer,
267 &local_id);
268
269 set1 = GNUNET_SET_create (cfg,
270 GNUNET_SET_OPERATION_UNION);
271 add_element_str (set1,
272 "333");
273 add_element_str (set1,
274 "k444");
275 /* duplicate -- ignored */
276 add_element_str (set1,
277 "k444");
278 remove_element_str (set1,
279 "333");
280 /* non-existent -- ignored */
281 remove_element_str (set1,
282 "999999999");
283 add_element_str (set1,
284 "k5555");
285 /* duplicate -- ignored */
286 remove_element_str (set1,
287 "333");
288 add_element_str (set1,
289 "k2");
290
291 check_count (set1,
292 "initial test",
293 3,
294 &test_copy,
295 NULL);
296}
297
298
299int
300main (int argc, char **argv)
301{
302 if (0 != GNUNET_TESTING_peer_run ("test_set_union_copy",
303 "test_set.conf",
304 &run, NULL))
305 {
306 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
307 "failed to start testing peer\n");
308 return 1;
309 }
310 return ret;
311}
diff --git a/src/contrib/service/set/test_set_union_result_symmetric.c b/src/contrib/service/set/test_set_union_result_symmetric.c
new file mode 100644
index 000000000..b6c7a82f6
--- /dev/null
+++ b/src/contrib/service/set/test_set_union_result_symmetric.c
@@ -0,0 +1,455 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file set/test_set_union_result_smmetric
23 * @brief testcase for symmetric result mode of the union set operation
24 * @author Florian Dold
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_testing_lib.h"
30#include "gnunet_set_service.h"
31
32
33/**
34 * Value to return from #main().
35 */
36static int ret;
37
38static struct GNUNET_PeerIdentity local_id;
39
40static struct GNUNET_HashCode app_id;
41
42static struct GNUNET_SET_Handle *set1;
43
44static struct GNUNET_SET_Handle *set2;
45
46static struct GNUNET_SET_ListenHandle *listen_handle;
47
48static const struct GNUNET_CONFIGURATION_Handle *config;
49
50static struct GNUNET_SET_OperationHandle *oh1;
51
52static struct GNUNET_SET_OperationHandle *oh2;
53
54static int iter_count;
55
56/**
57 * Are we testing correctness for the empty set union?
58 */
59static int empty;
60
61/**
62 * Number of elements found in set 1
63 */
64static unsigned int count_set1;
65
66/**
67 * Number of elements found in set 2
68 */
69static unsigned int count_set2;
70
71/**
72 * Task that is run when the test times out.
73 */
74static struct GNUNET_SCHEDULER_Task *timeout_task;
75
76
77static void
78result_cb_set1 (void *cls,
79 const struct GNUNET_SET_Element *element,
80 uint64_t current_size,
81 enum GNUNET_SET_Status status)
82{
83 switch (status)
84 {
85 case GNUNET_SET_STATUS_ADD_LOCAL:
86 count_set1++;
87 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
88 "set 1: got element\n");
89 break;
90
91 case GNUNET_SET_STATUS_FAILURE:
92 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
93 "set 1: failure\n");
94 oh1 = NULL;
95 ret = 1;
96 if (NULL != timeout_task)
97 {
98 GNUNET_SCHEDULER_cancel (timeout_task);
99 timeout_task = NULL;
100 }
101 GNUNET_SCHEDULER_shutdown ();
102 break;
103
104 case GNUNET_SET_STATUS_DONE:
105 oh1 = NULL;
106 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
107 "set 1: done\n");
108 GNUNET_SET_destroy (set1);
109 set1 = NULL;
110 if (NULL == set2)
111 {
112 if (NULL != timeout_task)
113 {
114 GNUNET_SCHEDULER_cancel (timeout_task);
115 timeout_task = NULL;
116 }
117 GNUNET_SCHEDULER_shutdown ();
118 }
119 break;
120
121 case GNUNET_SET_STATUS_ADD_REMOTE:
122 break;
123
124 default:
125 GNUNET_assert (0);
126 }
127}
128
129
130static void
131result_cb_set2 (void *cls,
132 const struct GNUNET_SET_Element *element,
133 uint64_t current_size,
134 enum GNUNET_SET_Status status)
135{
136 switch (status)
137 {
138 case GNUNET_SET_STATUS_ADD_LOCAL:
139 count_set2++;
140 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
141 "set 2: got element\n");
142 break;
143
144 case GNUNET_SET_STATUS_FAILURE:
145 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
146 "set 2: failure\n");
147 oh2 = NULL;
148 ret = 1;
149 if (NULL != timeout_task)
150 {
151 GNUNET_SCHEDULER_cancel (timeout_task);
152 timeout_task = NULL;
153 }
154 GNUNET_SCHEDULER_shutdown ();
155 break;
156
157 case GNUNET_SET_STATUS_DONE:
158 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
159 "set 2: done\n");
160 oh2 = NULL;
161 GNUNET_SET_destroy (set2);
162 set2 = NULL;
163 if (NULL == set1)
164 {
165 if (NULL != timeout_task)
166 {
167 GNUNET_SCHEDULER_cancel (timeout_task);
168 timeout_task = NULL;
169 }
170 GNUNET_SCHEDULER_shutdown ();
171 }
172 break;
173
174 case GNUNET_SET_STATUS_ADD_REMOTE:
175 break;
176
177 default:
178 GNUNET_assert (0);
179 }
180}
181
182
183static void
184listen_cb (void *cls,
185 const struct GNUNET_PeerIdentity *other_peer,
186 const struct GNUNET_MessageHeader *context_msg,
187 struct GNUNET_SET_Request *request)
188{
189 GNUNET_assert (NULL != context_msg);
190 GNUNET_assert (ntohs (context_msg->type) == GNUNET_MESSAGE_TYPE_DUMMY);
191 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
192 "listen cb called\n");
193 oh2 = GNUNET_SET_accept (request,
194 GNUNET_SET_RESULT_SYMMETRIC,
195 (struct GNUNET_SET_Option[]) { 0 },
196 &result_cb_set2,
197 NULL);
198 GNUNET_SET_commit (oh2,
199 set2);
200}
201
202
203/**
204 * Start the set operation.
205 *
206 * @param cls closure, unused
207 */
208static void
209start (void *cls)
210{
211 struct GNUNET_MessageHeader context_msg;
212
213 context_msg.size = htons (sizeof context_msg);
214 context_msg.type = htons (GNUNET_MESSAGE_TYPE_DUMMY);
215
216 listen_handle = GNUNET_SET_listen (config,
217 GNUNET_SET_OPERATION_UNION,
218 &app_id,
219 &listen_cb, NULL);
220 oh1 = GNUNET_SET_prepare (&local_id,
221 &app_id,
222 &context_msg,
223 GNUNET_SET_RESULT_SYMMETRIC,
224 (struct GNUNET_SET_Option[]) { 0 },
225 &result_cb_set1, NULL);
226 GNUNET_SET_commit (oh1, set1);
227}
228
229
230/**
231 * Initialize the second set, continue
232 *
233 * @param cls closure, unused
234 */
235static void
236init_set2 (void *cls)
237{
238 struct GNUNET_SET_Element element;
239
240 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
241 "initializing set 2\n");
242 if (empty)
243 {
244 start (NULL);
245 return;
246 }
247 element.element_type = 0;
248 element.data = "hello";
249 element.size = strlen (element.data);
250 GNUNET_SET_add_element (set2,
251 &element,
252 NULL,
253 NULL);
254 element.data = "quux";
255 element.size = strlen (element.data);
256 GNUNET_SET_add_element (set2,
257 &element,
258 NULL,
259 NULL);
260 element.data = "baz";
261 element.size = strlen (element.data);
262 GNUNET_SET_add_element (set2,
263 &element,
264 &start, NULL);
265}
266
267
268/**
269 * Initialize the first set, continue.
270 */
271static void
272init_set1 (void)
273{
274 struct GNUNET_SET_Element element;
275
276 if (empty)
277 {
278 init_set2 (NULL);
279 return;
280 }
281 element.element_type = 0;
282 element.data = "hello";
283 element.size = strlen (element.data);
284 GNUNET_SET_add_element (set1,
285 &element,
286 NULL,
287 NULL);
288 element.data = "bar";
289 element.size = strlen (element.data);
290 GNUNET_SET_add_element (set1,
291 &element,
292 &init_set2,
293 NULL);
294 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
295 "initialized set 1\n");
296}
297
298
299static int
300iter_cb (void *cls,
301 const struct GNUNET_SET_Element *element)
302{
303 if (NULL == element)
304 {
305 GNUNET_assert (iter_count == 3);
306 GNUNET_SET_destroy (cls);
307 return GNUNET_YES;
308 }
309 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
310 "iter: got element\n");
311 iter_count++;
312 return GNUNET_YES;
313}
314
315
316static void
317test_iter ()
318{
319 struct GNUNET_SET_Element element;
320 struct GNUNET_SET_Handle *iter_set;
321
322 iter_count = 0;
323 iter_set = GNUNET_SET_create (config, GNUNET_SET_OPERATION_UNION);
324 element.element_type = 0;
325 element.data = "hello";
326 element.size = strlen (element.data);
327 GNUNET_SET_add_element (iter_set, &element, NULL, NULL);
328 element.data = "bar";
329 element.size = strlen (element.data);
330 GNUNET_SET_add_element (iter_set, &element, NULL, NULL);
331 element.data = "quux";
332 element.size = strlen (element.data);
333 GNUNET_SET_add_element (iter_set, &element, NULL, NULL);
334
335 GNUNET_SET_iterate (iter_set,
336 &iter_cb,
337 iter_set);
338}
339
340
341/**
342 * Signature of the main function of a task.
343 *
344 * @param cls closure
345 */
346static void
347timeout_fail (void *cls)
348{
349 timeout_task = NULL;
350 GNUNET_SCHEDULER_shutdown ();
351 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
352 "test timed out\n");
353 ret = 1;
354}
355
356
357/**
358 * Function run on shutdown.
359 *
360 * @param cls closure
361 */
362static void
363do_shutdown (void *cls)
364{
365 if (NULL != timeout_task)
366 {
367 GNUNET_SCHEDULER_cancel (timeout_task);
368 timeout_task = NULL;
369 }
370 if (NULL != oh1)
371 {
372 GNUNET_SET_operation_cancel (oh1);
373 oh1 = NULL;
374 }
375 if (NULL != oh2)
376 {
377 GNUNET_SET_operation_cancel (oh2);
378 oh2 = NULL;
379 }
380 if (NULL != set1)
381 {
382 GNUNET_SET_destroy (set1);
383 set1 = NULL;
384 }
385 if (NULL != set2)
386 {
387 GNUNET_SET_destroy (set2);
388 set2 = NULL;
389 }
390 if (NULL != listen_handle)
391 {
392 GNUNET_SET_listen_cancel (listen_handle);
393 listen_handle = NULL;
394 }
395}
396
397
398/**
399 * Signature of the 'main' function for a (single-peer) testcase that
400 * is run using 'GNUNET_TESTING_peer_run'.
401 *
402 * @param cls closure
403 * @param cfg configuration of the peer that was started
404 * @param peer identity of the peer that was created
405 */
406static void
407run (void *cls,
408 const struct GNUNET_CONFIGURATION_Handle *cfg,
409 struct GNUNET_TESTING_Peer *peer)
410{
411 timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (
412 GNUNET_TIME_UNIT_SECONDS, 5),
413 &timeout_fail,
414 NULL);
415 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
416 NULL);
417 config = cfg;
418 GNUNET_TESTING_peer_get_identity (peer,
419 &local_id);
420
421 if (0)
422 test_iter ();
423
424 set1 = GNUNET_SET_create (cfg, GNUNET_SET_OPERATION_UNION);
425 set2 = GNUNET_SET_create (cfg, GNUNET_SET_OPERATION_UNION);
426 GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, &app_id);
427
428 /* test the real set reconciliation */
429 init_set1 ();
430}
431
432
433int
434main (int argc, char **argv)
435{
436 empty = 1;
437 if (0 != GNUNET_TESTING_peer_run ("test_set_api",
438 "test_set.conf",
439 &run, NULL))
440 {
441 return 1;
442 }
443 GNUNET_assert (0 == count_set1);
444 GNUNET_assert (0 == count_set2);
445 empty = 0;
446 if (0 != GNUNET_TESTING_peer_run ("test_set_api",
447 "test_set.conf",
448 &run, NULL))
449 {
450 return 1;
451 }
452 GNUNET_break (2 == count_set1);
453 GNUNET_break (1 == count_set2);
454 return ret;
455}