flwr-nightly 1.9.0.dev20240502__tar.gz → 1.9.0.dev20240506__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of flwr-nightly might be problematic. Click here for more details.

Files changed (218) hide show
  1. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/PKG-INFO +1 -1
  2. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/pyproject.toml +1 -1
  3. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/message.py +33 -4
  4. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/record/parametersrecord.py +0 -1
  5. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/record/recordset.py +12 -1
  6. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/app.py +13 -7
  7. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +94 -53
  8. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/state/in_memory_state.py +40 -8
  9. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/state/sqlite_state.py +45 -7
  10. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/state/state.py +9 -3
  11. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/LICENSE +0 -0
  12. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/README.md +0 -0
  13. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/__init__.py +0 -0
  14. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/__init__.py +0 -0
  15. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/app.py +0 -0
  16. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/build.py +0 -0
  17. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/config_utils.py +0 -0
  18. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/example.py +0 -0
  19. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/new/__init__.py +0 -0
  20. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/new/new.py +0 -0
  21. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/new/templates/__init__.py +0 -0
  22. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/new/templates/app/.gitignore.tpl +0 -0
  23. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/new/templates/app/README.md.tpl +0 -0
  24. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/new/templates/app/__init__.py +0 -0
  25. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/new/templates/app/code/__init__.py +0 -0
  26. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/new/templates/app/code/__init__.py.tpl +0 -0
  27. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/new/templates/app/code/client.numpy.py.tpl +0 -0
  28. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/new/templates/app/code/client.pytorch.py.tpl +0 -0
  29. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/new/templates/app/code/client.sklearn.py.tpl +0 -0
  30. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +0 -0
  31. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/new/templates/app/code/server.numpy.py.tpl +0 -0
  32. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/new/templates/app/code/server.pytorch.py.tpl +0 -0
  33. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/new/templates/app/code/server.sklearn.py.tpl +0 -0
  34. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +0 -0
  35. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/new/templates/app/code/task.pytorch.py.tpl +0 -0
  36. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +0 -0
  37. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +0 -0
  38. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +0 -0
  39. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +0 -0
  40. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +0 -0
  41. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/run/__init__.py +0 -0
  42. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/run/run.py +0 -0
  43. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/cli/utils.py +0 -0
  44. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/__init__.py +0 -0
  45. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/app.py +0 -0
  46. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/client.py +0 -0
  47. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/client_app.py +0 -0
  48. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/dpfedavg_numpy_client.py +0 -0
  49. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/grpc_client/__init__.py +0 -0
  50. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/grpc_client/connection.py +0 -0
  51. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/grpc_rere_client/__init__.py +0 -0
  52. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/grpc_rere_client/client_interceptor.py +0 -0
  53. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/grpc_rere_client/connection.py +0 -0
  54. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/heartbeat.py +0 -0
  55. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/message_handler/__init__.py +0 -0
  56. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/message_handler/message_handler.py +0 -0
  57. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/message_handler/task_handler.py +0 -0
  58. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/mod/__init__.py +0 -0
  59. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/mod/centraldp_mods.py +0 -0
  60. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/mod/comms_mods.py +0 -0
  61. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/mod/localdp_mod.py +0 -0
  62. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/mod/secure_aggregation/__init__.py +0 -0
  63. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/mod/secure_aggregation/secagg_mod.py +0 -0
  64. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/mod/secure_aggregation/secaggplus_mod.py +0 -0
  65. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/mod/utils.py +0 -0
  66. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/node_state.py +0 -0
  67. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/node_state_tests.py +0 -0
  68. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/numpy_client.py +0 -0
  69. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/rest_client/__init__.py +0 -0
  70. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/rest_client/connection.py +0 -0
  71. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/supernode/__init__.py +0 -0
  72. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/supernode/app.py +0 -0
  73. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/client/typing.py +0 -0
  74. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/__init__.py +0 -0
  75. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/address.py +0 -0
  76. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/constant.py +0 -0
  77. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/context.py +0 -0
  78. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/date.py +0 -0
  79. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/differential_privacy.py +0 -0
  80. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/differential_privacy_constants.py +0 -0
  81. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/dp.py +0 -0
  82. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/exit_handlers.py +0 -0
  83. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/grpc.py +0 -0
  84. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/logger.py +0 -0
  85. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/object_ref.py +0 -0
  86. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/parameter.py +0 -0
  87. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/pyproject.py +0 -0
  88. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/record/__init__.py +0 -0
  89. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/record/configsrecord.py +0 -0
  90. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/record/conversion_utils.py +0 -0
  91. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/record/metricsrecord.py +0 -0
  92. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/record/typeddict.py +0 -0
  93. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/recordset_compat.py +0 -0
  94. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/retry_invoker.py +0 -0
  95. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/secure_aggregation/__init__.py +0 -0
  96. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/secure_aggregation/crypto/__init__.py +0 -0
  97. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/secure_aggregation/crypto/shamir.py +0 -0
  98. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/secure_aggregation/crypto/symmetric_encryption.py +0 -0
  99. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/secure_aggregation/ndarrays_arithmetic.py +0 -0
  100. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/secure_aggregation/quantization.py +0 -0
  101. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/secure_aggregation/secaggplus_constants.py +0 -0
  102. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/secure_aggregation/secaggplus_utils.py +0 -0
  103. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/serde.py +0 -0
  104. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/telemetry.py +0 -0
  105. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/typing.py +0 -0
  106. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/common/version.py +0 -0
  107. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/__init__.py +0 -0
  108. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/driver_pb2.py +0 -0
  109. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/driver_pb2.pyi +0 -0
  110. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/driver_pb2_grpc.py +0 -0
  111. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/driver_pb2_grpc.pyi +0 -0
  112. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/error_pb2.py +0 -0
  113. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/error_pb2.pyi +0 -0
  114. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/error_pb2_grpc.py +0 -0
  115. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/error_pb2_grpc.pyi +0 -0
  116. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/fleet_pb2.py +0 -0
  117. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/fleet_pb2.pyi +0 -0
  118. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/fleet_pb2_grpc.py +0 -0
  119. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/fleet_pb2_grpc.pyi +0 -0
  120. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/node_pb2.py +0 -0
  121. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/node_pb2.pyi +0 -0
  122. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/node_pb2_grpc.py +0 -0
  123. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/node_pb2_grpc.pyi +0 -0
  124. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/recordset_pb2.py +0 -0
  125. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/recordset_pb2.pyi +0 -0
  126. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/recordset_pb2_grpc.py +0 -0
  127. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/recordset_pb2_grpc.pyi +0 -0
  128. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/task_pb2.py +0 -0
  129. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/task_pb2.pyi +0 -0
  130. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/task_pb2_grpc.py +0 -0
  131. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/task_pb2_grpc.pyi +0 -0
  132. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/transport_pb2.py +0 -0
  133. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/transport_pb2.pyi +0 -0
  134. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/transport_pb2_grpc.py +0 -0
  135. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/proto/transport_pb2_grpc.pyi +0 -0
  136. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/py.typed +0 -0
  137. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/__init__.py +0 -0
  138. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/client_manager.py +0 -0
  139. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/client_proxy.py +0 -0
  140. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/compat/__init__.py +0 -0
  141. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/compat/app.py +0 -0
  142. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/compat/app_utils.py +0 -0
  143. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/compat/driver_client_proxy.py +0 -0
  144. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/compat/legacy_context.py +0 -0
  145. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/criterion.py +0 -0
  146. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/driver/__init__.py +0 -0
  147. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/driver/driver.py +0 -0
  148. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/driver/grpc_driver.py +0 -0
  149. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/history.py +0 -0
  150. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/run_serverapp.py +0 -0
  151. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/server.py +0 -0
  152. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/server_app.py +0 -0
  153. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/server_config.py +0 -0
  154. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/strategy/__init__.py +0 -0
  155. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/strategy/aggregate.py +0 -0
  156. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/strategy/bulyan.py +0 -0
  157. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/strategy/dp_adaptive_clipping.py +0 -0
  158. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/strategy/dp_fixed_clipping.py +0 -0
  159. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/strategy/dpfedavg_adaptive.py +0 -0
  160. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/strategy/dpfedavg_fixed.py +0 -0
  161. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/strategy/fault_tolerant_fedavg.py +0 -0
  162. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/strategy/fedadagrad.py +0 -0
  163. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/strategy/fedadam.py +0 -0
  164. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/strategy/fedavg.py +0 -0
  165. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/strategy/fedavg_android.py +0 -0
  166. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/strategy/fedavgm.py +0 -0
  167. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/strategy/fedmedian.py +0 -0
  168. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/strategy/fedopt.py +0 -0
  169. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/strategy/fedprox.py +0 -0
  170. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/strategy/fedtrimmedavg.py +0 -0
  171. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/strategy/fedxgb_bagging.py +0 -0
  172. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/strategy/fedxgb_cyclic.py +0 -0
  173. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/strategy/fedxgb_nn_avg.py +0 -0
  174. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/strategy/fedyogi.py +0 -0
  175. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/strategy/krum.py +0 -0
  176. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/strategy/qfedavg.py +0 -0
  177. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/strategy/strategy.py +0 -0
  178. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/__init__.py +0 -0
  179. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/driver/__init__.py +0 -0
  180. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/driver/driver_grpc.py +0 -0
  181. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/driver/driver_servicer.py +0 -0
  182. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/fleet/__init__.py +0 -0
  183. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/fleet/grpc_bidi/__init__.py +0 -0
  184. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +0 -0
  185. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +0 -0
  186. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +0 -0
  187. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +0 -0
  188. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/fleet/grpc_rere/__init__.py +0 -0
  189. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +0 -0
  190. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/fleet/message_handler/__init__.py +0 -0
  191. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/fleet/message_handler/message_handler.py +0 -0
  192. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/fleet/rest_rere/__init__.py +0 -0
  193. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/fleet/rest_rere/rest_api.py +0 -0
  194. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/fleet/vce/__init__.py +0 -0
  195. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/fleet/vce/backend/__init__.py +0 -0
  196. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/fleet/vce/backend/backend.py +0 -0
  197. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/fleet/vce/backend/raybackend.py +0 -0
  198. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/fleet/vce/vce_api.py +0 -0
  199. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/state/__init__.py +0 -0
  200. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/state/state_factory.py +0 -0
  201. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/superlink/state/utils.py +0 -0
  202. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/typing.py +0 -0
  203. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/utils/__init__.py +0 -0
  204. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/utils/tensorboard.py +0 -0
  205. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/utils/validator.py +0 -0
  206. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/workflow/__init__.py +0 -0
  207. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/workflow/constant.py +0 -0
  208. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/workflow/default_workflows.py +0 -0
  209. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/workflow/secure_aggregation/__init__.py +0 -0
  210. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/workflow/secure_aggregation/secagg_workflow.py +0 -0
  211. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +0 -0
  212. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/simulation/__init__.py +0 -0
  213. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/simulation/app.py +0 -0
  214. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/simulation/ray_transport/__init__.py +0 -0
  215. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/simulation/ray_transport/ray_actor.py +0 -0
  216. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/simulation/ray_transport/ray_client_proxy.py +0 -0
  217. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/simulation/ray_transport/utils.py +0 -0
  218. {flwr_nightly-1.9.0.dev20240502 → flwr_nightly-1.9.0.dev20240506}/src/py/flwr/simulation/run_simulation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flwr-nightly
3
- Version: 1.9.0.dev20240502
3
+ Version: 1.9.0.dev20240506
4
4
  Summary: Flower: A Friendly Federated Learning Framework
5
5
  Home-page: https://flower.ai
6
6
  License: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "flwr-nightly"
7
- version = "1.9.0.dev20240502"
7
+ version = "1.9.0.dev20240506"
8
8
  description = "Flower: A Friendly Federated Learning Framework"
9
9
  license = "Apache-2.0"
10
10
  authors = ["The Flower Authors <hello@flower.ai>"]
@@ -18,7 +18,6 @@ from __future__ import annotations
18
18
 
19
19
  import time
20
20
  import warnings
21
- from dataclasses import dataclass
22
21
  from typing import Optional, cast
23
22
 
24
23
  from .record import RecordSet
@@ -26,7 +25,6 @@ from .record import RecordSet
26
25
  DEFAULT_TTL = 3600
27
26
 
28
27
 
29
- @dataclass
30
28
  class Metadata: # pylint: disable=too-many-instance-attributes
31
29
  """A dataclass holding metadata associated with the current message.
32
30
 
@@ -161,8 +159,18 @@ class Metadata: # pylint: disable=too-many-instance-attributes
161
159
  """Set partition_id."""
162
160
  self.__dict__["_partition_id"] = value
163
161
 
162
+ def __repr__(self) -> str:
163
+ """Return a string representation of this instance."""
164
+ view = ", ".join([f"{k.lstrip('_')}={v!r}" for k, v in self.__dict__.items()])
165
+ return f"{self.__class__.__qualname__}({view})"
166
+
167
+ def __eq__(self, other: object) -> bool:
168
+ """Compare two instances of the class."""
169
+ if not isinstance(other, self.__class__):
170
+ raise NotImplementedError
171
+ return self.__dict__ == other.__dict__
172
+
164
173
 
165
- @dataclass
166
174
  class Error:
167
175
  """A dataclass that stores information about an error that occurred.
168
176
 
@@ -191,8 +199,18 @@ class Error:
191
199
  """Reason reported about the error."""
192
200
  return cast(Optional[str], self.__dict__["_reason"])
193
201
 
202
+ def __repr__(self) -> str:
203
+ """Return a string representation of this instance."""
204
+ view = ", ".join([f"{k.lstrip('_')}={v!r}" for k, v in self.__dict__.items()])
205
+ return f"{self.__class__.__qualname__}({view})"
206
+
207
+ def __eq__(self, other: object) -> bool:
208
+ """Compare two instances of the class."""
209
+ if not isinstance(other, self.__class__):
210
+ raise NotImplementedError
211
+ return self.__dict__ == other.__dict__
212
+
194
213
 
195
- @dataclass
196
214
  class Message:
197
215
  """State of your application from the viewpoint of the entity using it.
198
216
 
@@ -357,6 +375,17 @@ class Message:
357
375
 
358
376
  return message
359
377
 
378
+ def __repr__(self) -> str:
379
+ """Return a string representation of this instance."""
380
+ view = ", ".join(
381
+ [
382
+ f"{k.lstrip('_')}={v!r}"
383
+ for k, v in self.__dict__.items()
384
+ if v is not None
385
+ ]
386
+ )
387
+ return f"{self.__class__.__qualname__}({view})"
388
+
360
389
 
361
390
  def _create_reply_metadata(msg: Message, ttl: float) -> Metadata:
362
391
  """Construct metadata for a reply message."""
@@ -82,7 +82,6 @@ def _check_value(value: Array) -> None:
82
82
  )
83
83
 
84
84
 
85
- @dataclass
86
85
  class ParametersRecord(TypedDict[str, Array]):
87
86
  """Parameters record.
88
87
 
@@ -83,7 +83,6 @@ class RecordSetData:
83
83
  )
84
84
 
85
85
 
86
- @dataclass
87
86
  class RecordSet:
88
87
  """RecordSet stores groups of parameters, metrics and configs."""
89
88
 
@@ -117,3 +116,15 @@ class RecordSet:
117
116
  """Dictionary holding ConfigsRecord instances."""
118
117
  data = cast(RecordSetData, self.__dict__["_data"])
119
118
  return data.configs_records
119
+
120
+ def __repr__(self) -> str:
121
+ """Return a string representation of this instance."""
122
+ flds = ("parameters_records", "metrics_records", "configs_records")
123
+ view = ", ".join([f"{fld}={getattr(self, fld)!r}" for fld in flds])
124
+ return f"{self.__class__.__qualname__}({view})"
125
+
126
+ def __eq__(self, other: object) -> bool:
127
+ """Compare two instances of the class."""
128
+ if not isinstance(other, self.__class__):
129
+ raise NotImplementedError
130
+ return self.__dict__ == other.__dict__
@@ -43,6 +43,7 @@ from flwr.common.constant import (
43
43
  from flwr.common.exit_handlers import register_exit_handlers
44
44
  from flwr.common.logger import log
45
45
  from flwr.common.secure_aggregation.crypto.symmetric_encryption import (
46
+ private_key_to_bytes,
46
47
  public_key_to_bytes,
47
48
  ssh_types_to_elliptic_curve,
48
49
  )
@@ -374,13 +375,18 @@ def run_superlink() -> None:
374
375
  server_private_key,
375
376
  server_public_key,
376
377
  ) = maybe_keys
377
- interceptors = [
378
- AuthenticateServerInterceptor(
379
- client_public_keys,
380
- server_private_key,
381
- server_public_key,
382
- )
383
- ]
378
+ state = state_factory.state()
379
+ state.store_client_public_keys(client_public_keys)
380
+ state.store_server_private_public_key(
381
+ private_key_to_bytes(server_private_key),
382
+ public_key_to_bytes(server_public_key),
383
+ )
384
+ log(
385
+ INFO,
386
+ "Client authentication enabled with %d known public keys",
387
+ len(client_public_keys),
388
+ )
389
+ interceptors = [AuthenticateServerInterceptor(state)]
384
390
 
385
391
  fleet_server = _run_fleet_api_grpc_rere(
386
392
  address=address,
@@ -16,17 +16,17 @@
16
16
 
17
17
 
18
18
  import base64
19
- from logging import INFO
20
- from typing import Any, Callable, Sequence, Set, Tuple, Union
19
+ from logging import WARNING
20
+ from typing import Any, Callable, Optional, Sequence, Tuple, Union
21
21
 
22
22
  import grpc
23
23
  from cryptography.hazmat.primitives.asymmetric import ec
24
24
 
25
25
  from flwr.common.logger import log
26
26
  from flwr.common.secure_aggregation.crypto.symmetric_encryption import (
27
+ bytes_to_private_key,
27
28
  bytes_to_public_key,
28
29
  generate_shared_key,
29
- public_key_to_bytes,
30
30
  verify_hmac,
31
31
  )
32
32
  from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
@@ -43,6 +43,8 @@ from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
43
43
  PushTaskResRequest,
44
44
  PushTaskResResponse,
45
45
  )
46
+ from flwr.proto.node_pb2 import Node # pylint: disable=E0611
47
+ from flwr.server.superlink.state import State
46
48
 
47
49
  _PUBLIC_KEY_HEADER = "public-key"
48
50
  _AUTH_TOKEN_HEADER = "auth-token"
@@ -79,22 +81,21 @@ def _get_value_from_tuples(
79
81
  class AuthenticateServerInterceptor(grpc.ServerInterceptor): # type: ignore
80
82
  """Server interceptor for client authentication."""
81
83
 
82
- def __init__(
83
- self,
84
- client_public_keys: Set[bytes],
85
- private_key: ec.EllipticCurvePrivateKey,
86
- public_key: ec.EllipticCurvePublicKey,
87
- ):
88
- self.server_private_key = private_key
89
- self.client_public_keys = client_public_keys
90
- self.encoded_server_public_key = base64.urlsafe_b64encode(
91
- public_key_to_bytes(public_key)
92
- )
93
- log(
94
- INFO,
95
- "Client authentication enabled with %d known public keys",
96
- len(client_public_keys),
97
- )
84
+ def __init__(self, state: State):
85
+ self.state = state
86
+
87
+ self.client_public_keys = state.get_client_public_keys()
88
+ if len(self.client_public_keys) == 0:
89
+ log(WARNING, "Authentication enabled, but no known public keys configured")
90
+
91
+ private_key = self.state.get_server_private_key()
92
+ public_key = self.state.get_server_public_key()
93
+
94
+ if private_key is None or public_key is None:
95
+ raise ValueError("Error loading authentication keys")
96
+
97
+ self.server_private_key = bytes_to_private_key(private_key)
98
+ self.encoded_server_public_key = base64.urlsafe_b64encode(public_key)
98
99
 
99
100
  def intercept_service(
100
101
  self,
@@ -124,45 +125,29 @@ class AuthenticateServerInterceptor(grpc.ServerInterceptor): # type: ignore
124
125
  _PUBLIC_KEY_HEADER, context.invocation_metadata()
125
126
  )
126
127
  )
127
- is_public_key_known = client_public_key_bytes in self.client_public_keys
128
- if not is_public_key_known:
128
+ if client_public_key_bytes not in self.client_public_keys:
129
129
  context.abort(grpc.StatusCode.UNAUTHENTICATED, "Access denied")
130
130
 
131
131
  if isinstance(request, CreateNodeRequest):
132
- context.send_initial_metadata(
133
- (
134
- (
135
- _PUBLIC_KEY_HEADER,
136
- self.encoded_server_public_key,
137
- ),
138
- )
132
+ return self._create_authenticated_node(
133
+ client_public_key_bytes, request, context
139
134
  )
140
- elif isinstance(
141
- request,
142
- (
143
- DeleteNodeRequest,
144
- PullTaskInsRequest,
145
- PushTaskResRequest,
146
- GetRunRequest,
147
- PingRequest,
148
- ),
149
- ):
150
- hmac_value = base64.urlsafe_b64decode(
151
- _get_value_from_tuples(
152
- _AUTH_TOKEN_HEADER, context.invocation_metadata()
153
- )
154
- )
155
- client_public_key = bytes_to_public_key(client_public_key_bytes)
156
- shared_secret = generate_shared_key(
157
- self.server_private_key,
158
- client_public_key,
159
- )
160
- verify = verify_hmac(
161
- shared_secret, request.SerializeToString(True), hmac_value
135
+
136
+ # Verify hmac value
137
+ hmac_value = base64.urlsafe_b64decode(
138
+ _get_value_from_tuples(
139
+ _AUTH_TOKEN_HEADER, context.invocation_metadata()
162
140
  )
163
- if not verify:
164
- context.abort(grpc.StatusCode.UNAUTHENTICATED, "Access denied")
165
- else:
141
+ )
142
+ public_key = bytes_to_public_key(client_public_key_bytes)
143
+
144
+ if not self._verify_hmac(public_key, request, hmac_value):
145
+ context.abort(grpc.StatusCode.UNAUTHENTICATED, "Access denied")
146
+
147
+ # Verify node_id
148
+ node_id = self.state.get_node_id(client_public_key_bytes)
149
+
150
+ if not self._verify_node_id(node_id, request):
166
151
  context.abort(grpc.StatusCode.UNAUTHENTICATED, "Access denied")
167
152
 
168
153
  return method_handler.unary_unary(request, context) # type: ignore
@@ -172,3 +157,59 @@ class AuthenticateServerInterceptor(grpc.ServerInterceptor): # type: ignore
172
157
  request_deserializer=method_handler.request_deserializer,
173
158
  response_serializer=method_handler.response_serializer,
174
159
  )
160
+
161
+ def _verify_node_id(
162
+ self,
163
+ node_id: Optional[int],
164
+ request: Union[
165
+ DeleteNodeRequest,
166
+ PullTaskInsRequest,
167
+ PushTaskResRequest,
168
+ GetRunRequest,
169
+ PingRequest,
170
+ ],
171
+ ) -> bool:
172
+ if node_id is None:
173
+ return False
174
+ if isinstance(request, PushTaskResRequest):
175
+ if len(request.task_res_list) == 0:
176
+ return False
177
+ return request.task_res_list[0].task.producer.node_id == node_id
178
+ if isinstance(request, GetRunRequest):
179
+ return node_id in self.state.get_nodes(request.run_id)
180
+ return request.node.node_id == node_id
181
+
182
+ def _verify_hmac(
183
+ self, public_key: ec.EllipticCurvePublicKey, request: Request, hmac_value: bytes
184
+ ) -> bool:
185
+ shared_secret = generate_shared_key(self.server_private_key, public_key)
186
+ return verify_hmac(shared_secret, request.SerializeToString(True), hmac_value)
187
+
188
+ def _create_authenticated_node(
189
+ self,
190
+ public_key_bytes: bytes,
191
+ request: CreateNodeRequest,
192
+ context: grpc.ServicerContext,
193
+ ) -> CreateNodeResponse:
194
+ context.send_initial_metadata(
195
+ (
196
+ (
197
+ _PUBLIC_KEY_HEADER,
198
+ self.encoded_server_public_key,
199
+ ),
200
+ )
201
+ )
202
+
203
+ node_id = self.state.get_node_id(public_key_bytes)
204
+
205
+ # Handle `CreateNode` here instead of calling the default method handler
206
+ # Return previously assigned `node_id` for the provided `public_key`
207
+ if node_id is not None:
208
+ self.state.acknowledge_ping(node_id, request.ping_interval)
209
+ return CreateNodeResponse(node=Node(node_id=node_id, anonymous=False))
210
+
211
+ # No `node_id` exists for the provided `public_key`
212
+ # Handle `CreateNode` here instead of calling the default method handler
213
+ # Note: the innermost `CreateNode` method will never be called
214
+ node_id = self.state.create_node(request.ping_interval, public_key_bytes)
215
+ return CreateNodeResponse(node=Node(node_id=node_id, anonymous=False))
@@ -30,19 +30,24 @@ from flwr.server.utils import validate_task_ins_or_res
30
30
  from .utils import make_node_unavailable_taskres
31
31
 
32
32
 
33
- class InMemoryState(State): # pylint: disable=R0902
33
+ class InMemoryState(State): # pylint: disable=R0902,R0904
34
34
  """In-memory State implementation."""
35
35
 
36
36
  def __init__(self) -> None:
37
+
37
38
  # Map node_id to (online_until, ping_interval)
38
39
  self.node_ids: Dict[int, Tuple[float, float]] = {}
40
+ self.public_key_to_node_id: Dict[bytes, int] = {}
41
+
39
42
  # Map run_id to (fab_id, fab_version)
40
43
  self.run_ids: Dict[int, Tuple[str, str]] = {}
41
44
  self.task_ins_store: Dict[UUID, TaskIns] = {}
42
45
  self.task_res_store: Dict[UUID, TaskRes] = {}
46
+
43
47
  self.client_public_keys: Set[bytes] = set()
44
48
  self.server_public_key: Optional[bytes] = None
45
49
  self.server_private_key: Optional[bytes] = None
50
+
46
51
  self.lock = threading.Lock()
47
52
 
48
53
  def store_task_ins(self, task_ins: TaskIns) -> Optional[UUID]:
@@ -205,23 +210,46 @@ class InMemoryState(State): # pylint: disable=R0902
205
210
  """
206
211
  return len(self.task_res_store)
207
212
 
208
- def create_node(self, ping_interval: float) -> int:
213
+ def create_node(
214
+ self, ping_interval: float, public_key: Optional[bytes] = None
215
+ ) -> int:
209
216
  """Create, store in state, and return `node_id`."""
210
217
  # Sample a random int64 as node_id
211
218
  node_id: int = int.from_bytes(os.urandom(8), "little", signed=True)
212
219
 
213
220
  with self.lock:
214
- if node_id not in self.node_ids:
215
- self.node_ids[node_id] = (time.time() + ping_interval, ping_interval)
216
- return node_id
217
- log(ERROR, "Unexpected node registration failure.")
218
- return 0
221
+ if node_id in self.node_ids:
222
+ log(ERROR, "Unexpected node registration failure.")
223
+ return 0
219
224
 
220
- def delete_node(self, node_id: int) -> None:
225
+ if public_key is not None:
226
+ if (
227
+ public_key in self.public_key_to_node_id
228
+ or node_id in self.public_key_to_node_id.values()
229
+ ):
230
+ log(ERROR, "Unexpected node registration failure.")
231
+ return 0
232
+
233
+ self.public_key_to_node_id[public_key] = node_id
234
+
235
+ self.node_ids[node_id] = (time.time() + ping_interval, ping_interval)
236
+ return node_id
237
+
238
+ def delete_node(self, node_id: int, public_key: Optional[bytes] = None) -> None:
221
239
  """Delete a client node."""
222
240
  with self.lock:
223
241
  if node_id not in self.node_ids:
224
242
  raise ValueError(f"Node {node_id} not found")
243
+
244
+ if public_key is not None:
245
+ if (
246
+ public_key not in self.public_key_to_node_id
247
+ or node_id not in self.public_key_to_node_id.values()
248
+ ):
249
+ raise ValueError("Public key or node_id not found")
250
+
251
+ del self.public_key_to_node_id[public_key]
252
+
225
253
  del self.node_ids[node_id]
226
254
 
227
255
  def get_nodes(self, run_id: int) -> Set[int]:
@@ -242,6 +270,10 @@ class InMemoryState(State): # pylint: disable=R0902
242
270
  if online_until > current_time
243
271
  }
244
272
 
273
+ def get_node_id(self, client_public_key: bytes) -> Optional[int]:
274
+ """Retrieve stored `node_id` filtered by `client_public_keys`."""
275
+ return self.public_key_to_node_id.get(client_public_key)
276
+
245
277
  def create_run(self, fab_id: str, fab_version: str) -> int:
246
278
  """Create a new run for the specified `fab_id` and `fab_version`."""
247
279
  # Sample a random int64 as run_id
@@ -36,7 +36,8 @@ SQL_CREATE_TABLE_NODE = """
36
36
  CREATE TABLE IF NOT EXISTS node(
37
37
  node_id INTEGER UNIQUE,
38
38
  online_until REAL,
39
- ping_interval REAL
39
+ ping_interval REAL,
40
+ public_key BLOB
40
41
  );
41
42
  """
42
43
 
@@ -534,26 +535,54 @@ class SqliteState(State): # pylint: disable=R0904
534
535
 
535
536
  return None
536
537
 
537
- def create_node(self, ping_interval: float) -> int:
538
+ def create_node(
539
+ self, ping_interval: float, public_key: Optional[bytes] = None
540
+ ) -> int:
538
541
  """Create, store in state, and return `node_id`."""
539
542
  # Sample a random int64 as node_id
540
543
  node_id: int = int.from_bytes(os.urandom(8), "little", signed=True)
541
544
 
545
+ query = "SELECT node_id FROM node WHERE public_key = :public_key;"
546
+ row = self.query(query, {"public_key": public_key})
547
+
548
+ if len(row) > 0:
549
+ log(ERROR, "Unexpected node registration failure.")
550
+ return 0
551
+
542
552
  query = (
543
- "INSERT INTO node (node_id, online_until, ping_interval) VALUES (?, ?, ?)"
553
+ "INSERT INTO node "
554
+ "(node_id, online_until, ping_interval, public_key) "
555
+ "VALUES (?, ?, ?, ?)"
544
556
  )
545
557
 
546
558
  try:
547
- self.query(query, (node_id, time.time() + ping_interval, ping_interval))
559
+ self.query(
560
+ query, (node_id, time.time() + ping_interval, ping_interval, public_key)
561
+ )
548
562
  except sqlite3.IntegrityError:
549
563
  log(ERROR, "Unexpected node registration failure.")
550
564
  return 0
551
565
  return node_id
552
566
 
553
- def delete_node(self, node_id: int) -> None:
567
+ def delete_node(self, node_id: int, public_key: Optional[bytes] = None) -> None:
554
568
  """Delete a client node."""
555
- query = "DELETE FROM node WHERE node_id = :node_id;"
556
- self.query(query, {"node_id": node_id})
569
+ query = "DELETE FROM node WHERE node_id = ?"
570
+ params = (node_id,)
571
+
572
+ if public_key is not None:
573
+ query += " AND public_key = ?"
574
+ params += (public_key,) # type: ignore
575
+
576
+ if self.conn is None:
577
+ raise AttributeError("State is not initialized.")
578
+
579
+ try:
580
+ with self.conn:
581
+ rows = self.conn.execute(query, params)
582
+ if rows.rowcount < 1:
583
+ raise ValueError("Public key or node_id not found")
584
+ except KeyError as exc:
585
+ log(ERROR, {"query": query, "data": params, "exception": exc})
557
586
 
558
587
  def get_nodes(self, run_id: int) -> Set[int]:
559
588
  """Retrieve all currently stored node IDs as a set.
@@ -574,6 +603,15 @@ class SqliteState(State): # pylint: disable=R0904
574
603
  result: Set[int] = {row["node_id"] for row in rows}
575
604
  return result
576
605
 
606
+ def get_node_id(self, client_public_key: bytes) -> Optional[int]:
607
+ """Retrieve stored `node_id` filtered by `client_public_keys`."""
608
+ query = "SELECT node_id FROM node WHERE public_key = :public_key;"
609
+ row = self.query(query, {"public_key": client_public_key})
610
+ if len(row) > 0:
611
+ node_id: int = row[0]["node_id"]
612
+ return node_id
613
+ return None
614
+
577
615
  def create_run(self, fab_id: str, fab_version: str) -> int:
578
616
  """Create a new run for the specified `fab_id` and `fab_version`."""
579
617
  # Sample a random int64 as run_id
@@ -22,7 +22,7 @@ from uuid import UUID
22
22
  from flwr.proto.task_pb2 import TaskIns, TaskRes # pylint: disable=E0611
23
23
 
24
24
 
25
- class State(abc.ABC):
25
+ class State(abc.ABC): # pylint: disable=R0904
26
26
  """Abstract State."""
27
27
 
28
28
  @abc.abstractmethod
@@ -132,11 +132,13 @@ class State(abc.ABC):
132
132
  """Delete all delivered TaskIns/TaskRes pairs."""
133
133
 
134
134
  @abc.abstractmethod
135
- def create_node(self, ping_interval: float) -> int:
135
+ def create_node(
136
+ self, ping_interval: float, public_key: Optional[bytes] = None
137
+ ) -> int:
136
138
  """Create, store in state, and return `node_id`."""
137
139
 
138
140
  @abc.abstractmethod
139
- def delete_node(self, node_id: int) -> None:
141
+ def delete_node(self, node_id: int, public_key: Optional[bytes] = None) -> None:
140
142
  """Remove `node_id` from state."""
141
143
 
142
144
  @abc.abstractmethod
@@ -149,6 +151,10 @@ class State(abc.ABC):
149
151
  an empty `Set` MUST be returned.
150
152
  """
151
153
 
154
+ @abc.abstractmethod
155
+ def get_node_id(self, client_public_key: bytes) -> Optional[int]:
156
+ """Retrieve stored `node_id` filtered by `client_public_keys`."""
157
+
152
158
  @abc.abstractmethod
153
159
  def create_run(self, fab_id: str, fab_version: str) -> int:
154
160
  """Create a new run for the specified `fab_id` and `fab_version`."""