RECLAIM: refactor revoke
[oweals/gnunet.git] / src / reclaim / gnunet-service-reclaim_tickets.c
1 /*
2    This file is part of GNUnet.
3    Copyright (C) 2012-2015 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 Martin Schanzenbach
23  * @file src/reclaim/gnunet-service-reclaim_tickets.c
24  * @brief reclaim tickets
25  *
26  */
27 #include <inttypes.h>
28
29 #include "gnunet-service-reclaim_tickets.h"
30
31 struct ParallelLookup;
32
33 struct RECLAIM_TICKETS_ConsumeHandle {
34   /**
35    * Ticket
36    */
37   struct GNUNET_RECLAIM_Ticket ticket;
38
39   /**
40    * LookupRequest
41    */
42   struct GNUNET_GNS_LookupRequest *lookup_request;
43
44   /**
45    * Audience Key
46    */
47   struct GNUNET_CRYPTO_EcdsaPrivateKey identity;
48
49   /**
50    * Audience Key
51    */
52   struct GNUNET_CRYPTO_EcdsaPublicKey identity_pub;
53
54   /**
55    * Lookup DLL
56    */
57   struct ParallelLookup *parallel_lookups_head;
58
59   /**
60    * Lookup DLL
61    */
62   struct ParallelLookup *parallel_lookups_tail;
63
64   /**
65    * Kill task
66    */
67   struct GNUNET_SCHEDULER_Task *kill_task;
68
69   /**
70    * Attributes
71    */
72   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs;
73
74   /**
75    * Lookup time
76    */
77   struct GNUNET_TIME_Absolute lookup_start_time;
78
79   /**
80    * Callback
81    */
82   RECLAIM_TICKETS_ConsumeCallback cb;
83
84   /**
85    * Callbacl closure
86    */
87   void *cb_cls;
88 };
89
90 /**
91  * Handle for a parallel GNS lookup job
92  */
93 struct ParallelLookup {
94   /* DLL */
95   struct ParallelLookup *next;
96
97   /* DLL */
98   struct ParallelLookup *prev;
99
100   /* The GNS request */
101   struct GNUNET_GNS_LookupRequest *lookup_request;
102
103   /* The handle the return to */
104   struct RECLAIM_TICKETS_ConsumeHandle *handle;
105
106   /**
107    * Lookup time
108    */
109   struct GNUNET_TIME_Absolute lookup_start_time;
110
111   /* The label to look up */
112   char *label;
113 };
114
115
116 /**
117  * A reference to a ticket stored in GNS
118  */
119 struct TicketReference {
120   /**
121    * DLL
122    */
123   struct TicketReference *next;
124
125   /**
126    * DLL
127    */
128   struct TicketReference *prev;
129
130   /**
131    * Attributes
132    */
133   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs;
134
135   /**
136    * Tickets
137    */
138   struct GNUNET_RECLAIM_Ticket ticket;
139 };
140
141
142 /**
143  * Ticket issue request handle
144  */
145 struct TicketIssueHandle {
146   /**
147    * Attributes to issue
148    */
149   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs;
150
151   /**
152    * Issuer Key
153    */
154   struct GNUNET_CRYPTO_EcdsaPrivateKey identity;
155
156   /**
157    * Ticket to issue
158    */
159   struct GNUNET_RECLAIM_Ticket ticket;
160
161   /**
162    * QueueEntry
163    */
164   struct GNUNET_NAMESTORE_QueueEntry *ns_qe;
165
166   /**
167    * Ticket reference list
168    */
169   struct TicketReference *ticket_refs_head;
170
171   /**
172    * Ticket reference list
173    */
174   struct TicketReference *ticket_refs_tail;
175
176   /**
177    * Number of references
178    */
179   uint32_t ticket_ref_num;
180
181   /**
182    * Callback
183    */
184   RECLAIM_TICKETS_TicketResult cb;
185
186   /**
187    * Callback cls
188    */
189   void *cb_cls;
190 };
191
192 /**
193  * Ticket iterator
194  */
195 struct RECLAIM_TICKETS_Iterator {
196   /**
197    * Issuer Key
198    */
199   struct GNUNET_CRYPTO_EcdsaPrivateKey identity;
200
201   /**
202    * Issuer pubkey
203    */
204   struct GNUNET_CRYPTO_EcdsaPublicKey identity_pub;
205
206   /**
207    * Namestore queue entry
208    */
209   struct GNUNET_NAMESTORE_QueueEntry *ns_qe;
210
211   /**
212    * Iter callback
213    */
214   RECLAIM_TICKETS_TicketIter cb;
215
216   /**
217    * Iter cls
218    */
219   void *cb_cls;
220
221   /**
222    * Ticket reference list
223    */
224   struct TicketReference *tickets_head;
225
226   /**
227    * Ticket reference list
228    */
229   struct TicketReference *tickets_tail;
230 };
231
232
233 struct RevokedAttributeEntry {
234   /**
235    * DLL
236    */
237   struct RevokedAttributeEntry *next;
238
239   /**
240    * DLL
241    */
242   struct RevokedAttributeEntry *prev;
243
244   /**
245    * Old ID of the attribute
246    */
247   uint64_t old_id;
248
249   /**
250    * New ID of the attribute
251    */
252   uint64_t new_id;
253 };
254
255
256 struct TicketRecordsEntry {
257   /**
258    * DLL
259    */
260   struct TicketRecordsEntry *next;
261
262   /**
263    * DLL
264    */
265   struct TicketRecordsEntry *prev;
266
267   /**
268    * Record count
269    */
270   unsigned int rd_count;
271
272   /**
273    * Data
274    */
275   char *data;
276
277   /**
278    * Data size
279    */
280   size_t data_size;
281
282   /**
283    * Label
284    */
285   char *label;
286 };
287
288 /**
289  * Ticket revocation request handle
290  */
291 struct RECLAIM_TICKETS_RevokeHandle {
292   /**
293    * Issuer Key
294    */
295   struct GNUNET_CRYPTO_EcdsaPrivateKey identity;
296
297   /**
298    * Callback
299    */
300   RECLAIM_TICKETS_RevokeCallback cb;
301
302   /**
303    * Callback cls
304    */
305   void *cb_cls;
306
307   /**
308    * Ticket to issue
309    */
310   struct GNUNET_RECLAIM_Ticket ticket;
311
312   /**
313    * QueueEntry
314    */
315   struct GNUNET_NAMESTORE_QueueEntry *ns_qe;
316
317   /**
318    * Namestore iterator
319    */
320   struct GNUNET_NAMESTORE_ZoneIterator *ns_it;
321
322   /**
323    * Revoked attributes
324    */
325   struct RevokedAttributeEntry *attrs_head;
326
327   /**
328    * Revoked attributes
329    */
330   struct RevokedAttributeEntry *attrs_tail;
331
332   /**
333    * Current attribute to move
334    */
335   struct RevokedAttributeEntry *move_attr;
336
337   /**
338    * Number of attributes in ticket
339    */
340   unsigned int ticket_attrs;
341
342   /**
343    * Tickets to update
344    */
345   struct TicketRecordsEntry *tickets_to_update_head;
346
347   /**
348    * Tickets to update
349    */
350   struct TicketRecordsEntry *tickets_to_update_tail;
351 };
352
353
354 /* Namestore handle */
355 static struct GNUNET_NAMESTORE_Handle *nsh;
356
357 /* GNS handle */
358 static struct GNUNET_GNS_Handle *gns;
359
360 /* Handle to the statistics service */
361 static struct GNUNET_STATISTICS_Handle *stats;
362
363 static void
364 move_attrs (struct RECLAIM_TICKETS_RevokeHandle *rh);
365
366 static void
367 move_attrs_cont (void *cls)
368 {
369   move_attrs ((struct RECLAIM_TICKETS_RevokeHandle *)cls);
370 }
371
372 /**
373  * Cleanup revoke handle
374  *
375  * @param rh the ticket revocation handle
376  */
377 static void
378 cleanup_rvk (struct RECLAIM_TICKETS_RevokeHandle *rh)
379 {
380   struct RevokedAttributeEntry *ae;
381   struct TicketRecordsEntry *le;
382   if (NULL != rh->ns_qe)
383     GNUNET_NAMESTORE_cancel (rh->ns_qe);
384   if (NULL != rh->ns_it)
385     GNUNET_NAMESTORE_zone_iteration_stop (rh->ns_it);
386   while (NULL != (ae = rh->attrs_head)) {
387     GNUNET_CONTAINER_DLL_remove (rh->attrs_head, rh->attrs_tail, ae);
388     GNUNET_free (ae);
389   }
390   while (NULL != (le = rh->tickets_to_update_head)) {
391     GNUNET_CONTAINER_DLL_remove (rh->tickets_to_update_head,
392                                  rh->tickets_to_update_head, le);
393     if (NULL != le->data)
394       GNUNET_free (le->data);
395     if (NULL != le->label)
396       GNUNET_free (le->label);
397     GNUNET_free (le);
398   }
399   GNUNET_free (rh);
400 }
401
402 static void
403 del_attr_finished (void *cls, int32_t success, const char *emsg)
404 {
405   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
406   rvk->ns_qe = NULL;
407   if (GNUNET_SYSERR == success) {
408     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error removing attribute: %s\n",
409                 emsg);
410     rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
411     cleanup_rvk (rvk);
412     return;
413   }
414   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Continuing\n");
415   rvk->move_attr = rvk->move_attr->next;
416   GNUNET_SCHEDULER_add_now (&move_attrs_cont, rvk);
417 }
418
419 static void
420 move_attr_finished (void *cls, int32_t success, const char *emsg)
421 {
422   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
423   char *label;
424   rvk->ns_qe = NULL;
425   if (GNUNET_SYSERR == success) {
426     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error moving attribute: %s\n", emsg);
427     rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
428     cleanup_rvk (rvk);
429     return;
430   }
431   label = GNUNET_STRINGS_data_to_string_alloc (&rvk->move_attr->old_id,
432                                                sizeof (uint64_t));
433   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Removing attribute %s\n", label);
434   rvk->ns_qe = GNUNET_NAMESTORE_records_store (nsh, &rvk->identity, label, 0,
435                                                NULL, &del_attr_finished, rvk);
436 }
437
438
439 static void
440 rvk_move_attr_cb (void *cls, const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
441                   const char *label, unsigned int rd_count,
442                   const struct GNUNET_GNSRECORD_Data *rd)
443 {
444   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
445   struct RevokedAttributeEntry *le;
446   char *new_label;
447   rvk->ns_qe = NULL;
448   if (0 == rd_count) {
449     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
450                 "The attribute %s no longer exists!\n", label);
451     le = rvk->move_attr;
452     rvk->move_attr = le->next;
453     GNUNET_CONTAINER_DLL_remove (rvk->attrs_head, rvk->attrs_tail, le);
454     GNUNET_free (le);
455     GNUNET_SCHEDULER_add_now (&move_attrs_cont, rvk);
456     return;
457   }
458   /** find a new place for this attribute **/
459   rvk->move_attr->new_id =
460       GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_STRONG, UINT64_MAX);
461   new_label = GNUNET_STRINGS_data_to_string_alloc (&rvk->move_attr->new_id,
462                                                    sizeof (uint64_t));
463   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Adding attribute %s\n", new_label);
464   rvk->ns_qe = GNUNET_NAMESTORE_records_store (nsh, &rvk->identity, new_label,
465                                                1, rd, &move_attr_finished, rvk);
466   GNUNET_free (new_label);
467 }
468
469
470 static void
471 rvk_ticket_update (void *cls, const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
472                    const char *label, unsigned int rd_count,
473                    const struct GNUNET_GNSRECORD_Data *rd)
474 {
475   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
476   struct TicketRecordsEntry *le;
477   struct RevokedAttributeEntry *ae;
478   int has_changed = GNUNET_NO;
479
480   /** Let everything point to the old record **/
481   for (int i = 0; i < rd_count; i++) {
482     if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR_REF != rd[i].record_type)
483       continue;
484     for (ae = rvk->attrs_head; NULL != ae; ae = ae->next) {
485       if (0 != memcmp (rd[i].data, &ae->old_id, sizeof (uint64_t)))
486         continue;
487       has_changed = GNUNET_YES;
488       break;
489     }
490     if (GNUNET_YES == has_changed)
491       break;
492   }
493   if (GNUNET_YES == has_changed) {
494     le = GNUNET_new (struct TicketRecordsEntry);
495     le->data_size = GNUNET_GNSRECORD_records_get_size (rd_count, rd);
496     le->data = GNUNET_malloc (le->data_size);
497     le->rd_count = rd_count;
498     le->label = GNUNET_strdup (label);
499     GNUNET_GNSRECORD_records_serialize (rd_count, rd, le->data_size, le->data);
500     GNUNET_CONTAINER_DLL_insert (rvk->tickets_to_update_head,
501                                  rvk->tickets_to_update_tail, le);
502   }
503   GNUNET_NAMESTORE_zone_iterator_next (rvk->ns_it, 1);
504 }
505
506
507 static void
508 process_tickets (void *cls);
509
510
511 static void
512 ticket_processed (void *cls, int32_t success, const char *emsg)
513 {
514   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
515   rvk->ns_qe = NULL;
516   GNUNET_SCHEDULER_add_now (&process_tickets, rvk);
517 }
518
519 static void
520 process_tickets (void *cls)
521 {
522   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
523   struct TicketRecordsEntry *le;
524   struct RevokedAttributeEntry *ae;
525   if (NULL == rvk->tickets_to_update_head) {
526     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
527                 "Finished updatding tickets, success\n");
528     rvk->cb (rvk->cb_cls, GNUNET_OK);
529     cleanup_rvk (rvk);
530     return;
531   }
532   le = rvk->tickets_to_update_head;
533   GNUNET_CONTAINER_DLL_remove (rvk->tickets_to_update_head,
534                                rvk->tickets_to_update_tail, le);
535   struct GNUNET_GNSRECORD_Data rd[le->rd_count];
536   GNUNET_GNSRECORD_records_deserialize (le->data_size, le->data, le->rd_count,
537                                         rd);
538   for (int i = 0; i < le->rd_count; i++) {
539     if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR_REF != rd[i].record_type)
540       continue;
541     for (ae = rvk->attrs_head; NULL != ae; ae = ae->next) {
542       if (0 != memcmp (rd[i].data, &ae->old_id, sizeof (uint64_t)))
543         continue;
544       rd[i].data = &ae->new_id;
545     }
546   }
547   rvk->ns_qe = GNUNET_NAMESTORE_records_store (
548       nsh, &rvk->identity, le->label, le->rd_count, rd, &ticket_processed, rvk);
549   GNUNET_free (le->label);
550   GNUNET_free (le->data);
551   GNUNET_free (le);
552 }
553
554 static void
555 rvk_ticket_update_finished (void *cls)
556 {
557   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
558   rvk->ns_it = NULL;
559   GNUNET_SCHEDULER_add_now (&process_tickets, rvk);
560 }
561
562
563 static void
564 rvk_ns_iter_err (void *cls)
565 {
566   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
567   rvk->ns_it = NULL;
568   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
569               "Namestore error on revocation (id=%" PRIu64 "\n",
570               rvk->move_attr->old_id);
571   rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
572   cleanup_rvk (rvk);
573 }
574
575
576 static void
577 rvk_ns_err (void *cls)
578 {
579   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
580   rvk->ns_qe = NULL;
581   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
582               "Namestore error on revocation (id=%" PRIu64 "\n",
583               rvk->move_attr->old_id);
584   rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
585   cleanup_rvk (rvk);
586 }
587
588
589 static void
590 move_attrs (struct RECLAIM_TICKETS_RevokeHandle *rvk)
591 {
592   char *label;
593
594   if (NULL == rvk->move_attr) {
595     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Finished moving attributes\n");
596     rvk->ns_it = GNUNET_NAMESTORE_zone_iteration_start (
597         nsh, &rvk->identity, &rvk_ns_iter_err, rvk, &rvk_ticket_update, rvk,
598         &rvk_ticket_update_finished, rvk);
599     return;
600   }
601   label = GNUNET_STRINGS_data_to_string_alloc (&rvk->move_attr->old_id,
602                                                sizeof (uint64_t));
603   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Moving attribute %s\n", label);
604
605   rvk->ns_qe = GNUNET_NAMESTORE_records_lookup (
606       nsh, &rvk->identity, label, &rvk_ns_err, rvk, &rvk_move_attr_cb, rvk);
607   GNUNET_free (label);
608 }
609
610
611 static void
612 remove_ticket_cont (void *cls, int32_t success, const char *emsg)
613 {
614   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
615   rvk->ns_qe = NULL;
616   if (GNUNET_SYSERR == success) {
617     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n", emsg);
618     rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
619     cleanup_rvk (rvk);
620     return;
621   }
622   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deleted ticket\n");
623   if (0 == rvk->ticket_attrs) {
624     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
625                 "No attributes to move... strange\n");
626     rvk->cb (rvk->cb_cls, GNUNET_OK);
627     cleanup_rvk (rvk);
628     return;
629   }
630   rvk->move_attr = rvk->attrs_head;
631   move_attrs (rvk);
632 }
633
634
635 static void
636 revoke_attrs_cb (void *cls, const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
637                  const char *label, unsigned int rd_count,
638                  const struct GNUNET_GNSRECORD_Data *rd)
639
640 {
641   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
642   struct RevokedAttributeEntry *le;
643   rvk->ns_qe = NULL;
644   for (int i = 0; i < rd_count; i++) {
645     if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR_REF != rd[i].record_type)
646       continue;
647     le = GNUNET_new (struct RevokedAttributeEntry);
648     le->old_id = *((uint64_t *)rd[i].data);
649     GNUNET_CONTAINER_DLL_insert (rvk->attrs_head, rvk->attrs_tail, le);
650     rvk->ticket_attrs++;
651   }
652
653   /** Now, remove ticket **/
654   rvk->ns_qe = GNUNET_NAMESTORE_records_store (nsh, &rvk->identity, label, 0,
655                                                NULL, &remove_ticket_cont, rvk);
656 }
657
658
659 static void
660 rvk_attrs_err_cb (void *cls)
661 {
662   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
663   rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
664   cleanup_rvk (rvk);
665 }
666
667
668 struct RECLAIM_TICKETS_RevokeHandle *
669 RECLAIM_TICKETS_revoke (const struct GNUNET_RECLAIM_Ticket *ticket,
670                         const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity,
671                         RECLAIM_TICKETS_RevokeCallback cb, void *cb_cls)
672 {
673   struct RECLAIM_TICKETS_RevokeHandle *rvk;
674   char *label;
675
676   rvk = GNUNET_new (struct RECLAIM_TICKETS_RevokeHandle);
677   rvk->cb = cb;
678   rvk->cb_cls = cb_cls;
679   rvk->identity = *identity;
680   rvk->ticket = *ticket;
681   GNUNET_CRYPTO_ecdsa_key_get_public (&rvk->identity, &rvk->ticket.identity);
682   /** Get shared attributes **/
683   label = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd, sizeof (uint64_t));
684
685   rvk->ns_qe = GNUNET_NAMESTORE_records_lookup (
686       nsh, identity, label, &rvk_attrs_err_cb, rvk, &revoke_attrs_cb, rvk);
687   return rvk;
688 }
689
690
691 void
692 RECLAIM_TICKETS_revoke_cancel (struct RECLAIM_TICKETS_RevokeHandle *rh)
693 {
694   cleanup_rvk (rh);
695 }
696 /*******************************
697  * Ticket consume
698  *******************************/
699
700 /**
701  * Cleanup ticket consume handle
702  * @param cth the handle to clean up
703  */
704 static void
705 cleanup_cth (struct RECLAIM_TICKETS_ConsumeHandle *cth)
706 {
707   struct ParallelLookup *lu;
708   struct ParallelLookup *tmp;
709   if (NULL != cth->lookup_request)
710     GNUNET_GNS_lookup_cancel (cth->lookup_request);
711   for (lu = cth->parallel_lookups_head; NULL != lu;) {
712     GNUNET_GNS_lookup_cancel (lu->lookup_request);
713     GNUNET_free (lu->label);
714     tmp = lu->next;
715     GNUNET_CONTAINER_DLL_remove (cth->parallel_lookups_head,
716                                  cth->parallel_lookups_tail, lu);
717     GNUNET_free (lu);
718     lu = tmp;
719   }
720
721   if (NULL != cth->attrs)
722     GNUNET_RECLAIM_ATTRIBUTE_list_destroy (cth->attrs);
723   GNUNET_free (cth);
724 }
725
726
727 static void
728 process_parallel_lookup_result (void *cls, uint32_t rd_count,
729                                 const struct GNUNET_GNSRECORD_Data *rd)
730 {
731   struct ParallelLookup *parallel_lookup = cls;
732   struct RECLAIM_TICKETS_ConsumeHandle *cth = parallel_lookup->handle;
733   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *attr_le;
734   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Parallel lookup finished (count=%u)\n",
735               rd_count);
736
737   GNUNET_CONTAINER_DLL_remove (cth->parallel_lookups_head,
738                                cth->parallel_lookups_tail, parallel_lookup);
739   GNUNET_free (parallel_lookup->label);
740
741   GNUNET_STATISTICS_update (
742       stats, "attribute_lookup_time_total",
743       GNUNET_TIME_absolute_get_duration (parallel_lookup->lookup_start_time)
744           .rel_value_us,
745       GNUNET_YES);
746   GNUNET_STATISTICS_update (stats, "attribute_lookups_count", 1, GNUNET_YES);
747
748
749   GNUNET_free (parallel_lookup);
750   if (1 != rd_count)
751     GNUNET_break (0); // TODO
752   if (rd->record_type == GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR) {
753     attr_le = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry);
754     attr_le->claim =
755         GNUNET_RECLAIM_ATTRIBUTE_deserialize (rd->data, rd->data_size);
756     GNUNET_CONTAINER_DLL_insert (cth->attrs->list_head, cth->attrs->list_tail,
757                                  attr_le);
758   }
759   if (NULL != cth->parallel_lookups_head)
760     return; // Wait for more
761   /* Else we are done */
762
763   GNUNET_SCHEDULER_cancel (cth->kill_task);
764   cth->cb (cth->cb_cls, &cth->ticket.identity, cth->attrs, GNUNET_OK, NULL);
765   cleanup_cth (cth);
766 }
767
768
769 static void
770 abort_parallel_lookups (void *cls)
771 {
772   struct RECLAIM_TICKETS_ConsumeHandle *cth = cls;
773   struct ParallelLookup *lu;
774   struct ParallelLookup *tmp;
775
776   cth->kill_task = NULL;
777   for (lu = cth->parallel_lookups_head; NULL != lu;) {
778     GNUNET_GNS_lookup_cancel (lu->lookup_request);
779     GNUNET_free (lu->label);
780     tmp = lu->next;
781     GNUNET_CONTAINER_DLL_remove (cth->parallel_lookups_head,
782                                  cth->parallel_lookups_tail, lu);
783     GNUNET_free (lu);
784     lu = tmp;
785   }
786   cth->cb (cth->cb_cls, NULL, NULL, GNUNET_SYSERR, "Aborted");
787 }
788
789
790 static void
791 lookup_authz_cb (void *cls, uint32_t rd_count,
792                  const struct GNUNET_GNSRECORD_Data *rd)
793 {
794   struct RECLAIM_TICKETS_ConsumeHandle *cth = cls;
795   struct ParallelLookup *parallel_lookup;
796   char *lbl;
797
798   cth->lookup_request = NULL;
799
800   GNUNET_STATISTICS_update (
801       stats, "reclaim_authz_lookup_time_total",
802       GNUNET_TIME_absolute_get_duration (cth->lookup_start_time).rel_value_us,
803       GNUNET_YES);
804   GNUNET_STATISTICS_update (stats, "reclaim_authz_lookups_count", 1,
805                             GNUNET_YES);
806
807   for (int i = 0; i < rd_count; i++) {
808     lbl = GNUNET_STRINGS_data_to_string_alloc (rd[i].data, rd[i].data_size);
809     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Attribute ref found %s\n", lbl);
810     parallel_lookup = GNUNET_new (struct ParallelLookup);
811     parallel_lookup->handle = cth;
812     parallel_lookup->label = lbl;
813     parallel_lookup->lookup_start_time = GNUNET_TIME_absolute_get ();
814     parallel_lookup->lookup_request = GNUNET_GNS_lookup (
815         gns, lbl, &cth->ticket.identity, GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR,
816         GNUNET_GNS_LO_DEFAULT, &process_parallel_lookup_result,
817         parallel_lookup);
818     GNUNET_CONTAINER_DLL_insert (cth->parallel_lookups_head,
819                                  cth->parallel_lookups_tail, parallel_lookup);
820   }
821   cth->kill_task = GNUNET_SCHEDULER_add_delayed (
822       GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 3),
823       &abort_parallel_lookups, cth);
824 }
825
826
827 struct RECLAIM_TICKETS_ConsumeHandle *
828 RECLAIM_TICKETS_consume (const struct GNUNET_CRYPTO_EcdsaPrivateKey *id,
829                          const struct GNUNET_RECLAIM_Ticket *ticket,
830                          RECLAIM_TICKETS_ConsumeCallback cb, void *cb_cls)
831 {
832   struct RECLAIM_TICKETS_ConsumeHandle *cth;
833   char *label;
834   cth = GNUNET_new (struct RECLAIM_TICKETS_ConsumeHandle);
835
836   cth->identity = *id;
837   GNUNET_CRYPTO_ecdsa_key_get_public (&cth->identity, &cth->identity_pub);
838   cth->attrs = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList);
839   cth->ticket = *ticket;
840   cth->cb = cb;
841   cth->cb_cls = cb_cls;
842   label =
843       GNUNET_STRINGS_data_to_string_alloc (&cth->ticket.rnd, sizeof (uint64_t));
844   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Looking for AuthZ info under %s\n",
845               label);
846   cth->lookup_start_time = GNUNET_TIME_absolute_get ();
847   cth->lookup_request = GNUNET_GNS_lookup (
848       gns, label, &cth->ticket.identity, GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR_REF,
849       GNUNET_GNS_LO_DEFAULT, &lookup_authz_cb, cth);
850   GNUNET_free (label);
851   return cth;
852 }
853
854 void
855 RECLAIM_TICKETS_consume_cancel (struct RECLAIM_TICKETS_ConsumeHandle *cth)
856 {
857   cleanup_cth (cth);
858   return;
859 }
860
861
862 /*******************************
863  * Ticket issue
864  *******************************/
865
866 /**
867  * Cleanup ticket consume handle
868  * @param handle the handle to clean up
869  */
870 static void
871 cleanup_issue_handle (struct TicketIssueHandle *handle)
872 {
873   struct TicketReference *tr;
874   struct TicketReference *tr_tmp;
875   if (NULL != handle->attrs)
876     GNUNET_RECLAIM_ATTRIBUTE_list_destroy (handle->attrs);
877   if (NULL != handle->ns_qe)
878     GNUNET_NAMESTORE_cancel (handle->ns_qe);
879   for (tr = handle->ticket_refs_head; NULL != tr;) {
880     if (NULL != tr->attrs)
881       GNUNET_RECLAIM_ATTRIBUTE_list_destroy (tr->attrs);
882     tr_tmp = tr;
883     tr = tr->next;
884     GNUNET_free (tr_tmp);
885   }
886   GNUNET_free (handle);
887 }
888
889
890 static void
891 store_ticket_refs_cont (void *cls, int32_t success, const char *emsg)
892 {
893   struct TicketIssueHandle *handle = cls;
894   handle->ns_qe = NULL;
895   if (GNUNET_OK != success) {
896     handle->cb (handle->cb_cls, NULL, GNUNET_SYSERR,
897                 "Error storing updated ticket refs in GNS");
898     cleanup_issue_handle (handle);
899     return;
900   }
901   handle->cb (handle->cb_cls, &handle->ticket, GNUNET_OK, NULL);
902   cleanup_issue_handle (handle);
903 }
904
905
906 static void
907 update_ticket_refs (void *cls)
908 {
909   struct TicketIssueHandle *handle = cls;
910   struct GNUNET_GNSRECORD_Data refs_rd[handle->ticket_ref_num];
911   struct TicketReference *tr;
912
913   tr = handle->ticket_refs_head;
914   for (int i = 0; i < handle->ticket_ref_num; i++) {
915     refs_rd[i].data = &tr->ticket;
916     refs_rd[i].data_size = sizeof (struct GNUNET_RECLAIM_Ticket);
917     refs_rd[i].expiration_time = GNUNET_TIME_UNIT_DAYS.rel_value_us;
918     refs_rd[i].record_type = GNUNET_GNSRECORD_TYPE_RECLAIM_TICKETREF;
919     refs_rd[i].flags =
920         GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION | GNUNET_GNSRECORD_RF_PRIVATE;
921     tr = tr->next;
922   }
923
924   handle->ns_qe = GNUNET_NAMESTORE_records_store (
925       nsh, &handle->identity, GNUNET_GNS_EMPTY_LABEL_AT, handle->ticket_ref_num,
926       refs_rd, &store_ticket_refs_cont, handle);
927 }
928
929
930 static void
931 ticket_lookup_cb (void *cls, const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
932                   const char *label, unsigned int rd_count,
933                   const struct GNUNET_GNSRECORD_Data *rd)
934 {
935   struct TicketIssueHandle *handle = cls;
936   struct TicketReference *tr;
937
938   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
939               "Received tickets from local namestore.\n");
940   handle->ns_qe = NULL;
941   for (int i = 0; i < rd_count; i++) {
942     if (GNUNET_GNSRECORD_TYPE_RECLAIM_TICKETREF != rd[i].record_type)
943       continue;
944     tr = GNUNET_new (struct TicketReference);
945     memcpy (&tr->ticket, rd[i].data, sizeof (struct GNUNET_RECLAIM_Ticket));
946     if (0 != memcmp (&tr->ticket.identity, &handle->ticket.identity,
947                      sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) {
948       // Not our ticket
949       GNUNET_free (tr);
950       continue;
951     }
952     GNUNET_CONTAINER_DLL_insert (handle->ticket_refs_head,
953                                  handle->ticket_refs_tail, tr);
954     handle->ticket_ref_num++;
955   }
956   tr = GNUNET_new (struct TicketReference);
957   tr->ticket = handle->ticket;
958   tr->attrs = GNUNET_RECLAIM_ATTRIBUTE_list_dup (handle->attrs);
959   GNUNET_CONTAINER_DLL_insert (handle->ticket_refs_head,
960                                handle->ticket_refs_tail, tr);
961   handle->ticket_ref_num++;
962   GNUNET_SCHEDULER_add_now (&update_ticket_refs, handle);
963 }
964
965
966 /**
967  * TODO maybe we should cleanup the ATTRREFS here?
968  */
969 static void
970 ticket_lookup_error_cb (void *cls)
971 {
972   struct TicketIssueHandle *handle = cls;
973   handle->ns_qe = NULL;
974   handle->cb (handle->cb_cls, &handle->ticket, GNUNET_SYSERR,
975               "Error checking for ticketsin GNS\n");
976   cleanup_issue_handle (handle);
977 }
978
979 static void
980 store_ticket_issue_cont (void *cls, int32_t success, const char *emsg)
981 {
982   struct TicketIssueHandle *handle = cls;
983
984   handle->ns_qe = NULL;
985   if (GNUNET_SYSERR == success) {
986     handle->cb (handle->cb_cls, &handle->ticket, GNUNET_SYSERR,
987                 "Error storing AuthZ ticket in GNS");
988     return;
989   }
990   /* First, local references to tickets */
991   handle->ns_qe = GNUNET_NAMESTORE_records_lookup (
992       nsh, &handle->identity, GNUNET_GNS_EMPTY_LABEL_AT,
993       &ticket_lookup_error_cb, handle, &ticket_lookup_cb, handle);
994 }
995
996
997 static void
998 issue_ticket (struct TicketIssueHandle *ih)
999 {
1000   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le;
1001   struct GNUNET_GNSRECORD_Data *attrs_record;
1002   char *label;
1003   size_t list_len = 0;
1004   int i;
1005
1006   for (le = ih->attrs->list_head; NULL != le; le = le->next)
1007     list_len++;
1008
1009   attrs_record =
1010       GNUNET_malloc (list_len * sizeof (struct GNUNET_GNSRECORD_Data));
1011   i = 0;
1012   for (le = ih->attrs->list_head; NULL != le; le = le->next) {
1013     attrs_record[i].data = &le->claim->id;
1014     attrs_record[i].data_size = sizeof (le->claim->id);
1015     attrs_record[i].expiration_time = GNUNET_TIME_UNIT_DAYS.rel_value_us;
1016     attrs_record[i].record_type = GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR_REF;
1017     attrs_record[i].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
1018     i++;
1019   }
1020
1021   label =
1022       GNUNET_STRINGS_data_to_string_alloc (&ih->ticket.rnd, sizeof (uint64_t));
1023   // Publish record
1024   ih->ns_qe = GNUNET_NAMESTORE_records_store (nsh, &ih->identity, label,
1025                                               list_len, attrs_record,
1026                                               &store_ticket_issue_cont, ih);
1027   GNUNET_free (attrs_record);
1028   GNUNET_free (label);
1029 }
1030
1031
1032 void
1033 RECLAIM_TICKETS_issue (const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity,
1034                        const struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs,
1035                        const struct GNUNET_CRYPTO_EcdsaPublicKey *audience,
1036                        RECLAIM_TICKETS_TicketResult cb, void *cb_cls)
1037 {
1038   struct TicketIssueHandle *tih;
1039   tih = GNUNET_new (struct TicketIssueHandle);
1040   tih->cb = cb;
1041   tih->cb_cls = cb_cls;
1042   tih->attrs = GNUNET_RECLAIM_ATTRIBUTE_list_dup (attrs);
1043   tih->identity = *identity;
1044   GNUNET_CRYPTO_ecdsa_key_get_public (identity, &tih->ticket.identity);
1045   tih->ticket.rnd =
1046       GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_STRONG, UINT64_MAX);
1047   tih->ticket.audience = *audience;
1048   issue_ticket (tih);
1049 }
1050
1051 /************************************
1052  * Ticket iteration
1053  ************************************/
1054
1055 static void
1056 cleanup_iter (struct RECLAIM_TICKETS_Iterator *iter)
1057 {
1058   struct TicketReference *tr;
1059   struct TicketReference *tr_tmp;
1060   if (NULL != iter->ns_qe)
1061     GNUNET_NAMESTORE_cancel (iter->ns_qe);
1062   for (tr = iter->tickets_head; NULL != tr;) {
1063     if (NULL != tr->attrs)
1064       GNUNET_RECLAIM_ATTRIBUTE_list_destroy (tr->attrs);
1065     tr_tmp = tr;
1066     tr = tr->next;
1067     GNUNET_free (tr_tmp);
1068   }
1069   GNUNET_free (iter);
1070 }
1071
1072 static void
1073 do_cleanup_iter (void *cls)
1074 {
1075   struct RECLAIM_TICKETS_Iterator *iter = cls;
1076   cleanup_iter (iter);
1077 }
1078
1079 /**
1080  * Perform ticket iteration step
1081  *
1082  * @param ti ticket iterator to process
1083  */
1084 static void
1085 run_ticket_iteration_round (struct RECLAIM_TICKETS_Iterator *iter)
1086 {
1087   struct TicketReference *tr;
1088   if (NULL == iter->tickets_head) {
1089     // No more tickets
1090     iter->cb (iter->cb_cls, NULL);
1091     GNUNET_SCHEDULER_add_now (&do_cleanup_iter, iter);
1092     return;
1093   }
1094   tr = iter->tickets_head;
1095   GNUNET_CONTAINER_DLL_remove (iter->tickets_head, iter->tickets_tail, tr);
1096   iter->cb (iter->cb_cls, &tr->ticket);
1097   if (NULL != tr->attrs)
1098     GNUNET_RECLAIM_ATTRIBUTE_list_destroy (tr->attrs);
1099   GNUNET_free (tr);
1100 }
1101
1102 static void
1103 collect_tickets_cb (void *cls, const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
1104                     const char *label, unsigned int rd_count,
1105                     const struct GNUNET_GNSRECORD_Data *rd)
1106 {
1107   struct RECLAIM_TICKETS_Iterator *iter = cls;
1108   struct TicketReference *tr;
1109   iter->ns_qe = NULL;
1110
1111   for (int i = 0; i < rd_count; i++) {
1112     if (GNUNET_GNSRECORD_TYPE_RECLAIM_TICKETREF != rd[i].record_type)
1113       continue;
1114     tr = GNUNET_new (struct TicketReference);
1115     memcpy (&tr->ticket, rd[i].data, sizeof (struct GNUNET_RECLAIM_Ticket));
1116     if (0 != memcmp (&tr->ticket.identity, &iter->identity_pub,
1117                      sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) {
1118       // Not our ticket
1119       GNUNET_free (tr);
1120       continue;
1121     }
1122     GNUNET_CONTAINER_DLL_insert (iter->tickets_head, iter->tickets_tail, tr);
1123   }
1124   run_ticket_iteration_round (iter);
1125 }
1126
1127 static void
1128 collect_tickets_error_cb (void *cls)
1129 {
1130   struct RECLAIM_TICKETS_Iterator *iter = cls;
1131   iter->ns_qe = NULL;
1132   iter->cb (iter->cb_cls, NULL);
1133   cleanup_iter (iter);
1134 }
1135
1136 void
1137 RECLAIM_TICKETS_iteration_next (struct RECLAIM_TICKETS_Iterator *iter)
1138 {
1139   run_ticket_iteration_round (iter);
1140 }
1141
1142 void
1143 RECLAIM_TICKETS_iteration_stop (struct RECLAIM_TICKETS_Iterator *iter)
1144 {
1145   cleanup_iter (iter);
1146 }
1147
1148 struct RECLAIM_TICKETS_Iterator *
1149 RECLAIM_TICKETS_iteration_start (
1150     const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity,
1151     RECLAIM_TICKETS_TicketIter cb, void *cb_cls)
1152 {
1153   struct RECLAIM_TICKETS_Iterator *iter;
1154
1155   iter = GNUNET_new (struct RECLAIM_TICKETS_Iterator);
1156   iter->identity = *identity;
1157   GNUNET_CRYPTO_ecdsa_key_get_public (identity, &iter->identity_pub);
1158   iter->cb = cb;
1159   iter->cb_cls = cb_cls;
1160   iter->ns_qe = GNUNET_NAMESTORE_records_lookup (
1161       nsh, identity, GNUNET_GNS_EMPTY_LABEL_AT, &collect_tickets_error_cb, iter,
1162       &collect_tickets_cb, iter);
1163   return iter;
1164 }
1165
1166
1167 int
1168 RECLAIM_TICKETS_init (const struct GNUNET_CONFIGURATION_Handle *c)
1169 {
1170   // Connect to identity and namestore services
1171   nsh = GNUNET_NAMESTORE_connect (c);
1172   if (NULL == nsh) {
1173     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
1174                          "error connecting to namestore");
1175     return GNUNET_SYSERR;
1176   }
1177   gns = GNUNET_GNS_connect (c);
1178   if (NULL == gns) {
1179     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "error connecting to gns");
1180     return GNUNET_SYSERR;
1181   }
1182   stats = GNUNET_STATISTICS_create ("reclaim", c);
1183   return GNUNET_OK;
1184 }
1185
1186 void
1187 RECLAIM_TICKETS_deinit (void)
1188 {
1189   if (NULL != nsh)
1190     GNUNET_NAMESTORE_disconnect (nsh);
1191   nsh = NULL;
1192   if (NULL != gns)
1193     GNUNET_GNS_disconnect (gns);
1194   gns = NULL;
1195   if (NULL != stats) {
1196     GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1197     stats = NULL;
1198   }
1199 }