flwr-nightly 1.14.0.dev20241216__py3-none-any.whl → 1.15.0.dev20250112__py3-none-any.whl
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.
- flwr/cli/cli_user_auth_interceptor.py +6 -2
- flwr/cli/log.py +8 -6
- flwr/cli/login/login.py +11 -4
- flwr/cli/ls.py +7 -4
- flwr/cli/new/templates/app/.gitignore.tpl +3 -0
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +3 -3
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
- flwr/cli/run/run.py +7 -2
- flwr/cli/stop.py +3 -2
- flwr/cli/utils.py +83 -14
- flwr/client/app.py +17 -9
- flwr/client/client.py +0 -32
- flwr/client/grpc_rere_client/client_interceptor.py +6 -0
- flwr/client/grpc_rere_client/grpc_adapter.py +16 -0
- flwr/client/message_handler/message_handler.py +0 -2
- flwr/client/numpy_client.py +0 -44
- flwr/client/supernode/app.py +1 -2
- flwr/common/auth_plugin/auth_plugin.py +33 -23
- flwr/common/constant.py +2 -0
- flwr/common/grpc.py +154 -3
- flwr/common/record/recordset.py +1 -1
- flwr/common/secure_aggregation/crypto/symmetric_encryption.py +45 -0
- flwr/common/telemetry.py +13 -3
- flwr/common/typing.py +20 -0
- flwr/proto/exec_pb2.py +12 -24
- flwr/proto/exec_pb2.pyi +27 -54
- flwr/proto/fleet_pb2.py +40 -27
- flwr/proto/fleet_pb2.pyi +84 -0
- flwr/proto/fleet_pb2_grpc.py +66 -0
- flwr/proto/fleet_pb2_grpc.pyi +20 -0
- flwr/server/app.py +54 -33
- flwr/server/run_serverapp.py +8 -9
- flwr/server/serverapp/app.py +17 -2
- flwr/server/superlink/driver/serverappio_grpc.py +1 -1
- flwr/server/superlink/driver/serverappio_servicer.py +29 -6
- flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +2 -165
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +16 -0
- flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +2 -1
- flwr/server/superlink/fleet/vce/vce_api.py +2 -2
- flwr/server/superlink/linkstate/in_memory_linkstate.py +36 -24
- flwr/server/superlink/linkstate/linkstate.py +14 -4
- flwr/server/superlink/linkstate/sqlite_linkstate.py +56 -31
- flwr/server/superlink/simulation/simulationio_grpc.py +1 -1
- flwr/server/superlink/simulation/simulationio_servicer.py +13 -0
- flwr/simulation/app.py +15 -4
- flwr/simulation/run_simulation.py +35 -7
- flwr/superexec/exec_grpc.py +1 -1
- flwr/superexec/exec_servicer.py +23 -2
- {flwr_nightly-1.14.0.dev20241216.dist-info → flwr_nightly-1.15.0.dev20250112.dist-info}/METADATA +5 -5
- {flwr_nightly-1.14.0.dev20241216.dist-info → flwr_nightly-1.15.0.dev20250112.dist-info}/RECORD +60 -60
- {flwr_nightly-1.14.0.dev20241216.dist-info → flwr_nightly-1.15.0.dev20250112.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.14.0.dev20241216.dist-info → flwr_nightly-1.15.0.dev20250112.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.14.0.dev20241216.dist-info → flwr_nightly-1.15.0.dev20250112.dist-info}/entry_points.txt +0 -0
flwr/client/numpy_client.py
CHANGED
@@ -21,13 +21,11 @@ from typing import Callable
|
|
21
21
|
from flwr.client.client import Client
|
22
22
|
from flwr.common import (
|
23
23
|
Config,
|
24
|
-
Context,
|
25
24
|
NDArrays,
|
26
25
|
Scalar,
|
27
26
|
ndarrays_to_parameters,
|
28
27
|
parameters_to_ndarrays,
|
29
28
|
)
|
30
|
-
from flwr.common.logger import warn_deprecated_feature_with_example
|
31
29
|
from flwr.common.typing import (
|
32
30
|
Code,
|
33
31
|
EvaluateIns,
|
@@ -71,8 +69,6 @@ Example
|
|
71
69
|
class NumPyClient(ABC):
|
72
70
|
"""Abstract base class for Flower clients using NumPy."""
|
73
71
|
|
74
|
-
_context: Context
|
75
|
-
|
76
72
|
def get_properties(self, config: Config) -> dict[str, Scalar]:
|
77
73
|
"""Return a client's set of properties.
|
78
74
|
|
@@ -175,34 +171,6 @@ class NumPyClient(ABC):
|
|
175
171
|
_ = (self, parameters, config)
|
176
172
|
return 0.0, 0, {}
|
177
173
|
|
178
|
-
@property
|
179
|
-
def context(self) -> Context:
|
180
|
-
"""Getter for `Context` client attribute."""
|
181
|
-
warn_deprecated_feature_with_example(
|
182
|
-
"Accessing the context via the client's attribute is deprecated.",
|
183
|
-
example_message="Instead, pass it to the client's "
|
184
|
-
"constructor in your `client_fn()` which already "
|
185
|
-
"receives a context object.",
|
186
|
-
code_example="def client_fn(context: Context) -> Client:\n\n"
|
187
|
-
"\t\t# Your existing client_fn\n\n"
|
188
|
-
"\t\t# Pass `context` to the constructor\n"
|
189
|
-
"\t\treturn FlowerClient(context).to_client()",
|
190
|
-
)
|
191
|
-
return self._context
|
192
|
-
|
193
|
-
@context.setter
|
194
|
-
def context(self, context: Context) -> None:
|
195
|
-
"""Setter for `Context` client attribute."""
|
196
|
-
self._context = context
|
197
|
-
|
198
|
-
def get_context(self) -> Context:
|
199
|
-
"""Get the run context from this client."""
|
200
|
-
return self.context
|
201
|
-
|
202
|
-
def set_context(self, context: Context) -> None:
|
203
|
-
"""Apply a run context to this client."""
|
204
|
-
self.context = context
|
205
|
-
|
206
174
|
def to_client(self) -> Client:
|
207
175
|
"""Convert to object to Client type and return it."""
|
208
176
|
return _wrap_numpy_client(client=self)
|
@@ -299,21 +267,9 @@ def _evaluate(self: Client, ins: EvaluateIns) -> EvaluateRes:
|
|
299
267
|
)
|
300
268
|
|
301
269
|
|
302
|
-
def _get_context(self: Client) -> Context:
|
303
|
-
"""Return context of underlying NumPyClient."""
|
304
|
-
return self.numpy_client.get_context() # type: ignore
|
305
|
-
|
306
|
-
|
307
|
-
def _set_context(self: Client, context: Context) -> None:
|
308
|
-
"""Apply context to underlying NumPyClient."""
|
309
|
-
self.numpy_client.set_context(context) # type: ignore
|
310
|
-
|
311
|
-
|
312
270
|
def _wrap_numpy_client(client: NumPyClient) -> Client:
|
313
271
|
member_dict: dict[str, Callable] = { # type: ignore
|
314
272
|
"__init__": _constructor,
|
315
|
-
"get_context": _get_context,
|
316
|
-
"set_context": _set_context,
|
317
273
|
}
|
318
274
|
|
319
275
|
# Add wrapper type methods (if overridden)
|
flwr/client/supernode/app.py
CHANGED
@@ -114,9 +114,8 @@ def run_client_app() -> None:
|
|
114
114
|
event(EventType.RUN_CLIENT_APP_ENTER)
|
115
115
|
log(
|
116
116
|
ERROR,
|
117
|
-
"The command `flower-client-app` has been replaced by `
|
117
|
+
"The command `flower-client-app` has been replaced by `flwr run`.",
|
118
118
|
)
|
119
|
-
log(INFO, "Execute `flower-supernode --help` to learn how to use it.")
|
120
119
|
register_exit_handlers(event_type=EventType.RUN_CLIENT_APP_LEAVE)
|
121
120
|
|
122
121
|
|
@@ -18,26 +18,31 @@
|
|
18
18
|
from abc import ABC, abstractmethod
|
19
19
|
from collections.abc import Sequence
|
20
20
|
from pathlib import Path
|
21
|
-
from typing import
|
21
|
+
from typing import Optional, Union
|
22
22
|
|
23
23
|
from flwr.proto.exec_pb2_grpc import ExecStub
|
24
24
|
|
25
|
+
from ..typing import UserAuthCredentials, UserAuthLoginDetails
|
26
|
+
|
25
27
|
|
26
28
|
class ExecAuthPlugin(ABC):
|
27
29
|
"""Abstract Flower Auth Plugin class for ExecServicer.
|
28
30
|
|
29
31
|
Parameters
|
30
32
|
----------
|
31
|
-
|
32
|
-
|
33
|
+
user_auth_config_path : Path
|
34
|
+
Path to the YAML file containing the authentication configuration.
|
33
35
|
"""
|
34
36
|
|
35
37
|
@abstractmethod
|
36
|
-
def __init__(
|
38
|
+
def __init__(
|
39
|
+
self,
|
40
|
+
user_auth_config_path: Path,
|
41
|
+
):
|
37
42
|
"""Abstract constructor."""
|
38
43
|
|
39
44
|
@abstractmethod
|
40
|
-
def get_login_details(self) ->
|
45
|
+
def get_login_details(self) -> Optional[UserAuthLoginDetails]:
|
41
46
|
"""Get the login details."""
|
42
47
|
|
43
48
|
@abstractmethod
|
@@ -47,7 +52,7 @@ class ExecAuthPlugin(ABC):
|
|
47
52
|
"""Validate authentication tokens in the provided metadata."""
|
48
53
|
|
49
54
|
@abstractmethod
|
50
|
-
def get_auth_tokens(self,
|
55
|
+
def get_auth_tokens(self, device_code: str) -> Optional[UserAuthCredentials]:
|
51
56
|
"""Get authentication tokens."""
|
52
57
|
|
53
58
|
@abstractmethod
|
@@ -62,50 +67,55 @@ class CliAuthPlugin(ABC):
|
|
62
67
|
|
63
68
|
Parameters
|
64
69
|
----------
|
65
|
-
|
66
|
-
|
70
|
+
credentials_path : Path
|
71
|
+
Path to the user's authentication credentials file.
|
67
72
|
"""
|
68
73
|
|
69
74
|
@staticmethod
|
70
75
|
@abstractmethod
|
71
76
|
def login(
|
72
|
-
login_details:
|
77
|
+
login_details: UserAuthLoginDetails,
|
73
78
|
exec_stub: ExecStub,
|
74
|
-
) ->
|
75
|
-
"""Authenticate the user
|
79
|
+
) -> UserAuthCredentials:
|
80
|
+
"""Authenticate the user and retrieve authentication credentials.
|
76
81
|
|
77
82
|
Parameters
|
78
83
|
----------
|
79
|
-
login_details :
|
80
|
-
|
84
|
+
login_details : UserAuthLoginDetails
|
85
|
+
An object containing the user's login details.
|
81
86
|
exec_stub : ExecStub
|
82
|
-
|
87
|
+
A stub for executing RPC calls to the server.
|
83
88
|
|
84
89
|
Returns
|
85
90
|
-------
|
86
|
-
|
87
|
-
|
88
|
-
in JSON format.
|
91
|
+
UserAuthCredentials
|
92
|
+
The authentication credentials obtained after login.
|
89
93
|
"""
|
90
94
|
|
91
95
|
@abstractmethod
|
92
|
-
def __init__(self,
|
96
|
+
def __init__(self, credentials_path: Path):
|
93
97
|
"""Abstract constructor."""
|
94
98
|
|
95
99
|
@abstractmethod
|
96
|
-
def store_tokens(self,
|
97
|
-
"""Store authentication tokens
|
100
|
+
def store_tokens(self, credentials: UserAuthCredentials) -> None:
|
101
|
+
"""Store authentication tokens to the `credentials_path`.
|
98
102
|
|
99
|
-
The
|
100
|
-
at `
|
103
|
+
The credentials, including tokens, will be saved as a JSON file
|
104
|
+
at `credentials_path`.
|
101
105
|
"""
|
102
106
|
|
103
107
|
@abstractmethod
|
104
108
|
def load_tokens(self) -> None:
|
105
|
-
"""Load authentication tokens from the
|
109
|
+
"""Load authentication tokens from the `credentials_path`."""
|
106
110
|
|
107
111
|
@abstractmethod
|
108
112
|
def write_tokens_to_metadata(
|
109
113
|
self, metadata: Sequence[tuple[str, Union[str, bytes]]]
|
110
114
|
) -> Sequence[tuple[str, Union[str, bytes]]]:
|
111
115
|
"""Write authentication tokens to the provided metadata."""
|
116
|
+
|
117
|
+
@abstractmethod
|
118
|
+
def read_tokens_from_metadata(
|
119
|
+
self, metadata: Sequence[tuple[str, Union[str, bytes]]]
|
120
|
+
) -> Optional[UserAuthCredentials]:
|
121
|
+
"""Read authentication tokens from the provided metadata."""
|
flwr/common/constant.py
CHANGED
@@ -114,6 +114,8 @@ MAX_RETRY_DELAY = 20 # Maximum delay duration between two consecutive retries.
|
|
114
114
|
# Constants for user authentication
|
115
115
|
CREDENTIALS_DIR = ".credentials"
|
116
116
|
AUTH_TYPE = "auth_type"
|
117
|
+
ACCESS_TOKEN_KEY = "access_token"
|
118
|
+
REFRESH_TOKEN_KEY = "refresh_token"
|
117
119
|
|
118
120
|
|
119
121
|
class MessageType:
|
flwr/common/grpc.py
CHANGED
@@ -15,16 +15,26 @@
|
|
15
15
|
"""Utility functions for gRPC."""
|
16
16
|
|
17
17
|
|
18
|
+
import concurrent.futures
|
19
|
+
import sys
|
18
20
|
from collections.abc import Sequence
|
19
|
-
from logging import DEBUG
|
20
|
-
from typing import Optional
|
21
|
+
from logging import DEBUG, ERROR
|
22
|
+
from typing import Any, Callable, Optional
|
21
23
|
|
22
24
|
import grpc
|
23
25
|
|
24
|
-
from
|
26
|
+
from .address import is_port_in_use
|
27
|
+
from .logger import log
|
25
28
|
|
26
29
|
GRPC_MAX_MESSAGE_LENGTH: int = 536_870_912 # == 512 * 1024 * 1024
|
27
30
|
|
31
|
+
INVALID_CERTIFICATES_ERR_MSG = """
|
32
|
+
When setting any of root_certificate, certificate, or private_key,
|
33
|
+
all of them need to be set.
|
34
|
+
"""
|
35
|
+
|
36
|
+
AddServicerToServerFn = Callable[..., Any]
|
37
|
+
|
28
38
|
|
29
39
|
def create_channel(
|
30
40
|
server_address: str,
|
@@ -66,3 +76,144 @@ def create_channel(
|
|
66
76
|
channel = grpc.intercept_channel(channel, interceptors)
|
67
77
|
|
68
78
|
return channel
|
79
|
+
|
80
|
+
|
81
|
+
def valid_certificates(certificates: tuple[bytes, bytes, bytes]) -> bool:
|
82
|
+
"""Validate certificates tuple."""
|
83
|
+
is_valid = (
|
84
|
+
all(isinstance(certificate, bytes) for certificate in certificates)
|
85
|
+
and len(certificates) == 3
|
86
|
+
)
|
87
|
+
|
88
|
+
if not is_valid:
|
89
|
+
log(ERROR, INVALID_CERTIFICATES_ERR_MSG)
|
90
|
+
|
91
|
+
return is_valid
|
92
|
+
|
93
|
+
|
94
|
+
def generic_create_grpc_server( # pylint: disable=too-many-arguments,R0917
|
95
|
+
servicer_and_add_fn: tuple[Any, AddServicerToServerFn],
|
96
|
+
server_address: str,
|
97
|
+
max_concurrent_workers: int = 1000,
|
98
|
+
max_message_length: int = GRPC_MAX_MESSAGE_LENGTH,
|
99
|
+
keepalive_time_ms: int = 210000,
|
100
|
+
certificates: Optional[tuple[bytes, bytes, bytes]] = None,
|
101
|
+
interceptors: Optional[Sequence[grpc.ServerInterceptor]] = None,
|
102
|
+
) -> grpc.Server:
|
103
|
+
"""Create a gRPC server with a single servicer.
|
104
|
+
|
105
|
+
Parameters
|
106
|
+
----------
|
107
|
+
servicer_and_add_fn : tuple
|
108
|
+
A tuple holding a servicer implementation and a matching
|
109
|
+
add_Servicer_to_server function.
|
110
|
+
server_address : str
|
111
|
+
Server address in the form of HOST:PORT e.g. "[::]:8080"
|
112
|
+
max_concurrent_workers : int
|
113
|
+
Maximum number of clients the server can process before returning
|
114
|
+
RESOURCE_EXHAUSTED status (default: 1000)
|
115
|
+
max_message_length : int
|
116
|
+
Maximum message length that the server can send or receive.
|
117
|
+
Int valued in bytes. -1 means unlimited. (default: GRPC_MAX_MESSAGE_LENGTH)
|
118
|
+
keepalive_time_ms : int
|
119
|
+
Flower uses a default gRPC keepalive time of 210000ms (3 minutes 30 seconds)
|
120
|
+
because some cloud providers (for example, Azure) agressively clean up idle
|
121
|
+
TCP connections by terminating them after some time (4 minutes in the case
|
122
|
+
of Azure). Flower does not use application-level keepalive signals and relies
|
123
|
+
on the assumption that the transport layer will fail in cases where the
|
124
|
+
connection is no longer active. `keepalive_time_ms` can be used to customize
|
125
|
+
the keepalive interval for specific environments. The default Flower gRPC
|
126
|
+
keepalive of 210000 ms (3 minutes 30 seconds) ensures that Flower can keep
|
127
|
+
the long running streaming connection alive in most environments. The actual
|
128
|
+
gRPC default of this setting is 7200000 (2 hours), which results in dropped
|
129
|
+
connections in some cloud environments.
|
130
|
+
|
131
|
+
These settings are related to the issue described here:
|
132
|
+
- https://github.com/grpc/proposal/blob/master/A8-client-side-keepalive.md
|
133
|
+
- https://github.com/grpc/grpc/blob/master/doc/keepalive.md
|
134
|
+
- https://grpc.io/docs/guides/performance/
|
135
|
+
|
136
|
+
Mobile Flower clients may choose to increase this value if their server
|
137
|
+
environment allows long-running idle TCP connections.
|
138
|
+
(default: 210000)
|
139
|
+
certificates : Tuple[bytes, bytes, bytes] (default: None)
|
140
|
+
Tuple containing root certificate, server certificate, and private key to
|
141
|
+
start a secure SSL-enabled server. The tuple is expected to have three bytes
|
142
|
+
elements in the following order:
|
143
|
+
|
144
|
+
* CA certificate.
|
145
|
+
* server certificate.
|
146
|
+
* server private key.
|
147
|
+
interceptors : Optional[Sequence[grpc.ServerInterceptor]] (default: None)
|
148
|
+
A list of gRPC interceptors.
|
149
|
+
|
150
|
+
Returns
|
151
|
+
-------
|
152
|
+
server : grpc.Server
|
153
|
+
A non-running instance of a gRPC server.
|
154
|
+
"""
|
155
|
+
# Check if port is in use
|
156
|
+
if is_port_in_use(server_address):
|
157
|
+
sys.exit(f"Port in server address {server_address} is already in use.")
|
158
|
+
|
159
|
+
# Deconstruct tuple into servicer and function
|
160
|
+
servicer, add_servicer_to_server_fn = servicer_and_add_fn
|
161
|
+
|
162
|
+
# Possible options:
|
163
|
+
# https://github.com/grpc/grpc/blob/v1.43.x/include/grpc/impl/codegen/grpc_types.h
|
164
|
+
options = [
|
165
|
+
# Maximum number of concurrent incoming streams to allow on a http2
|
166
|
+
# connection. Int valued.
|
167
|
+
("grpc.max_concurrent_streams", max(100, max_concurrent_workers)),
|
168
|
+
# Maximum message length that the channel can send.
|
169
|
+
# Int valued, bytes. -1 means unlimited.
|
170
|
+
("grpc.max_send_message_length", max_message_length),
|
171
|
+
# Maximum message length that the channel can receive.
|
172
|
+
# Int valued, bytes. -1 means unlimited.
|
173
|
+
("grpc.max_receive_message_length", max_message_length),
|
174
|
+
# The gRPC default for this setting is 7200000 (2 hours). Flower uses a
|
175
|
+
# customized default of 210000 (3 minutes and 30 seconds) to improve
|
176
|
+
# compatibility with popular cloud providers. Mobile Flower clients may
|
177
|
+
# choose to increase this value if their server environment allows
|
178
|
+
# long-running idle TCP connections.
|
179
|
+
("grpc.keepalive_time_ms", keepalive_time_ms),
|
180
|
+
# Setting this to zero will allow sending unlimited keepalive pings in between
|
181
|
+
# sending actual data frames.
|
182
|
+
("grpc.http2.max_pings_without_data", 0),
|
183
|
+
# Is it permissible to send keepalive pings from the client without
|
184
|
+
# any outstanding streams. More explanation here:
|
185
|
+
# https://github.com/adap/flower/pull/2197
|
186
|
+
("grpc.keepalive_permit_without_calls", 0),
|
187
|
+
]
|
188
|
+
|
189
|
+
server = grpc.server(
|
190
|
+
concurrent.futures.ThreadPoolExecutor(max_workers=max_concurrent_workers),
|
191
|
+
# Set the maximum number of concurrent RPCs this server will service before
|
192
|
+
# returning RESOURCE_EXHAUSTED status, or None to indicate no limit.
|
193
|
+
maximum_concurrent_rpcs=max_concurrent_workers,
|
194
|
+
options=options,
|
195
|
+
interceptors=interceptors,
|
196
|
+
)
|
197
|
+
add_servicer_to_server_fn(servicer, server)
|
198
|
+
|
199
|
+
if certificates is not None:
|
200
|
+
if not valid_certificates(certificates):
|
201
|
+
sys.exit(1)
|
202
|
+
|
203
|
+
root_certificate_b, certificate_b, private_key_b = certificates
|
204
|
+
|
205
|
+
server_credentials = grpc.ssl_server_credentials(
|
206
|
+
((private_key_b, certificate_b),),
|
207
|
+
root_certificates=root_certificate_b,
|
208
|
+
# A boolean indicating whether or not to require clients to be
|
209
|
+
# authenticated. May only be True if root_certificates is not None.
|
210
|
+
# We are explicitly setting the current gRPC default to document
|
211
|
+
# the option. For further reference see:
|
212
|
+
# https://grpc.github.io/grpc/python/grpc.html#create-server-credentials
|
213
|
+
require_client_auth=False,
|
214
|
+
)
|
215
|
+
server.add_secure_port(server_address, server_credentials)
|
216
|
+
else:
|
217
|
+
server.add_insecure_port(server_address)
|
218
|
+
|
219
|
+
return server
|
flwr/common/record/recordset.py
CHANGED
@@ -151,7 +151,7 @@ class RecordSet:
|
|
151
151
|
>>> p_record = ParametersRecord({"my_array": arr})
|
152
152
|
>>>
|
153
153
|
>>> # Adding it to the record_set would look like this
|
154
|
-
>>> my_recordset.
|
154
|
+
>>> my_recordset.parameters_records["my_parameters"] = p_record
|
155
155
|
|
156
156
|
For additional examples on how to construct each of the records types shown
|
157
157
|
above, please refer to the documentation for :code:`ConfigsRecord`,
|
@@ -117,3 +117,48 @@ def verify_hmac(key: bytes, message: bytes, hmac_value: bytes) -> bool:
|
|
117
117
|
return True
|
118
118
|
except InvalidSignature:
|
119
119
|
return False
|
120
|
+
|
121
|
+
|
122
|
+
def sign_message(private_key: ec.EllipticCurvePrivateKey, message: bytes) -> bytes:
|
123
|
+
"""Sign a message using the provided EC private key.
|
124
|
+
|
125
|
+
Parameters
|
126
|
+
----------
|
127
|
+
private_key : ec.EllipticCurvePrivateKey
|
128
|
+
The EC private key to sign the message with.
|
129
|
+
message : bytes
|
130
|
+
The message to be signed.
|
131
|
+
|
132
|
+
Returns
|
133
|
+
-------
|
134
|
+
bytes
|
135
|
+
The signature of the message.
|
136
|
+
"""
|
137
|
+
signature = private_key.sign(message, ec.ECDSA(hashes.SHA256()))
|
138
|
+
return signature
|
139
|
+
|
140
|
+
|
141
|
+
def verify_signature(
|
142
|
+
public_key: ec.EllipticCurvePublicKey, message: bytes, signature: bytes
|
143
|
+
) -> bool:
|
144
|
+
"""Verify a signature against a message using the provided EC public key.
|
145
|
+
|
146
|
+
Parameters
|
147
|
+
----------
|
148
|
+
public_key : ec.EllipticCurvePublicKey
|
149
|
+
The EC public key to verify the signature.
|
150
|
+
message : bytes
|
151
|
+
The original message.
|
152
|
+
signature : bytes
|
153
|
+
The signature to verify.
|
154
|
+
|
155
|
+
Returns
|
156
|
+
-------
|
157
|
+
bool
|
158
|
+
True if the signature is valid, False otherwise.
|
159
|
+
"""
|
160
|
+
try:
|
161
|
+
public_key.verify(signature, message, ec.ECDSA(hashes.SHA256()))
|
162
|
+
return True
|
163
|
+
except InvalidSignature:
|
164
|
+
return False
|
flwr/common/telemetry.py
CHANGED
@@ -151,6 +151,16 @@ class EventType(str, Enum):
|
|
151
151
|
|
152
152
|
# Not yet implemented
|
153
153
|
|
154
|
+
# --- `flwr-*` commands ------------------------------------------------------------
|
155
|
+
|
156
|
+
# CLI: flwr-simulation
|
157
|
+
FLWR_SIMULATION_RUN_ENTER = auto()
|
158
|
+
FLWR_SIMULATION_RUN_LEAVE = auto()
|
159
|
+
|
160
|
+
# CLI: flwr-serverapp
|
161
|
+
FLWR_SERVERAPP_RUN_ENTER = auto()
|
162
|
+
FLWR_SERVERAPP_RUN_LEAVE = auto()
|
163
|
+
|
154
164
|
# --- Simulation Engine ------------------------------------------------------------
|
155
165
|
|
156
166
|
# CLI: flower-simulation
|
@@ -171,12 +181,12 @@ class EventType(str, Enum):
|
|
171
181
|
RUN_SUPERNODE_ENTER = auto()
|
172
182
|
RUN_SUPERNODE_LEAVE = auto()
|
173
183
|
|
174
|
-
#
|
184
|
+
# --- DEPRECATED -------------------------------------------------------------------
|
185
|
+
|
186
|
+
# [DEPRECATED] CLI: `flower-server-app`
|
175
187
|
RUN_SERVER_APP_ENTER = auto()
|
176
188
|
RUN_SERVER_APP_LEAVE = auto()
|
177
189
|
|
178
|
-
# --- DEPRECATED -------------------------------------------------------------------
|
179
|
-
|
180
190
|
# [DEPRECATED] CLI: `flower-client-app`
|
181
191
|
RUN_CLIENT_APP_ENTER = auto()
|
182
192
|
RUN_CLIENT_APP_LEAVE = auto()
|
flwr/common/typing.py
CHANGED
@@ -266,3 +266,23 @@ class InvalidRunStatusException(BaseException):
|
|
266
266
|
def __init__(self, message: str) -> None:
|
267
267
|
super().__init__(message)
|
268
268
|
self.message = message
|
269
|
+
|
270
|
+
|
271
|
+
# OIDC user authentication types
|
272
|
+
@dataclass
|
273
|
+
class UserAuthLoginDetails:
|
274
|
+
"""User authentication login details."""
|
275
|
+
|
276
|
+
auth_type: str
|
277
|
+
device_code: str
|
278
|
+
verification_uri_complete: str
|
279
|
+
expires_in: int
|
280
|
+
interval: int
|
281
|
+
|
282
|
+
|
283
|
+
@dataclass
|
284
|
+
class UserAuthCredentials:
|
285
|
+
"""User authentication tokens."""
|
286
|
+
|
287
|
+
access_token: str
|
288
|
+
refresh_token: str
|
flwr/proto/exec_pb2.py
CHANGED
@@ -18,7 +18,7 @@ from flwr.proto import recordset_pb2 as flwr_dot_proto_dot_recordset__pb2
|
|
18
18
|
from flwr.proto import run_pb2 as flwr_dot_proto_dot_run__pb2
|
19
19
|
|
20
20
|
|
21
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66lwr/proto/exec.proto\x12\nflwr.proto\x1a\x14\x66lwr/proto/fab.proto\x1a\x1a\x66lwr/proto/transport.proto\x1a\x1a\x66lwr/proto/recordset.proto\x1a\x14\x66lwr/proto/run.proto\"\xfb\x01\n\x0fStartRunRequest\x12\x1c\n\x03\x66\x61\x62\x18\x01 \x01(\x0b\x32\x0f.flwr.proto.Fab\x12H\n\x0foverride_config\x18\x02 \x03(\x0b\x32/.flwr.proto.StartRunRequest.OverrideConfigEntry\x12\x35\n\x12\x66\x65\x64\x65ration_options\x18\x03 \x01(\x0b\x32\x19.flwr.proto.ConfigsRecord\x1aI\n\x13OverrideConfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.flwr.proto.Scalar:\x02\x38\x01\"2\n\x10StartRunResponse\x12\x13\n\x06run_id\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\t\n\x07_run_id\"<\n\x11StreamLogsRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\x12\x17\n\x0f\x61\x66ter_timestamp\x18\x02 \x01(\x01\"B\n\x12StreamLogsResponse\x12\x12\n\nlog_output\x18\x01 \x01(\t\x12\x18\n\x10latest_timestamp\x18\x02 \x01(\x01\"1\n\x0fListRunsRequest\x12\x13\n\x06run_id\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\t\n\x07_run_id\"\x9d\x01\n\x10ListRunsResponse\x12;\n\x08run_dict\x18\x01 \x03(\x0b\x32).flwr.proto.ListRunsResponse.RunDictEntry\x12\x0b\n\x03now\x18\x02 \x01(\t\x1a?\n\x0cRunDictEntry\x12\x0b\n\x03key\x18\x01 \x01(\x04\x12\x1e\n\x05value\x18\x02 \x01(\x0b\x32\x0f.flwr.proto.Run:\x02\x38\x01\"\x18\n\x16GetLoginDetailsRequest\"\
|
21
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66lwr/proto/exec.proto\x12\nflwr.proto\x1a\x14\x66lwr/proto/fab.proto\x1a\x1a\x66lwr/proto/transport.proto\x1a\x1a\x66lwr/proto/recordset.proto\x1a\x14\x66lwr/proto/run.proto\"\xfb\x01\n\x0fStartRunRequest\x12\x1c\n\x03\x66\x61\x62\x18\x01 \x01(\x0b\x32\x0f.flwr.proto.Fab\x12H\n\x0foverride_config\x18\x02 \x03(\x0b\x32/.flwr.proto.StartRunRequest.OverrideConfigEntry\x12\x35\n\x12\x66\x65\x64\x65ration_options\x18\x03 \x01(\x0b\x32\x19.flwr.proto.ConfigsRecord\x1aI\n\x13OverrideConfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.flwr.proto.Scalar:\x02\x38\x01\"2\n\x10StartRunResponse\x12\x13\n\x06run_id\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\t\n\x07_run_id\"<\n\x11StreamLogsRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\x12\x17\n\x0f\x61\x66ter_timestamp\x18\x02 \x01(\x01\"B\n\x12StreamLogsResponse\x12\x12\n\nlog_output\x18\x01 \x01(\t\x12\x18\n\x10latest_timestamp\x18\x02 \x01(\x01\"1\n\x0fListRunsRequest\x12\x13\n\x06run_id\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\t\n\x07_run_id\"\x9d\x01\n\x10ListRunsResponse\x12;\n\x08run_dict\x18\x01 \x03(\x0b\x32).flwr.proto.ListRunsResponse.RunDictEntry\x12\x0b\n\x03now\x18\x02 \x01(\t\x1a?\n\x0cRunDictEntry\x12\x0b\n\x03key\x18\x01 \x01(\x04\x12\x1e\n\x05value\x18\x02 \x01(\x0b\x32\x0f.flwr.proto.Run:\x02\x38\x01\"\x18\n\x16GetLoginDetailsRequest\"\x8a\x01\n\x17GetLoginDetailsResponse\x12\x11\n\tauth_type\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65vice_code\x18\x02 \x01(\t\x12!\n\x19verification_uri_complete\x18\x03 \x01(\t\x12\x12\n\nexpires_in\x18\x04 \x01(\x03\x12\x10\n\x08interval\x18\x05 \x01(\x03\"+\n\x14GetAuthTokensRequest\x12\x13\n\x0b\x64\x65vice_code\x18\x01 \x01(\t\"D\n\x15GetAuthTokensResponse\x12\x14\n\x0c\x61\x63\x63\x65ss_token\x18\x01 \x01(\t\x12\x15\n\rrefresh_token\x18\x02 \x01(\t\" \n\x0eStopRunRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\"\"\n\x0fStopRunResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x32\xe5\x03\n\x04\x45xec\x12G\n\x08StartRun\x12\x1b.flwr.proto.StartRunRequest\x1a\x1c.flwr.proto.StartRunResponse\"\x00\x12\x44\n\x07StopRun\x12\x1a.flwr.proto.StopRunRequest\x1a\x1b.flwr.proto.StopRunResponse\"\x00\x12O\n\nStreamLogs\x12\x1d.flwr.proto.StreamLogsRequest\x1a\x1e.flwr.proto.StreamLogsResponse\"\x00\x30\x01\x12G\n\x08ListRuns\x12\x1b.flwr.proto.ListRunsRequest\x1a\x1c.flwr.proto.ListRunsResponse\"\x00\x12\\\n\x0fGetLoginDetails\x12\".flwr.proto.GetLoginDetailsRequest\x1a#.flwr.proto.GetLoginDetailsResponse\"\x00\x12V\n\rGetAuthTokens\x12 .flwr.proto.GetAuthTokensRequest\x1a!.flwr.proto.GetAuthTokensResponse\"\x00\x62\x06proto3')
|
22
22
|
|
23
23
|
_globals = globals()
|
24
24
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
@@ -29,12 +29,6 @@ if _descriptor._USE_C_DESCRIPTORS == False:
|
|
29
29
|
_globals['_STARTRUNREQUEST_OVERRIDECONFIGENTRY']._serialized_options = b'8\001'
|
30
30
|
_globals['_LISTRUNSRESPONSE_RUNDICTENTRY']._options = None
|
31
31
|
_globals['_LISTRUNSRESPONSE_RUNDICTENTRY']._serialized_options = b'8\001'
|
32
|
-
_globals['_GETLOGINDETAILSRESPONSE_LOGINDETAILSENTRY']._options = None
|
33
|
-
_globals['_GETLOGINDETAILSRESPONSE_LOGINDETAILSENTRY']._serialized_options = b'8\001'
|
34
|
-
_globals['_GETAUTHTOKENSREQUEST_AUTHDETAILSENTRY']._options = None
|
35
|
-
_globals['_GETAUTHTOKENSREQUEST_AUTHDETAILSENTRY']._serialized_options = b'8\001'
|
36
|
-
_globals['_GETAUTHTOKENSRESPONSE_AUTHTOKENSENTRY']._options = None
|
37
|
-
_globals['_GETAUTHTOKENSRESPONSE_AUTHTOKENSENTRY']._serialized_options = b'8\001'
|
38
32
|
_globals['_STARTRUNREQUEST']._serialized_start=138
|
39
33
|
_globals['_STARTRUNREQUEST']._serialized_end=389
|
40
34
|
_globals['_STARTRUNREQUEST_OVERRIDECONFIGENTRY']._serialized_start=316
|
@@ -54,21 +48,15 @@ if _descriptor._USE_C_DESCRIPTORS == False:
|
|
54
48
|
_globals['_GETLOGINDETAILSREQUEST']._serialized_start=784
|
55
49
|
_globals['_GETLOGINDETAILSREQUEST']._serialized_end=808
|
56
50
|
_globals['_GETLOGINDETAILSRESPONSE']._serialized_start=811
|
57
|
-
_globals['_GETLOGINDETAILSRESPONSE']._serialized_end=
|
58
|
-
_globals['
|
59
|
-
_globals['
|
60
|
-
_globals['
|
61
|
-
_globals['
|
62
|
-
_globals['
|
63
|
-
_globals['
|
64
|
-
_globals['
|
65
|
-
_globals['
|
66
|
-
_globals['
|
67
|
-
_globals['
|
68
|
-
_globals['_STOPRUNREQUEST']._serialized_start=1268
|
69
|
-
_globals['_STOPRUNREQUEST']._serialized_end=1300
|
70
|
-
_globals['_STOPRUNRESPONSE']._serialized_start=1302
|
71
|
-
_globals['_STOPRUNRESPONSE']._serialized_end=1336
|
72
|
-
_globals['_EXEC']._serialized_start=1339
|
73
|
-
_globals['_EXEC']._serialized_end=1824
|
51
|
+
_globals['_GETLOGINDETAILSRESPONSE']._serialized_end=949
|
52
|
+
_globals['_GETAUTHTOKENSREQUEST']._serialized_start=951
|
53
|
+
_globals['_GETAUTHTOKENSREQUEST']._serialized_end=994
|
54
|
+
_globals['_GETAUTHTOKENSRESPONSE']._serialized_start=996
|
55
|
+
_globals['_GETAUTHTOKENSRESPONSE']._serialized_end=1064
|
56
|
+
_globals['_STOPRUNREQUEST']._serialized_start=1066
|
57
|
+
_globals['_STOPRUNREQUEST']._serialized_end=1098
|
58
|
+
_globals['_STOPRUNRESPONSE']._serialized_start=1100
|
59
|
+
_globals['_STOPRUNRESPONSE']._serialized_end=1134
|
60
|
+
_globals['_EXEC']._serialized_start=1137
|
61
|
+
_globals['_EXEC']._serialized_end=1622
|
74
62
|
# @@protoc_insertion_point(module_scope)
|