summaryrefslogtreecommitdiff
path: root/doc/sphinx/reducer.rst
blob: 320db48bfd1610463269051356e772843d527aaf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
..
  This file is part of Anastasis
  Copyright (C) 2019-2021 Anastasis SARL

  Anastasis is free software; you can redistribute it and/or modify it under the
  terms of the GNU Affero General Public License as published by the Free Software
  Foundation; either version 2.1, or (at your option) any later version.

  Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY
  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.

  You should have received a copy of the GNU Affero General Public License along with
  Anastasis; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>

  @author Christian Grothoff
  @author Dominik Meister
  @author Dennis Neufeld

-----------
Reducer API
-----------

This section describes the Anastasis Reducer API which is used by client applications
to store or load the different states the client application can have.
The reducer takes a state_ in JSON syntax and returns the new state in JSON syntax.

For example a **state** may take the following structure:

.. code-block:: json

    {
      "backup_state": "CONTINENT_SELECTING",
      "continents": [
        "Europe",
        "North_America"
      ]
    }

The new state depends on the previous one and on the transition action_ with its
arguments given to the reducer. A **transition argument** also is a statement in JSON syntax:

.. code-block:: json

    {
      "continent": "Europe"
    }

The new state returned by the reducer with the state and transition argument defined
above would look like following for the transition action_ ``select_continent``:

.. code-block:: json

  {
    "backup_state": "COUNTRY_SELECTING",
    "continents": [
      "Europe",
      "North_America"
    ],
    "selected_continent": "Europe",
    "countries": [
      {
        "code": "ch",
        "name": "Switzerland",
        "continent": "Europe",
        "name_i18n": {
          "de_DE": "Schweiz",
          "de_CH": "Schwiiz",
          "fr": "Suisse",
          "en": "Swiss"
        },
        "currency": "CHF"
      },
      {
        "code": "de",
        "name": "Germany",
        "continent": "Europe",
        "continent_i18n": {
          "de": "Europa"
        },
        "name_i18n": {
          "de_DE": "Deutschland",
          "de_CH": "Deutschland",
          "fr": "Allemagne",
          "en": "Germany"
        },
        "currency": "EUR"
      }
    ]
  }

States
^^^^^^

Overall, the reducer knows the following states:

  - **ERROR**: The transition led to an error. No further transitions are possible from
               this state, but the client may want to continue from a previous state.
  - **CONTINENT_SELECTING**: The user should specify the continent where they are living,
               so that we can show a list of countries to choose from.
  - **COUNTRY_SELECTING**: The user should specify the country where they are living,
               so that we can determine appropriate attributes, currencies and Anastasis
               providers.
  - **USER_ATTRIBUTES_COLLECTING**: The user should provide the country-specific personal
               attributes.
  - **AUTHENTICATIONS_EDITING**: The user should add authentication methods to be used
               during recovery.
  - **POLICIES_REVIEWING**: The user should review the recovery policies.
  - **SECRET_EDITING**: The user should edit the secret to be backed up.
  - **TRUTHS_PAYING**: The user needs to pay for one or more uploads of data associated
               with an authentication method.
  - **POLICIES_PAYING**: The user needs to pay for storing the recovery policy document.
  - **BACKUP_FINISHED**: A backup has been successfully generated.
  - **SECRET_SELECTING**: The user needs to select a recovery policy document with
               the secret that is to be recovered.
  - **CHALLENGE_SELECTING**: The user needs to select an authorization challenge to
               proceed with recovery.
  - **CHALLENGE_PAYING**: The user needs to pay to proceed with the authorization challenge.
  - **CHALLENGE_SOLVING**: The user needs to solve the authorization challenge.
  - **RECOVERY_FINISHED**: The secret of the user has been recovered.

State names:

  - In SELECTING-states, the user has to choose one value out of a predefined set of values (for example a continent out of a set of continents).
  - In COLLECTING-states, the user has to give certain values.
  - In EDITING-states, the user is free to choose which values he wants to give.
  - In REVEIWING-states, the user may make a few choices, but primarily is expected to affirm something.
  - in PAYING-states, the user must make a payment.
  - in FINISHED-states, the operation has definitively concluded.


Backup Reducer
^^^^^^^^^^^^^^
.. _state:
.. _action:
.. figure:: anastasis_reducer_backup.png
    :name: fig-anastasis_reducer_backup
    :alt: fig-anastasis_reducer_backup
    :scale: 75 %
    :align: center

    Backup states and their transitions.


The illustration above shows the different states the reducer can have during a backup
process.


Recovery Reducer
^^^^^^^^^^^^^^^^
.. figure:: anastasis_reducer_recovery.png
    :name: fig-anastasis_reducer_recovery
    :alt: fig-anastasis_reducer_recovery
    :scale: 75 %
    :align: center

    Recovery states and their transitions.


The illustration above shows the different states the reducer can have during a recovery
process.


Reducer transitions
^^^^^^^^^^^^^^^^^^^
In the following, the individual transitions will be specified in more detail.
Note that we only show fields added by the reducer, typically the previous
state is preserved to enable "back" transitions to function smoothly.


Initial state
-------------

The initial states for backup and recovery processes are:

**Initial backup state:**

.. code-block:: json

    {
      "backup_state": "CONTINENT_SELECTING",
      "continents": [
        "Europe",
        "North America"
      ]
    }


**Initial recovery state:**

.. code-block:: json

    {
      "recovery_state": "CONTINENT_SELECTING",
      "continents": [
        "Europe",
        "North America"
      ]
    }

Here, "continents" is an array of English strings with the names of the
continents which contain countries for which Anastasis could function (based
on having providers that are known to operate and rules being provided for
user attributes from those countries).

For internationalization, another field ``continents_i18n`` may be present.
This field would be a map of language names to arrays of translated
continent names:

.. code-block:: json

    {
      "recovery_state": "CONTINENT_SELECTING",
      "continents": [
        "Europe",
        "North America"
      ]
      "continents_i18n":
      {
        "de_DE" : [
          "Europa",
          "Nordamerika"
          ],
        "de_CH" : [
          "Europa",
          "Nordamerika"
          ]
      }
    }

Translations must be given in the same order as the main English array.


Common transitions
------------------

**select_continent:**

Here the user specifies the continent they live on.  Arguments (example):

.. code-block:: json

    {
      "continent": "Europe"
    }

The continent must be given using the English name from the ``continents`` array.
Using a translated continent name is invalid and may result in failure.

The reducer returns an updated state with a list of countries to choose from,
for example:

.. code-block:: json

    {
      "backup_state": "COUNTRY_SELECTING",
      "selected_continent": "Europe",
      "countries": [
        {
          "code": "ch",
          "name": "Switzerland",
          "continent": "Europe",
          "name_i18n": {
            "de_DE": "Schweiz",
            "de_CH": "Schwiiz",
            "fr": "Suisse",
            "en": "Swiss"
          },
          "currency": "CHF"
        },
        {
          "code": "de",
          "name": "Germany",
          "continent": "Europe",
          "continent_i18n": {
            "de": "Europa"
          },
          "name_i18n": {
            "de_DE": "Deutschland",
            "de_CH": "Deutschland",
            "fr": "Allemagne",
            "en": "Germany"
          },
          "currency": "EUR"
        }
      ]
    }

Here ``countries`` is an array of countries on the ``selected_continent``.  For
each country, the ``code`` is the ISO 3166-1 alpha-2 country code.  The
``continent`` is only present because some countries span continents, the
information is redundant and will always match ``selected_continent``.  The
``name`` is the name of the country in English, internationalizations of the
name may be provided in ``name_i18n``.  The ``currency`` is **an** official
currency of the country, if a country has multiple currencies, it may appear
multiple times in the list.  In this case, the user should select the entry
with the currency they intend to pay with.  It is also possible for users
to select a currency that does not match their country, but user interfaces
should by default try to use currencies that match the user's residence.


**select_country:**

Selects the country (via the country code) and specifies the currency.
The latter is needed as some countries have more than one currency,
and some use-cases may also involve users insisting on paying with
foreign currency.

Arguments (example):

.. code-block:: json

    {
      "country_code": "de",
      "currency": "EUR"
    }

The ``country_code`` must be an ISO 3166-1 alpha-2 country code from
the array of ``countries`` of the reducer's state.  The ``currency``
field must be a valid currency accepted by the Taler payment system.

The reducer returns a new state with the list of attributes the
user is expected to provide, as well as possible authentication
providers that accept payments in the selected currency:

.. code-block:: json

    {
      "backup_state": "USER_ATTRIBUTES_COLLECTING",
      "selected_country": "de",
      "currency": "EUR",
      "required_attributes": [
        {
          "type": "string",
          "name": "full_name",
          "label": "Full name",
          "label_i18n": {
            "de_DE": "Vollstaendiger Name",
            "de_CH": "Vollstaendiger. Name",
            "fr": "Nom complet",
            "en": "Full name"
          },
          "widget": "anastasis_gtk_ia_full_name",
          "uuid" : "9e8f463f-575f-42cb-85f3-759559997331"
        },
        {
          "type": "date",
          "name": "birthdate",
          "label": "Birthdate",
          "label_i18n": {
            "de_DE": "Geburtsdatum",
            "de_CH": "Geburtsdatum",
            "fr": "Date de naissance",
            "en": "Birthdate"
          },
          "uuid" : "83d655c7-bdb6-484d-904e-80c1058c8854"
          "widget": "anastasis_gtk_ia_birthdate"
        },
 	    {
	       "type": "string",
           "name": "tax_number",
	       "label": "Taxpayer identification number",
	       "label_i18n":{
		     "de_DE": "Steuerliche Identifikationsnummer",
	  	     "de_CH": "Steuerliche Identifikationsnummer",
	   	     "en": "German taxpayer identification number"
	       },
	       "widget": "anastasis_gtk_ia_tax_de",
           "uuid": "dae48f85-e3ff-47a4-a4a3-ed981ed8c3c6",
	       "validation-regex": "^[0-9]{11}$",
	       "validation-logic": "DE_TIN_check"
   	    },
        {
          "type": "string",
          "name": "social_security_number",
          "label": "Social security number",
          "label_i18n": {
            "de_DE": "Sozialversicherungsnummer",
            "de_CH": "Sozialversicherungsnummer",
            "fr": "Numéro de sécurité sociale",
            "en": "Social security number"
          },
          "widget": "anastasis_gtk_ia_ssn",
   	      "validation-regex": "^[0-9]{8}[[:upper:]][0-9]{3}$",
	      "validation-logic": "DE_SVN_check"
          "optional" : true
      }
      ],
      "authentication_providers": {
        "http://localhost:8089/": {
          "http_status": 200,
          "methods": [
            { "type" : "question",
              "usage_fee" : "EUR:0.0" },
            { "type" : "sms",
              "usage_fee" : "EUR:0.5" }
          ],
          "annual_fee": "EUR:4.99",
          "truth_upload_fee": "EUR:4.99",
          "liability_limit": "EUR:1",
          "currency": "EUR",
          "truth_lifetime": { "d_ms" : 50000000 },
          "storage_limit_in_megabytes": 1,
          "provider_name": "Anastasis 4",
          "salt": "CXAPCKSH9D3MYJTS9536RHJHCW"
        },
        "http://localhost:8088/": {
          "http_status": 200,
          "methods": [
            { "type" : "question",
              "usage_fee" : "EUR:0.01" },
            { "type" : "sms",
              "usage_fee" : "EUR:0.55" }
          ],
          "annual_fee": "EUR:0.99",
          "truth_upload_fee": "EUR:3.99",
          "liability_limit": "EUR:1",
          "currency": "EUR",
          "truth_lifetime": { "d_ms" : 50000000 },
          "storage_limit_in_megabytes": 1,
          "provider_name": "Anastasis 4",
          "salt": "CXAPCKSH9D3MYJTS9536RHJHCW"
        }
      }
    }

The array of ``required_attributes`` contains attributes about the user
that must be provided includes:

  - **type**: The type of the attribute, for now only ``string`` and ``date`` are
    supported.
  - **name**: The name of the attribute, this is the key under which the
    attribute value must be provided later. The name must be unique per response.
  - **label**: A human-readable description of the attribute in English.
    Translated descriptions may be provided under **label_i18n**.
  - **uuid**: A UUID that uniquely identifies identical attributes across
    different countries. Useful to preserve values should the user enter
    some attributes, and then switch to another country.  Note that
    attributes must not be preserved if they merely have the same **name**,
    only the **uuid** will be identical if the semantics is identical.
  - **widget**: An optional name of a widget that is known to nicely render
    the attribute entry in user interfaces where named widgets are
    supported.
  - **validation-regex**: An optional extended POSIX regular expression
    that is to be used to validate (string) inputs to ensure they are
    well-formed.
  - **validation-logic**: Optional name of a function that should be called
    to validate the input.  If the function is not known to the particular
    client, the respective validation can be skipped (at the expense of
    typos by users not being detected, possibly rendering secrets
    irrecoverable).
  - **optional**: Optional boolean field that, if ``true``, indicates that
    this attribute is not actually required but optional and users MAY leave
    it blank in case they do not have the requested information. Used for
    common fields that apply to some large part of the population but are
    not sufficiently universal to be actually required.

The authentication providers are listed under a key that is the
base URL of the service. For each provider, the following
information is provided if the provider was successfully contacted:

  - **http_status**: HTTP status code, always ``200`` on success.
  - **methods**: Array of authentication methods supported by this
    provider. Includes the **type** of the authentication method
    and the **usage_fee** (how much the user must pay for authorization
    using this method during recovery).
  - **annual_fee**: Fee the provider charges to store the recovery
    policy for one year.
  - **truth_upload_fee**: Fee the provider charges to store a key share.
  - **liability_limit**: Amount the provider can be held liable for in
    case a key share or recovery document cannot be recovered due to
    provider failures.
  - **currency**: Currency in which the provider wants to be paid,
    will match all of the fees.
  - **storage_limit_in_megabytes**: Maximum size of an upload (for
    both recovery document and truth data) in megabytes.
  - **provider_name**: Human-readable name of the provider's business.
  - **salt**: Salt value used by the provider, used to derive the
    user's identity at this provider. Should be unique per provider,
    and must never change for a given provider.  The salt is
    base32 encoded.

If contacting the provider failed, the information returned is:

  - **http_status**: HTTP status code (if available, possibly 0 if
    we did not even obtain an HTTP response).
  - **error_code**: Taler error code, never 0.


**add_provider**:

This operation can be performed in state ``USER_ATTRIBUTES_COLLECTING``.  It
adds one or more Anastasis providers to the list of providers the reducer
should henceforth consider.  Note that removing providers is not possible at
this time.

Here, the client must provide an array with the base URLs of the
providers to add, for example:

.. code-block:: json

    {
      "urls": [
        "http://localhost:8888/",
        "http://localhost:8089/"
      ]
    }

Note that existing providers will remain in the state.  The following is an
example for an expected new state where the service on port 8089 is
unreachable, the service on port 8088 was previously known, and service on
port 8888 was now added:

.. code-block:: json

    {
      "backup_state": "USER_ATTRIBUTES_COLLECTING",
      "authentication_providers": {
        "http://localhost:8089/": {
          "error_code": 11,
          "http_status": 0
        },
        "http://localhost:8088/": {
          "http_status": 200,
          "methods": [
            { "type" : "question",
              "usage_fee" : "EUR:0.01" },
            { "type" : "sms",
              "usage_fee" : "EUR:0.55" }
          ],
          "annual_fee": "EUR:0.99",
          "truth_upload_fee": "EUR:3.99",
          "liability_limit": "EUR:1",
          "currency": "EUR",
          "truth_lifetime": { "d_ms" : 50000000 },
          "storage_limit_in_megabytes": 1,
          "provider_name": "Anastasis 4",
          "salt": "CXAPCKSH9D3MYJTS9536RHJHCW"
        }
        "http://localhost:8888/": {
          "methods": [
            { "type" : "question",
              "usage_fee" : "EUR:0.01" },
            { "type" : "sms",
              "usage_fee" : "EUR:0.55" }
          ],
          "annual_fee": "EUR:0.99",
          "truth_upload_fee": "EUR:3.99",
          "liability_limit": "EUR:1",
          "currency": "EUR",
          "truth_lifetime": { "d_ms" : 50000000 },
          "storage_limit_in_megabytes": 1,
          "provider_name": "Anastasis 42",
          "salt": "BXAPCKSH9D3MYJTS9536RHJHCX"
        }
      }
    }



Backup transitions
------------------

**enter_user_attributes:**

This transition provides the user's personal attributes. The specific set of
attributes required depends on the country of residence of the user.  Some
attributes may be optional, in which case they should be omitted entirely
(that is, not simply be set to ``null`` or an empty string).  Example
arguments would be:

.. code-block:: json

    {
      "identity_attributes": {
        "full_name": "Max Musterman",
        "social_security_number": "123456789",
        "birthdate": "2000-01-01",
        "birthplace": "Earth"
     }
    }

Note that at this stage, the state machines between backup and
recovery diverge and the ``recovery_state`` will begin to look
very different from the ``backup_state``.

For backups, if all required attributes are present, the reducer will
transition to an ``AUTHENTICATIONS_EDITING`` state with the attributes added
to it:

.. code-block:: json

    {
      "backup_state": "AUTHENTICATIONS_EDITING",
      "identity_attributes": {
        "full_name": "Max Musterman",
        "social_security_number": "123456789",
        "birthdate": "2000-01-01",
        "birthplace": "Earth"
      }
    }

If required attributes are missing, do not match the required regular
expression, or fail the custom validation logic, the reducer SHOULD transition
to an error state indicating what was wrong about the input.  A reducer that
does not support some specific validation logic MAY accept the invalid input
and proceed anyway.  The error state will include a Taler error code that
is specific to the failure, and optional details.  Example:

.. code-block:: json

    {
      "backup_state": "ERROR",
      "code": 8404,
      "hint": "An input did not match the regular expression.",
      "detail": "social_security_number"
    }

Clients may safely repeat this transition to validate the user's inputs
until they satisfy all of the constraints.  This way, the user interface
does not have to perform the input validation directly.


**add_authentication**:

This transition adds an authentication method.  The method must be supported
by one or more providers that are included in the current state.  Adding an
authentication method requires specifying the ``type`` and ``instructions`` to
be given to the user.  The ``challenge`` is encrypted and stored at the
Anastasis provider. The specific semantics of the value depend on the
``type``.  Typical challenges values are a phone number (to send an SMS to),
an e-mail address (to send a PIN code to) or the answer to a security
question.  Note that these challenge values will still be encrypted (and
possibly hashed) before being given to the Anastasis providers.

Note that the ``challenge`` must be given in Crockford Base32 encoding, as it
MAY include binary data (such as a photograph of the user).  In the latter
case, the optional ``mime_type`` field must be provided to give the MIME type
of the value encoded in ``challenge``.

.. code-block:: json

    {
      "authentication_method":
      {
        "type": "question",
        "mime_type" : "text/plain",
        "instructions" : "What is your favorite GNU package?",
        "challenge" : "E1QPPS8A",
      }
    }

If the information provided is valid, the reducer will add the new
authentication method to the array of authentication methods:

.. code-block:: json

    {
      "backup_state": "AUTHENTICATIONS_EDITING",
      "authentication_methods": [
        {
          "type": "question",
          "mime_type" : "text/plain",
          "instructions" : "What is your favorite GNU package?",
          "challenge" : "E1QPPS8A",
        },
        {
          "type": "email",
          "instructions" : "E-mail to user@*le.com",
          "challenge": "ENSPAWJ0CNW62VBGDHJJWRVFDM50"
        }
      ]
    }


**delete_authentication**:

This transition can be used to remove an authentication method from the
array of authentication methods. It simply requires the index of the
authentication method to remove. Note that the array is 0-indexed:

.. code-block:: json

    {
      "authentication_method": 1
    }

Assuming we begin with the state from the example above, this would
remove the ``email`` authentication method, resulting in the following
response:

.. code-block:: json

    {
      "backup_state": "AUTHENTICATIONS_EDITING",
      "authentication_methods": [
        {
          "type": "question",
          "mime_type" : "text/plain",
          "instructions" : "What is your favorite GNU package?",
          "challenge" : "gdb",
        }
      ]
    }

If the index is invalid, the reducer will instead
transition into an ``ERROR`` state.


**next** (from ``AUTHENTICATIONS_EDITING``):

This transition confirms that the user has finished adding (or removing)
authentication methods, and that the system should now automatically compute
a set of reasonable recovery policies.

This transition does not take any mandatory arguments.  Optional arguments can
be provided to upload the recovery document only to a specific subset of the
providers:

.. code-block:: json

    {
      "providers": [
        "http://localhost:8088/",
        "http://localhost:8089/"
      ]
    }

The resulting state provides the suggested recovery policies in a way suitable
for presentation to the user:

.. code-block:: javascript

    {
      "backup_state": "POLICIES_REVIEWING",
      "policy_providers" : [
          { "provider_url" : "http://localhost:8088/" },
          { "provider_url" : "http://localhost:8089/" }
      ],
      "policies": [
        {
          "methods": [
            {
              "authentication_method": 0,
              "provider": "http://localhost:8088/"
            },
            {
              "authentication_method": 1,
              "provider": "http://localhost:8089/"
            },
            {
              "authentication_method": 2,
              "provider": "http://localhost:8087/"
            }
          ]
        },
        {
          "methods": [
            {
              "authentication_method": 0,
              "provider": "http://localhost:8088/"
            },
            {
              "authentication_method": 1,
              "provider": "http://localhost:8089/"
            },
            {
              "authentication_method": 3,
              "provider": "http://localhost:8089/"
            }
          ]
        }
      ]
    }

For each recovery policy, the state includes the specific details of which
authentication ``methods`` must be solved to recovery the secret using this
policy.  The ``methods`` array specifies the index of the
``authentication_method`` in the ``authentication_methods`` array, as well as
the provider that was selected to supervise this authentication.

If no authentication method was provided, the reducer will transition into an
``ERROR`` state instead of suggesting policies.


**add_policy**:

Using this transition, the user can add an additional recovery policy to the
state.  The argument format is the same that is used in the existing state.
An example for a possible argument would thus be:

.. code-block:: javascript

    {
      "policy": [
        {
          "authentication_method": 1,
          "provider": "http://localhost:8088/"
        },
        {
          "authentication_method": 3,
          "provider": "http://localhost:8089/"
        }
      ]
    }

Note that the specified providers must already be in the
``authentication_providers`` of the state. You cannot add new providers at
this stage.  The reducer will simply attempt to append the suggested policy to
the "policies" array, returning an updated state:

.. code-block:: json

    {
      "backup_state": "POLICIES_REVIEWING",
      "policies": [
        {
          "methods": [
            {
              "authentication_method": 0,
              "provider": "http://localhost:8089/"
            },
            {
              "authentication_method": 1,
              "provider": "http://localhost:8088/"
            }
          ]
        },
        {
          "methods": [
            {
              "authentication_method": 0,
              "provider": "http://localhost:8089/"
            },
            {
              "authentication_method": 2,
              "provider": "http://localhost:8088/"
            }
          ]
        },
        {
          "methods": [
            {
              "authentication_method": 1,
              "provider": "http://localhost:8089/"
            },
            {
              "authentication_method": 2,
              "provider": "http://localhost:8088/"
            }
          ]
        },
        {
          "methods": [
            {
              "authentication_method": 1,
              "provider": "http://localhost:8088/"
            },
            {
              "authentication_method": 3,
              "provider": "http://localhost:8089/"
            }
          ]
        }
      ]
    }

If the new policy is invalid, for example because it adds an unknown
authentication method, or the selected provider does not support the type of
authentication, the reducer will transition into an ``ERROR`` state instead of
adding the new policy.


**update_policy**:

Using this transition, the user can modify an existing recovery policy
in the state.
The argument format is the same that is used in **add_policy**,
except there is an additional key ``policy_index`` which
identifies the policy to modify.
An example for a possible argument would thus be:

.. code-block:: javascript

    {
      "policy_index" : 1,
      "policy": [
        {
          "authentication_method": 1,
          "provider": "http://localhost:8088/"
        },
        {
          "authentication_method": 3,
          "provider": "http://localhost:8089/"
        }
      ]
    }

If the new policy is invalid, for example because it adds an unknown
authentication method, or the selected provider does not support the type of
authentication, the reducer will transition into an ``ERROR`` state instead of
modifying the policy.



**delete_policy:**

This transition allows the deletion of a recovery policy. The argument
simply specifies the index of the policy to delete, for example:

.. code-block:: json

    {
      "policy_index": 3
    }

Given as input the state from the example above, the expected new state would
be:

.. code-block:: json

    {
      "backup_state": "POLICIES_REVIEWING",
      "policies": [
        {
          "methods": [
            {
              "authentication_method": 0,
              "provider": "http://localhost:8089/"
            },
            {
              "authentication_method": 1,
              "provider": "http://localhost:8088/"
            }
          ]
        },
        {
          "methods": [
            {
              "authentication_method": 0,
              "provider": "http://localhost:8089/"
            },
            {
              "authentication_method": 2,
              "provider": "http://localhost:8088/"
            }
          ]
        },
        {
          "methods": [
            {
              "authentication_method": 1,
              "provider": "http://localhost:8089/"
            },
            {
              "authentication_method": 2,
              "provider": "http://localhost:8088/"
            }
          ]
        }
      ]
    }

If the index given is invalid, the reducer will transition into an ``ERROR`` state
instead of deleting a policy.


**delete_challenge:**

This transition allows the deletion of an individual
challenge from a recovery policy. The argument
simply specifies the index of the policy and challenge
to delete, for example:

.. code-block:: json

    {
      "policy_index": 1,
      "challenge_index" : 1
    }

Given as input the state from the example above, the expected new state would
be:

.. code-block:: json

    {
      "backup_state": "POLICIES_REVIEWING",
      "policies": [
        {
          "methods": [
            {
              "authentication_method": 0,
              "provider": "http://localhost:8089/"
            },
            {
              "authentication_method": 1,
              "provider": "http://localhost:8088/"
            }
          ]
        },
        {
          "methods": [
            {
              "authentication_method": 0,
              "provider": "http://localhost:8089/"
            }
          ]
        },
        {
          "methods": [
            {
              "authentication_method": 1,
              "provider": "http://localhost:8089/"
            },
            {
              "authentication_method": 2,
              "provider": "http://localhost:8088/"
            }
          ]
        }
      ]
    }

If the index given is invalid, the reducer will transition into an ``ERROR`` state
instead of deleting a challenge.


**next** (from ``POLICIES_REVIEWING``):

Using this transition, the user confirms that the policies in the current
state are acceptable. The transition does not take any arguments.

The reducer will simply transition to the ``SECRET_EDITING`` state:

.. code-block:: json

    {
      "backup_state": "SECRET_EDITING",
      "upload_fees" : [ "KUDOS:42" ],
      "expiration" : { "t_ms" : 1245362362 }
    }

Here, ``upload_fees`` is an array of applicable upload fees for the
given policy expiration time. This is an array because fees could
be in different currencies.  The final cost may be lower if the
user already paid for some of the time.

If the array of ``policies`` is currently empty, the reducer will transition
into an ``ERROR`` state instead of allowing the user to continue.


**enter_secret:**

This transition provides the reducer with the actual core ``secret`` of the user
that Anastasis is supposed to backup (and possibly recover). The argument is
simply the Crockford-Base32 encoded ``value`` together with its ``mime`` type, or a ``text`` field with a human-readable secret text.
For example:

.. code-block:: javascript

    {
      "secret": {
         "value": "EDJP6WK5EG50",
         "mime" : "text/plain"
      },
      "expiration" : { "t_ms" : 1245362362 }
    }

If the application is unaware of the format, it set the ``mime`` field to ``null``.
The ``expiration`` field is optional.

The reducer remains in the ``SECRET_EDITING`` state, but now the secret and
updated expiration time are part of the state and the cost calculations will
be updated.

.. code-block:: json

    {
      "backup_state": "SECRET_EDITING",
      "core_secret" : {
         "value": "EDJP6WK5EG50",
         "mime" : "text/plain"
      },
      "expiration" : { "t_ms" : 1245362362 },
      "upload_fees" : [ "KUDOS:42" ]
    }


**clear_secret:**

This transition removes the core secret from the state.  It is simply a
convenience function to undo ``enter_secret`` without providing a new value
immediately. The transition takes no arguments.  The resuting state will no
longer have the ``core_secret`` field, and be otherwise unchanged. Calling
**clear_secret** on a state without a ``core_secret`` will result in an error.


**enter_secret_name:**

This transition provides the reducer with a name for the core ``secret`` of the user. This name will be given to the user as a hint when seleting a recovery policy document during recovery, prior to satisfying any of the challenges. The argument simply contains the name for the secret.
Applications that have built-in support for Anastasis MUST prefix the
secret name with an underscore and an application-specific identifier
registered in GANA so that they can use recognize their own backups.
An example argument would be:

.. code-block:: javascript

    {
      "name": "_TALERWALLET_MyPinePhone",
    }

Here, ``MyPinePhone`` might be chosen by the user to identify the
device that was being backed up.

The reducer remains in the ``SECRET_EDITING`` state, but now the
secret name is updated:

.. code-block:: json

    {
      "secret_name" : "_TALERWALLET_MyPinePhone"
    }


**update_expiration:**

This transition asks the reducer to change the desired expiration time
and to update the associated cost. For example:

.. code-block:: javascript

    {
      "expiration" : { "t_ms" : 1245362362 }
    }

The reducer remains in the ``SECRET_EDITING`` state, but the
expiration time and cost calculation will be updated.

.. code-block:: json

    {
      "backup_state": "SECRET_EDITING",
      "expiration" : { "t_ms" : 1245362362 },
      "upload_fees" : [ { "fee": "KUDOS:43" } ]
    }


**next** (from ``SECRET_EDITING``):

Using this transition, the user confirms that the secret and expiration
settings in the current state are acceptable. The transition does not take any
arguments.

If the secret is currently empty, the reducer will transition into an
``ERROR`` state instead of allowing the user to continue.

After adding a secret, the reducer may transition into different states
depending on whether payment(s) are necessary.  If payments are needed, the
``secret`` will be stored in the state under ``core_secret``.  Applications
should be careful when persisting the resulting state, as the ``core_secret``
is not protected in the ``PAYING`` states.  The ``PAYING`` states only differ
in terms of what the payments are for (key shares or the recovery document),
in all cases the state simply includes an array of Taler URIs that refer to
payments that need to be made with the Taler wallet.

If all payments are complete, the reducer will transition into the
``BACKUP_FINISHED`` state and (if applicable) delete the ``core_secret`` as an
additional safety measure.

Example results are thus:

.. code-block:: json

    {
      "backup_state": "TRUTHS_PAYING",
      "secret_name" : "$NAME",
      "core_secret" : { "$anything":"$anything" },
      "payments": [
        "taler://pay/...",
        "taler://pay/..."
      ]
    }

.. code-block:: json

    {
      "backup_state": "POLICIES_PAYING",
      "secret_name" : "$NAME",
      "core_secret" : { "$anything":"$anything" },
      "payments": [
        "taler://pay/...",
        "taler://pay/..."
      ]
    }

.. code-block:: json

    {
      "backup_state": "BACKUP_FINISHED",
      "success_details": {
        "http://localhost:8080/" : {
          "policy_version" : 1,
          "policy_expiration" : { "t_ms" : 1245362362000 }
        },
        "http://localhost:8081/" : {
          "policy_version" : 3,
          "policy_expiration" : { "t_ms" : 1245362362000 }
        }
      }
    }


**pay:**

This transition suggests to the reducer that a payment may have been made or
is immanent, and that the reducer should check with the Anastasis service
provider to see if the operation is now possible.  The operation takes one
optional argument, which is a ``timeout`` value that specifies how long the
reducer may wait (in long polling) for the payment to complete:

.. code-block:: json

    {
      "timeout":  { "d_ms" : 5000 },
    }

The specified timeout is passed on to the Anastasis service provider(s), which
will wait this long before giving up.  If no timeout is given, the check is
done as quickly as possible without additional delays.  The reducer will continue
to either an updated state with the remaining payment requests, to the
``BACKUP_FINISHED`` state (if all payments have been completed and the backup
finished), or into an ``ERROR`` state in case there was an irrecoverable error,
indicating the specific provider and how it failed.  An example for this
final error state would be:

.. code-block:: json

    {
      "backup_state": "ERROR",
      "http_status" : 500,
      "upload_status" : 52,
      "provider_url" : "https://bad.example.com/",
    }

Here, the fields have the following meaning:

  - **http_status** is the HTTP status returned by the Anastasis provider.
  - **upload_status** is the Taler error code return by the provider.
  - **provider_url** is the base URL of the failing provider.

In the above example, 52 would thus imply that the Anastasis provider failed to
store information into its database.


Recovery transitions
--------------------

**enter_user_attributes:**

This transition provides the user's personal attributes. The specific set of
attributes required depends on the country of residence of the user.  Some
attributes may be optional, in which case they should be omitted entirely
(that is, not simply be set to ``null`` or an empty string).  The
arguments are identical to the **enter_user_attributes** transition from
the backup process.  Example arguments would thus be:

.. code-block:: json

    {
      "identity_attributes": {
        "full_name": "Max Musterman",
        "social_security_number": "123456789",
        "birthdate": "2000-01-01",
        "birthplace": "Earth"
      }
    }

However, in contrast to the backup process, the reducer will attempt to
retrieve the latest recovery document from all known providers for the
selected currency given the above inputs.  If a recovery document was found
by any provider, the reducer will attempt to load it and transition to
a state where the user can choose which challenges to satisfy:

.. code-block:: json

    {
      "recovery_state": "CHALLENGE_SELECTING",
      "recovery_information": {
        "challenges": [
          {
            "uuid": "MW2R3RCBZPHNC78AW8AKWRCHF9KV3Y82EN62T831ZP54S3K5599G",
            "cost": "TESTKUDOS:0",
            "type": "question",
            "instructions": "q1"
          },
          {
            "uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0",
            "cost": "TESTKUDOS:0",
            "type": "email",
            "instructions": "e-mail address m?il@f*.bar"
          },
        ],
        "policies": [
          [
            {
              "uuid": "MW2R3RCBZPHNC78AW8AKWRCHF9KV3Y82EN62T831ZP54S3K5599G"
            },
            {
              "uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0"
            },
          ],
        ],
        "provider_url": "http://localhost:8088/",
        "version": 1,
      },
      "recovery_document": {
        "...": "..."
      }
    }

The ``recovery_document`` is an internal representation of the recovery
information and of no concern to the user interface. The pertinent information
is in the ``recovery_information``. Here, the ``challenges`` array is a list
of possible challenges the user could attempt to solve next, while ``policies``
is an array of policies, with each policy being an array of challenges.
Satisfying all of the challenges of one of the policies will enable the secret
to be recovered.  The ``provider_url`` from where the recovery document was
obtained and its ``version`` are also provided.  Each challenge comes with
four mandatory fields:

    - **uuid**: A unique identifier of the challenge; this is what the
      UUIDs in the policies array refer to, but also this UUID may be
      included in messages sent to the user. They allow the user to
      distinguish different PIN/TANs should say the same phone number be
      used for SMS-authentication with different providers.
    - **cost**: This is the amount the Anastasis provider will charge
      to allow the user to pass the challenge.
    - **type**: This is the type of the challenge, as a string.
    - **instructions**: Contains additional important hints for the user
      to allow the user to satisfy the challenge.  It typically includes
      an abbreviated form of the contact information or the security
      question. Details depend on ``type``.

If a recovery document was not found, either the user never performed
a backup, entered incorrect attributes, or used a provider not yet in
the list of Anastasis providers.  Hence, the user must now either
select a different provider, or go ``back`` and update the identity
attributes.  In the case a recovery document was not found, the
transition fails, returning the error code and a human-readable error
message together with a transition failure:

.. code-block:: json

    {
      "recovery_state": "ERROR",
      "error_message": "account unknown to Anastasis server",
      "error_code": 9,
    }

Here, the ``error_code`` is from the ``enum ANASTASIS_RecoveryStatus``
and describes precisely what failed about the download, while the
``error_message`` is a human-readable (English) explanation of the code.
Applications may want to translate the message using GNU gettext;
translations should be available in the ``anastasis`` text domain.
However, in general it should be sufficient to display the slightly
more generic Taler error code that is returned with the new state.


**change_version:**

Even if a recovery document was found, it is possible that the user
intended to recover a different version, or recover a backup where
the recovery document is stored at a different provider. Thus, the
reducer allows the user to explicitly switch to a different provider
or recovery document version using the ``change_version`` transition,
which takes a provider URL and policy version as arguments:

.. code-block:: json

    {
      "provider_url": "https://localhost:8080/",
      "version": 2
    }

Note that using a version of 0 implies fetching "the latest version".  The
resulting states are the same as those of the ``enter_user_attributes``
transition, except that the recovery document version is not necessarily the
latest available version at the provider.


**select_challenge:**

Selecting a challenge takes different, depending on the state of the payment.
A comprehensive example for ``select_challenge`` would be:

.. code-block:: json

    {
        "uuid": "80H646H5ZBR453C02Y5RT55VQSJZGM5REWFXVY0SWXY1TNE8CT30"
        "timeout" : { "d_ms" : 5000 },
        "payment_secret": "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG"
    }

The ``uuid`` field is mandatory and specifies the selected challenge.
The other fields are optional, and are needed in case the user has
previously been requested to pay for the challenge.  In this case,
the ``payment_secret`` identifies the previous payment request, and
``timeout`` says how long the Anastasis service should wait for the
payment to be completed before giving up (long polling).

Depending on the type of the challenge and the need for payment, the
reducer may transition into ``CHALLENGE_SOLVING`` or ``CHALLENGE_PAYING``
states.  In ``CHALLENGE_SOLVING``, the new state will primarily specify
the selected challenge:

.. code-block:: json

    {
      "backup_state": "CHALLENGE_SOLVING",
      "selected_challenge_uuid": "80H646H5ZBR453C02Y5RT55VQSJZGM5REWFXVY0SWXY1TNE8CT30"
    }

In ``CHALLENGE_PAYING``, the new state will include instructions for payment
in the ``challenge_feedback``. In general, ``challenge_feedback`` includes
information about attempted challenges, with the final state being ``solved``:

.. code-block:: json

    {
      "recovery_state": "CHALLENGE_SELECTING",
      "recovery_information": {
        "...": "..."
      }
      "challenge_feedback": {
        "80H646H5ZBR453C02Y5RT55VQSJZGM5REWFXVY0SWXY1TNE8CT30" : {
          "state" : "solved"
        }
      }
    }

Challenges feedback for a challenge can have many different ``state`` values
that applications must all handle. States other than ``solved`` are:

   - **payment**: Here, the user must pay for a challenge. An example would be:

     .. code-block:: json

        {
          "backup_state": "CHALLENGE_PAYING",
          "selected_challenge_uuid": "80H646H5ZBR453C02Y5RT55VQSJZGM5REWFXVY0SWXY1TNE8CT30",
          "challenge_feedback": {
            "80H646H5ZBR453C02Y5RT55VQSJZGM5REWFXVY0SWXY1TNE8CT30" : {
             "state" : "payment",
             "taler_pay_uri" : "taler://pay/...",
             "provider" : "https://localhost:8080/",
             "payment_secret" : "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG"
            }
          }
        }

   - **body**: Here, the server provided an HTTP reply for
     how to solve the challenge, but the reducer could not parse
     them into a known format. A mime-type may be provided and may
     help parse the details.

     .. code-block:: json

        {
          "recovery_state": "CHALLENGE_SOLVING",
          "recovery_information": {
              "...": "..."
          }
          "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0",
          "challenge_feedback": {
            "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": {
              "state": "body",
              "body": "CROCKFORDBASE32ENCODEDBODY",
              "http_status": 403,
              "mime_type" : "anything/possible"
            }
          }
        }

   - **hint**: Here, the server provided human-readable hint for
     how to solve the challenge.  Note that the ``hint`` provided this
     time is from the Anastasis provider and may differ from the ``instructions``
     for the challenge under ``recovery_information``:

     .. code-block:: json

        {
          "recovery_state": "CHALLENGE_SOLVING",
          "recovery_information": {
              "...": "..."
          }
          "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0",
          "challenge_feedback": {
            "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": {
              "state": "hint",
              "hint": "Recovery TAN send to email mail@DOMAIN",
              "http_status": 403
            }
          }
        }

   - **details**: Here, the server provided a detailed JSON status response
     related to solving the challenge:

     .. code-block:: json

       {
         "recovery_state": "CHALLENGE_SOLVING",
         "recovery_information": {
            "...": "..."
         }
         "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0",
         "challenge_feedback": {
           "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": {
             "state": "details",
             "details": {
               "code": 8111,
               "hint": "The client's response to the challenge was invalid.",
               "detail" : null
             },
             "http_status": 403
           }
         }
       }

   - **redirect**: To solve the challenge, the user must visit the indicated
     Web site at ``redirect_url``, for example to perform video authentication:

    .. code-block:: json

       {
         "recovery_state": "CHALLENGE_SOLVING",
         "recovery_information": {
            "...": "..."
         }
         "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0",
         "challenge_feedback": {
           "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": {
             "state": "redirect",
             "redirect_url": "https://videoconf.example.com/",
             "http_status": 303
           }
         }
       }

   - **server-failure**: This indicates that the Anastasis provider encountered
     a failure and recovery using this challenge cannot proceed at this time.
     Examples for failures might be that the provider is unable to send SMS
     messages at this time due to an outage.  The body includes details about
     the failure. The user may try again later or continue with other challenges.

     .. code-block:: json

       {
         "recovery_state": "CHALLENGE_SELECTING",
         "recovery_information": {
             "...": "..."
         }
         "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0",
         "challenge_feedback": {
           "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": {
            "state": "server-failure",
            "http_status": "500",
            "error_code": 52
          }
        }
      }

    - **truth-unknown**: This indicates that the Anastasis provider is unaware of
      the specified challenge. This is typically a permanent failure, and user
      interfaces should not allow users to re-try this challenge.

      .. code-block:: json

        {
          "recovery_state": "CHALLENGE_SELECTING",
          "recovery_information": {
              "...": "..."
          }
          "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0",
          "challenge_feedback": {
            "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": {
              "state": "truth-unknown",
              "error_code": 8108
            }
          }
        }

    - **rate-limit-exceeded**: This indicates that the user has made too many invalid attempts in too short an amount of time.

      .. code-block:: json

         {
           "recovery_state": "CHALLENGE_SELECTING",
           "recovery_information": {
               "...": "..."
           }
           "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0",
           "challenge_feedback": {
             "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": {
               "state": "rate-limit-exceeded",
               "error_code": 8121
             }
           }
         }

    - **authentication-timeout**: This indicates that the challenge is awaiting for some external authentication process to complete. The application should ``poll`` for it to complete, or proceed with selecting other challenges.

      .. code-block:: json

         {
           "recovery_state": "CHALLENGE_SELECTING",
           "recovery_information": {
               "...": "..."
           }
           "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0",
           "challenge_feedback": {
             "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": {
               "state": "authentication-timeout",
               "error_code": 8122
             }
           }
         }

    - **authentication-instructions**: This indicates that the challenge requires the user to perform some authentication method-specific actions. Details about what the user should do are provided.

      .. code-block:: json

        {
          "recovery_state": "CHALLENGE_SELECTING",
          "recovery_information": {
              "...": "..."
          }
          "selected_challenge_uuid": "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0",
          "challenge_feedback": {
            "TXYKGE1SJZHJ4M2FKSV1P2RZVNTHZFB9E3A79QE956D3SCAWXPK0": {
              "state": "external-instructions",
              "method": "iban",
              "async": true, // optional
              "answer_code": 987654321, // optional
              "details": {
                "...": "..."
              }
            }
          }
        }

    If "async" is "true", then the client should
    poll for the challenge being satisfied using
    the "answer_code" that has been provided.

    The specific instructions on how to satisfy
    the challenge depend on the ``method``.
    They include:

      - **iban**: The user must perform a wire transfer from their account to the Anastasis provider.

        .. code-block:: json

          {
            "challenge_amount": "EUR:1",
            "credit_iban": "DE12345789000",
            "business_name": "Data Loss Incorporated",
            "wire_transfer_subject": "Anastasis 987654321"
          }

        Note that the actual wire transfer subject must contain both
        the numeric ``answer_code`` as well as
        the string ``Anastasis``.

**poll:**

With a ``poll`` transition, the application indicates that it wants to wait longer for one or more of the challenges that are in state ``authentication-timeout`` to possibly complete.  While technically optional, the ``timeout`` argument should really be provided to enable long-polling, for example:

.. code-block:: json

    {
        "timeout" : { "d_ms" : 5000 },
    }


**pay:**

With a ``pay`` transition, the application indicates to the reducer that
a payment may have been made. Here, it is again possible to specify an
optional ``timeout`` argument for long-polling, for example:

.. code-block:: json

    {
        "payment_secret": "ABCDADF242525AABASD52525235ABABFDABABANALASDAAKASDAS"
        "timeout" : { "d_ms" : 5000 },
    }

Depending on the type of the challenge and the result of the operation, the
new state may be ``CHALLENGE_SOLVING`` (if say the SMS was now sent to the
user), ``CHALLENGE_SELECTING`` (if the answer to the security question was
correct), ``RECOVERY_FINISHED`` (if this was the last challenge that needed to
be solved) or still ``CHALLENGE_PAYING`` (if the challenge was not actually
paid for).  For sample messages, see the different types of
``challenge_feedback`` in the section about ``select_challenge``.


**solve_challenge:**

Solving a challenge takes various formats, depending on the type of the
challenge and what is known about the answer.  The different supported
formats are:

.. code-block:: json

    {
        "answer": "answer to security question"
    }

.. code-block:: json

    {
        "pin": 1234
    }

.. code-block:: json

    {
        "hash": "SOMEBASE32ENCODEDHASHVALUE"
    }