flwr 1.14.0__py3-none-any.whl → 1.15.0__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/auth_plugin/__init__.py +31 -0
- flwr/cli/auth_plugin/oidc_cli_plugin.py +150 -0
- flwr/cli/cli_user_auth_interceptor.py +6 -2
- flwr/cli/config_utils.py +24 -147
- flwr/cli/constant.py +27 -0
- flwr/cli/install.py +1 -1
- flwr/cli/log.py +18 -3
- flwr/cli/login/login.py +43 -8
- flwr/cli/ls.py +14 -5
- flwr/cli/new/templates/app/README.md.tpl +3 -2
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +4 -4
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +2 -2
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +4 -4
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +2 -2
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +2 -2
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +2 -2
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +4 -4
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +3 -3
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +2 -2
- flwr/cli/run/run.py +21 -11
- flwr/cli/stop.py +13 -4
- flwr/cli/utils.py +54 -40
- flwr/client/app.py +36 -48
- flwr/client/clientapp/app.py +19 -25
- flwr/client/clientapp/utils.py +1 -1
- flwr/client/grpc_client/connection.py +1 -12
- flwr/client/grpc_rere_client/client_interceptor.py +19 -119
- flwr/client/grpc_rere_client/connection.py +46 -36
- flwr/client/grpc_rere_client/grpc_adapter.py +12 -12
- flwr/client/message_handler/task_handler.py +0 -17
- flwr/client/rest_client/connection.py +34 -26
- flwr/client/supernode/app.py +18 -72
- flwr/common/args.py +25 -47
- flwr/common/auth_plugin/auth_plugin.py +34 -23
- flwr/common/config.py +166 -16
- flwr/common/constant.py +22 -9
- flwr/common/differential_privacy.py +2 -1
- flwr/common/exit/__init__.py +24 -0
- flwr/common/exit/exit.py +99 -0
- flwr/common/exit/exit_code.py +93 -0
- flwr/common/exit_handlers.py +24 -10
- flwr/common/grpc.py +167 -4
- flwr/common/logger.py +26 -7
- flwr/common/record/recordset.py +1 -1
- flwr/common/secure_aggregation/crypto/symmetric_encryption.py +45 -0
- flwr/common/serde.py +6 -4
- flwr/common/typing.py +20 -0
- flwr/proto/clientappio_pb2.py +1 -1
- flwr/proto/error_pb2.py +1 -1
- flwr/proto/exec_pb2.py +13 -25
- flwr/proto/exec_pb2.pyi +27 -54
- flwr/proto/fab_pb2.py +1 -1
- flwr/proto/fleet_pb2.py +31 -31
- flwr/proto/fleet_pb2.pyi +23 -23
- flwr/proto/fleet_pb2_grpc.py +30 -30
- flwr/proto/fleet_pb2_grpc.pyi +20 -20
- flwr/proto/grpcadapter_pb2.py +1 -1
- flwr/proto/log_pb2.py +1 -1
- flwr/proto/message_pb2.py +1 -1
- flwr/proto/node_pb2.py +3 -3
- flwr/proto/node_pb2.pyi +1 -4
- flwr/proto/recordset_pb2.py +1 -1
- flwr/proto/run_pb2.py +1 -1
- flwr/proto/serverappio_pb2.py +24 -25
- flwr/proto/serverappio_pb2.pyi +26 -32
- flwr/proto/serverappio_pb2_grpc.py +28 -28
- flwr/proto/serverappio_pb2_grpc.pyi +16 -16
- flwr/proto/simulationio_pb2.py +1 -1
- flwr/proto/task_pb2.py +1 -1
- flwr/proto/transport_pb2.py +1 -1
- flwr/server/app.py +116 -128
- flwr/server/compat/app_utils.py +0 -1
- flwr/server/compat/driver_client_proxy.py +1 -2
- flwr/server/driver/grpc_driver.py +32 -27
- flwr/server/driver/inmemory_driver.py +2 -1
- flwr/server/serverapp/app.py +12 -10
- flwr/server/superlink/driver/serverappio_grpc.py +1 -1
- flwr/server/superlink/driver/serverappio_servicer.py +74 -48
- flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +20 -88
- flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +2 -165
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +25 -24
- flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +97 -168
- flwr/server/superlink/fleet/message_handler/message_handler.py +37 -24
- flwr/server/superlink/fleet/rest_rere/rest_api.py +16 -18
- flwr/server/superlink/fleet/vce/vce_api.py +2 -2
- flwr/server/superlink/linkstate/in_memory_linkstate.py +45 -75
- flwr/server/superlink/linkstate/linkstate.py +17 -38
- flwr/server/superlink/linkstate/sqlite_linkstate.py +81 -145
- flwr/server/superlink/linkstate/utils.py +18 -8
- flwr/server/superlink/simulation/simulationio_grpc.py +1 -1
- flwr/server/utils/validator.py +9 -34
- flwr/simulation/app.py +4 -6
- flwr/simulation/legacy_app.py +4 -2
- flwr/simulation/run_simulation.py +1 -1
- flwr/simulation/simulationio_connection.py +2 -1
- flwr/superexec/exec_grpc.py +1 -1
- flwr/superexec/exec_servicer.py +23 -2
- {flwr-1.14.0.dist-info → flwr-1.15.0.dist-info}/METADATA +8 -8
- {flwr-1.14.0.dist-info → flwr-1.15.0.dist-info}/RECORD +102 -96
- {flwr-1.14.0.dist-info → flwr-1.15.0.dist-info}/LICENSE +0 -0
- {flwr-1.14.0.dist-info → flwr-1.15.0.dist-info}/WHEEL +0 -0
- {flwr-1.14.0.dist-info → flwr-1.15.0.dist-info}/entry_points.txt +0 -0
flwr/common/grpc.py
CHANGED
|
@@ -15,16 +15,33 @@
|
|
|
15
15
|
"""Utility functions for gRPC."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
import concurrent.futures
|
|
19
|
+
import os
|
|
20
|
+
import sys
|
|
18
21
|
from collections.abc import Sequence
|
|
19
|
-
from logging import DEBUG
|
|
20
|
-
from typing import Optional
|
|
22
|
+
from logging import DEBUG, ERROR
|
|
23
|
+
from typing import Any, Callable, Optional
|
|
21
24
|
|
|
22
25
|
import grpc
|
|
23
26
|
|
|
24
|
-
from
|
|
27
|
+
from .address import is_port_in_use
|
|
28
|
+
from .logger import log
|
|
25
29
|
|
|
26
30
|
GRPC_MAX_MESSAGE_LENGTH: int = 536_870_912 # == 512 * 1024 * 1024
|
|
27
31
|
|
|
32
|
+
INVALID_CERTIFICATES_ERR_MSG = """
|
|
33
|
+
When setting any of root_certificate, certificate, or private_key,
|
|
34
|
+
all of them need to be set.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
AddServicerToServerFn = Callable[..., Any]
|
|
38
|
+
|
|
39
|
+
if "GRPC_VERBOSITY" not in os.environ:
|
|
40
|
+
os.environ["GRPC_VERBOSITY"] = "error"
|
|
41
|
+
# The following flags can be uncommented for debugging. Other possible values:
|
|
42
|
+
# https://github.com/grpc/grpc/blob/master/doc/environment_variables.md
|
|
43
|
+
# os.environ["GRPC_TRACE"] = "tcp,http"
|
|
44
|
+
|
|
28
45
|
|
|
29
46
|
def create_channel(
|
|
30
47
|
server_address: str,
|
|
@@ -63,6 +80,152 @@ def create_channel(
|
|
|
63
80
|
log(DEBUG, "Opened secure gRPC connection using certificates")
|
|
64
81
|
|
|
65
82
|
if interceptors is not None:
|
|
66
|
-
channel = grpc.intercept_channel(channel, interceptors)
|
|
83
|
+
channel = grpc.intercept_channel(channel, *interceptors)
|
|
67
84
|
|
|
68
85
|
return channel
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def valid_certificates(certificates: tuple[bytes, bytes, bytes]) -> bool:
|
|
89
|
+
"""Validate certificates tuple."""
|
|
90
|
+
is_valid = (
|
|
91
|
+
all(isinstance(certificate, bytes) for certificate in certificates)
|
|
92
|
+
and len(certificates) == 3
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
if not is_valid:
|
|
96
|
+
log(ERROR, INVALID_CERTIFICATES_ERR_MSG)
|
|
97
|
+
|
|
98
|
+
return is_valid
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def generic_create_grpc_server( # pylint: disable=too-many-arguments,R0917
|
|
102
|
+
servicer_and_add_fn: tuple[Any, AddServicerToServerFn],
|
|
103
|
+
server_address: str,
|
|
104
|
+
max_concurrent_workers: int = 1000,
|
|
105
|
+
max_message_length: int = GRPC_MAX_MESSAGE_LENGTH,
|
|
106
|
+
keepalive_time_ms: int = 210000,
|
|
107
|
+
certificates: Optional[tuple[bytes, bytes, bytes]] = None,
|
|
108
|
+
interceptors: Optional[Sequence[grpc.ServerInterceptor]] = None,
|
|
109
|
+
) -> grpc.Server:
|
|
110
|
+
"""Create a gRPC server with a single servicer.
|
|
111
|
+
|
|
112
|
+
Parameters
|
|
113
|
+
----------
|
|
114
|
+
servicer_and_add_fn : tuple
|
|
115
|
+
A tuple holding a servicer implementation and a matching
|
|
116
|
+
add_Servicer_to_server function.
|
|
117
|
+
server_address : str
|
|
118
|
+
Server address in the form of HOST:PORT e.g. "[::]:8080"
|
|
119
|
+
max_concurrent_workers : int
|
|
120
|
+
Maximum number of clients the server can process before returning
|
|
121
|
+
RESOURCE_EXHAUSTED status (default: 1000)
|
|
122
|
+
max_message_length : int
|
|
123
|
+
Maximum message length that the server can send or receive.
|
|
124
|
+
Int valued in bytes. -1 means unlimited. (default: GRPC_MAX_MESSAGE_LENGTH)
|
|
125
|
+
keepalive_time_ms : int
|
|
126
|
+
Flower uses a default gRPC keepalive time of 210000ms (3 minutes 30 seconds)
|
|
127
|
+
because some cloud providers (for example, Azure) agressively clean up idle
|
|
128
|
+
TCP connections by terminating them after some time (4 minutes in the case
|
|
129
|
+
of Azure). Flower does not use application-level keepalive signals and relies
|
|
130
|
+
on the assumption that the transport layer will fail in cases where the
|
|
131
|
+
connection is no longer active. `keepalive_time_ms` can be used to customize
|
|
132
|
+
the keepalive interval for specific environments. The default Flower gRPC
|
|
133
|
+
keepalive of 210000 ms (3 minutes 30 seconds) ensures that Flower can keep
|
|
134
|
+
the long running streaming connection alive in most environments. The actual
|
|
135
|
+
gRPC default of this setting is 7200000 (2 hours), which results in dropped
|
|
136
|
+
connections in some cloud environments.
|
|
137
|
+
|
|
138
|
+
These settings are related to the issue described here:
|
|
139
|
+
- https://github.com/grpc/proposal/blob/master/A8-client-side-keepalive.md
|
|
140
|
+
- https://github.com/grpc/grpc/blob/master/doc/keepalive.md
|
|
141
|
+
- https://grpc.io/docs/guides/performance/
|
|
142
|
+
|
|
143
|
+
Mobile Flower clients may choose to increase this value if their server
|
|
144
|
+
environment allows long-running idle TCP connections.
|
|
145
|
+
(default: 210000)
|
|
146
|
+
certificates : Tuple[bytes, bytes, bytes] (default: None)
|
|
147
|
+
Tuple containing root certificate, server certificate, and private key to
|
|
148
|
+
start a secure SSL-enabled server. The tuple is expected to have three bytes
|
|
149
|
+
elements in the following order:
|
|
150
|
+
|
|
151
|
+
* CA certificate.
|
|
152
|
+
* server certificate.
|
|
153
|
+
* server private key.
|
|
154
|
+
interceptors : Optional[Sequence[grpc.ServerInterceptor]] (default: None)
|
|
155
|
+
A list of gRPC interceptors.
|
|
156
|
+
|
|
157
|
+
Returns
|
|
158
|
+
-------
|
|
159
|
+
server : grpc.Server
|
|
160
|
+
A non-running instance of a gRPC server.
|
|
161
|
+
"""
|
|
162
|
+
# Check if port is in use
|
|
163
|
+
if is_port_in_use(server_address):
|
|
164
|
+
sys.exit(f"Port in server address {server_address} is already in use.")
|
|
165
|
+
|
|
166
|
+
# Deconstruct tuple into servicer and function
|
|
167
|
+
servicer, add_servicer_to_server_fn = servicer_and_add_fn
|
|
168
|
+
|
|
169
|
+
# Possible options:
|
|
170
|
+
# https://github.com/grpc/grpc/blob/v1.43.x/include/grpc/impl/codegen/grpc_types.h
|
|
171
|
+
options = [
|
|
172
|
+
# Maximum number of concurrent incoming streams to allow on a http2
|
|
173
|
+
# connection. Int valued.
|
|
174
|
+
("grpc.max_concurrent_streams", max(100, max_concurrent_workers)),
|
|
175
|
+
# Maximum message length that the channel can send.
|
|
176
|
+
# Int valued, bytes. -1 means unlimited.
|
|
177
|
+
("grpc.max_send_message_length", max_message_length),
|
|
178
|
+
# Maximum message length that the channel can receive.
|
|
179
|
+
# Int valued, bytes. -1 means unlimited.
|
|
180
|
+
("grpc.max_receive_message_length", max_message_length),
|
|
181
|
+
# The gRPC default for this setting is 7200000 (2 hours). Flower uses a
|
|
182
|
+
# customized default of 210000 (3 minutes and 30 seconds) to improve
|
|
183
|
+
# compatibility with popular cloud providers. Mobile Flower clients may
|
|
184
|
+
# choose to increase this value if their server environment allows
|
|
185
|
+
# long-running idle TCP connections.
|
|
186
|
+
("grpc.keepalive_time_ms", keepalive_time_ms),
|
|
187
|
+
# Setting this to zero will allow sending unlimited keepalive pings in between
|
|
188
|
+
# sending actual data frames.
|
|
189
|
+
("grpc.http2.max_pings_without_data", 0),
|
|
190
|
+
# Is it permissible to send keepalive pings from the client without
|
|
191
|
+
# any outstanding streams. More explanation here:
|
|
192
|
+
# https://github.com/adap/flower/pull/2197
|
|
193
|
+
("grpc.keepalive_permit_without_calls", 0),
|
|
194
|
+
]
|
|
195
|
+
|
|
196
|
+
server = grpc.server(
|
|
197
|
+
concurrent.futures.ThreadPoolExecutor(max_workers=max_concurrent_workers),
|
|
198
|
+
# Set the maximum number of concurrent RPCs this server will service before
|
|
199
|
+
# returning RESOURCE_EXHAUSTED status, or None to indicate no limit.
|
|
200
|
+
maximum_concurrent_rpcs=max_concurrent_workers,
|
|
201
|
+
options=options,
|
|
202
|
+
interceptors=interceptors,
|
|
203
|
+
)
|
|
204
|
+
add_servicer_to_server_fn(servicer, server)
|
|
205
|
+
|
|
206
|
+
if certificates is not None:
|
|
207
|
+
if not valid_certificates(certificates):
|
|
208
|
+
sys.exit(1)
|
|
209
|
+
|
|
210
|
+
root_certificate_b, certificate_b, private_key_b = certificates
|
|
211
|
+
|
|
212
|
+
server_credentials = grpc.ssl_server_credentials(
|
|
213
|
+
((private_key_b, certificate_b),),
|
|
214
|
+
root_certificates=root_certificate_b,
|
|
215
|
+
# A boolean indicating whether or not to require clients to be
|
|
216
|
+
# authenticated. May only be True if root_certificates is not None.
|
|
217
|
+
# We are explicitly setting the current gRPC default to document
|
|
218
|
+
# the option. For further reference see:
|
|
219
|
+
# https://grpc.github.io/grpc/python/grpc.html#create-server-credentials
|
|
220
|
+
require_client_auth=False,
|
|
221
|
+
)
|
|
222
|
+
server.add_secure_port(server_address, server_credentials)
|
|
223
|
+
else:
|
|
224
|
+
server.add_insecure_port(server_address)
|
|
225
|
+
|
|
226
|
+
return server
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def on_channel_state_change(channel_connectivity: str) -> None:
|
|
230
|
+
"""Log channel connectivity."""
|
|
231
|
+
log(DEBUG, channel_connectivity)
|
flwr/common/logger.py
CHANGED
|
@@ -17,12 +17,13 @@
|
|
|
17
17
|
|
|
18
18
|
import json as _json
|
|
19
19
|
import logging
|
|
20
|
+
import os
|
|
20
21
|
import re
|
|
21
22
|
import sys
|
|
22
23
|
import threading
|
|
23
24
|
import time
|
|
24
25
|
from io import StringIO
|
|
25
|
-
from logging import WARN, LogRecord
|
|
26
|
+
from logging import ERROR, WARN, LogRecord
|
|
26
27
|
from logging.handlers import HTTPHandler
|
|
27
28
|
from queue import Empty, Queue
|
|
28
29
|
from typing import TYPE_CHECKING, Any, Optional, TextIO, Union
|
|
@@ -42,6 +43,7 @@ from .constant import LOG_UPLOAD_INTERVAL
|
|
|
42
43
|
LOGGER_NAME = "flwr"
|
|
43
44
|
FLOWER_LOGGER = logging.getLogger(LOGGER_NAME)
|
|
44
45
|
FLOWER_LOGGER.setLevel(logging.DEBUG)
|
|
46
|
+
log = FLOWER_LOGGER.log # pylint: disable=invalid-name
|
|
45
47
|
|
|
46
48
|
LOG_COLORS = {
|
|
47
49
|
"DEBUG": "\033[94m", # Blue
|
|
@@ -101,7 +103,7 @@ class ConsoleHandler(StreamHandler):
|
|
|
101
103
|
|
|
102
104
|
|
|
103
105
|
def update_console_handler(
|
|
104
|
-
level: Optional[int] = None,
|
|
106
|
+
level: Optional[Union[int, str]] = None,
|
|
105
107
|
timestamps: Optional[bool] = None,
|
|
106
108
|
colored: Optional[bool] = None,
|
|
107
109
|
) -> None:
|
|
@@ -125,6 +127,27 @@ console_handler = ConsoleHandler(
|
|
|
125
127
|
console_handler.setLevel(logging.INFO)
|
|
126
128
|
FLOWER_LOGGER.addHandler(console_handler)
|
|
127
129
|
|
|
130
|
+
# Set log level via env var (show timestamps for `DEBUG`)
|
|
131
|
+
if log_level := os.getenv("FLWR_LOG_LEVEL"):
|
|
132
|
+
log_level = log_level.upper()
|
|
133
|
+
try:
|
|
134
|
+
is_debug = log_level == "DEBUG"
|
|
135
|
+
if is_debug:
|
|
136
|
+
log(
|
|
137
|
+
WARN,
|
|
138
|
+
"DEBUG logs enabled. Do not use this in production, as it may expose "
|
|
139
|
+
"sensitive details.",
|
|
140
|
+
)
|
|
141
|
+
update_console_handler(level=log_level, timestamps=is_debug, colored=True)
|
|
142
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
143
|
+
# Alert user but don't raise exception
|
|
144
|
+
log(
|
|
145
|
+
ERROR,
|
|
146
|
+
"Failed to set logging level %s. Using default level: %s",
|
|
147
|
+
log_level,
|
|
148
|
+
logging.getLevelName(console_handler.level),
|
|
149
|
+
)
|
|
150
|
+
|
|
128
151
|
|
|
129
152
|
class CustomHTTPHandler(HTTPHandler):
|
|
130
153
|
"""Custom HTTPHandler which overrides the mapLogRecords method."""
|
|
@@ -185,10 +208,6 @@ def configure(
|
|
|
185
208
|
FLOWER_LOGGER.addHandler(http_handler)
|
|
186
209
|
|
|
187
210
|
|
|
188
|
-
logger = logging.getLogger(LOGGER_NAME) # pylint: disable=invalid-name
|
|
189
|
-
log = logger.log # pylint: disable=invalid-name
|
|
190
|
-
|
|
191
|
-
|
|
192
211
|
def warn_preview_feature(name: str) -> None:
|
|
193
212
|
"""Warn the user when they use a preview feature."""
|
|
194
213
|
log(
|
|
@@ -320,7 +339,7 @@ def _log_uploader(
|
|
|
320
339
|
) -> None:
|
|
321
340
|
"""Upload logs to the SuperLink."""
|
|
322
341
|
exit_flag = False
|
|
323
|
-
node = Node(node_id=node_id
|
|
342
|
+
node = Node(node_id=node_id)
|
|
324
343
|
msgs: list[str] = []
|
|
325
344
|
while True:
|
|
326
345
|
# Fetch all messages from the queue
|
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/serde.py
CHANGED
|
@@ -21,6 +21,8 @@ from typing import Any, TypeVar, cast
|
|
|
21
21
|
|
|
22
22
|
from google.protobuf.message import Message as GrpcMessage
|
|
23
23
|
|
|
24
|
+
from flwr.common.constant import SUPERLINK_NODE_ID
|
|
25
|
+
|
|
24
26
|
# pylint: disable=E0611
|
|
25
27
|
from flwr.proto.clientappio_pb2 import ClientAppOutputCode, ClientAppOutputStatus
|
|
26
28
|
from flwr.proto.error_pb2 import Error as ProtoError
|
|
@@ -605,8 +607,8 @@ def message_to_taskins(message: Message) -> TaskIns:
|
|
|
605
607
|
group_id=md.group_id,
|
|
606
608
|
run_id=md.run_id,
|
|
607
609
|
task=Task(
|
|
608
|
-
producer=Node(node_id=
|
|
609
|
-
consumer=Node(node_id=md.dst_node_id
|
|
610
|
+
producer=Node(node_id=SUPERLINK_NODE_ID), # Assume driver node
|
|
611
|
+
consumer=Node(node_id=md.dst_node_id),
|
|
610
612
|
created_at=md.created_at,
|
|
611
613
|
ttl=md.ttl,
|
|
612
614
|
ancestry=[md.reply_to_message] if md.reply_to_message != "" else [],
|
|
@@ -659,8 +661,8 @@ def message_to_taskres(message: Message) -> TaskRes:
|
|
|
659
661
|
group_id=md.group_id,
|
|
660
662
|
run_id=md.run_id,
|
|
661
663
|
task=Task(
|
|
662
|
-
producer=Node(node_id=md.src_node_id
|
|
663
|
-
consumer=Node(node_id=
|
|
664
|
+
producer=Node(node_id=md.src_node_id),
|
|
665
|
+
consumer=Node(node_id=SUPERLINK_NODE_ID), # Assume driver node
|
|
664
666
|
created_at=md.created_at,
|
|
665
667
|
ttl=md.ttl,
|
|
666
668
|
ancestry=[md.reply_to_message] if md.reply_to_message != "" else [],
|
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/clientappio_pb2.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
3
|
# source: flwr/proto/clientappio.proto
|
|
4
|
-
# Protobuf Python Version: 4.25.
|
|
4
|
+
# Protobuf Python Version: 4.25.1
|
|
5
5
|
"""Generated protocol buffer code."""
|
|
6
6
|
from google.protobuf import descriptor as _descriptor
|
|
7
7
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
flwr/proto/error_pb2.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
3
|
# source: flwr/proto/error.proto
|
|
4
|
-
# Protobuf Python Version: 4.25.
|
|
4
|
+
# Protobuf Python Version: 4.25.1
|
|
5
5
|
"""Generated protocol buffer code."""
|
|
6
6
|
from google.protobuf import descriptor as _descriptor
|
|
7
7
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
flwr/proto/exec_pb2.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
3
|
# source: flwr/proto/exec.proto
|
|
4
|
-
# Protobuf Python Version: 4.25.
|
|
4
|
+
# Protobuf Python Version: 4.25.1
|
|
5
5
|
"""Generated protocol buffer code."""
|
|
6
6
|
from google.protobuf import descriptor as _descriptor
|
|
7
7
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
@@ -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)
|
flwr/proto/exec_pb2.pyi
CHANGED
|
@@ -143,77 +143,50 @@ global___GetLoginDetailsRequest = GetLoginDetailsRequest
|
|
|
143
143
|
|
|
144
144
|
class GetLoginDetailsResponse(google.protobuf.message.Message):
|
|
145
145
|
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
) -> None: ...
|
|
157
|
-
def ClearField(self, field_name: typing_extensions.Literal["key",b"key","value",b"value"]) -> None: ...
|
|
158
|
-
|
|
159
|
-
LOGIN_DETAILS_FIELD_NUMBER: builtins.int
|
|
160
|
-
@property
|
|
161
|
-
def login_details(self) -> google.protobuf.internal.containers.ScalarMap[typing.Text, typing.Text]: ...
|
|
146
|
+
AUTH_TYPE_FIELD_NUMBER: builtins.int
|
|
147
|
+
DEVICE_CODE_FIELD_NUMBER: builtins.int
|
|
148
|
+
VERIFICATION_URI_COMPLETE_FIELD_NUMBER: builtins.int
|
|
149
|
+
EXPIRES_IN_FIELD_NUMBER: builtins.int
|
|
150
|
+
INTERVAL_FIELD_NUMBER: builtins.int
|
|
151
|
+
auth_type: typing.Text
|
|
152
|
+
device_code: typing.Text
|
|
153
|
+
verification_uri_complete: typing.Text
|
|
154
|
+
expires_in: builtins.int
|
|
155
|
+
interval: builtins.int
|
|
162
156
|
def __init__(self,
|
|
163
157
|
*,
|
|
164
|
-
|
|
158
|
+
auth_type: typing.Text = ...,
|
|
159
|
+
device_code: typing.Text = ...,
|
|
160
|
+
verification_uri_complete: typing.Text = ...,
|
|
161
|
+
expires_in: builtins.int = ...,
|
|
162
|
+
interval: builtins.int = ...,
|
|
165
163
|
) -> None: ...
|
|
166
|
-
def ClearField(self, field_name: typing_extensions.Literal["
|
|
164
|
+
def ClearField(self, field_name: typing_extensions.Literal["auth_type",b"auth_type","device_code",b"device_code","expires_in",b"expires_in","interval",b"interval","verification_uri_complete",b"verification_uri_complete"]) -> None: ...
|
|
167
165
|
global___GetLoginDetailsResponse = GetLoginDetailsResponse
|
|
168
166
|
|
|
169
167
|
class GetAuthTokensRequest(google.protobuf.message.Message):
|
|
170
168
|
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
KEY_FIELD_NUMBER: builtins.int
|
|
174
|
-
VALUE_FIELD_NUMBER: builtins.int
|
|
175
|
-
key: typing.Text
|
|
176
|
-
value: typing.Text
|
|
177
|
-
def __init__(self,
|
|
178
|
-
*,
|
|
179
|
-
key: typing.Text = ...,
|
|
180
|
-
value: typing.Text = ...,
|
|
181
|
-
) -> None: ...
|
|
182
|
-
def ClearField(self, field_name: typing_extensions.Literal["key",b"key","value",b"value"]) -> None: ...
|
|
183
|
-
|
|
184
|
-
AUTH_DETAILS_FIELD_NUMBER: builtins.int
|
|
185
|
-
@property
|
|
186
|
-
def auth_details(self) -> google.protobuf.internal.containers.ScalarMap[typing.Text, typing.Text]: ...
|
|
169
|
+
DEVICE_CODE_FIELD_NUMBER: builtins.int
|
|
170
|
+
device_code: typing.Text
|
|
187
171
|
def __init__(self,
|
|
188
172
|
*,
|
|
189
|
-
|
|
173
|
+
device_code: typing.Text = ...,
|
|
190
174
|
) -> None: ...
|
|
191
|
-
def ClearField(self, field_name: typing_extensions.Literal["
|
|
175
|
+
def ClearField(self, field_name: typing_extensions.Literal["device_code",b"device_code"]) -> None: ...
|
|
192
176
|
global___GetAuthTokensRequest = GetAuthTokensRequest
|
|
193
177
|
|
|
194
178
|
class GetAuthTokensResponse(google.protobuf.message.Message):
|
|
195
179
|
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
key: typing.Text
|
|
201
|
-
value: typing.Text
|
|
202
|
-
def __init__(self,
|
|
203
|
-
*,
|
|
204
|
-
key: typing.Text = ...,
|
|
205
|
-
value: typing.Text = ...,
|
|
206
|
-
) -> None: ...
|
|
207
|
-
def ClearField(self, field_name: typing_extensions.Literal["key",b"key","value",b"value"]) -> None: ...
|
|
208
|
-
|
|
209
|
-
AUTH_TOKENS_FIELD_NUMBER: builtins.int
|
|
210
|
-
@property
|
|
211
|
-
def auth_tokens(self) -> google.protobuf.internal.containers.ScalarMap[typing.Text, typing.Text]: ...
|
|
180
|
+
ACCESS_TOKEN_FIELD_NUMBER: builtins.int
|
|
181
|
+
REFRESH_TOKEN_FIELD_NUMBER: builtins.int
|
|
182
|
+
access_token: typing.Text
|
|
183
|
+
refresh_token: typing.Text
|
|
212
184
|
def __init__(self,
|
|
213
185
|
*,
|
|
214
|
-
|
|
186
|
+
access_token: typing.Text = ...,
|
|
187
|
+
refresh_token: typing.Text = ...,
|
|
215
188
|
) -> None: ...
|
|
216
|
-
def ClearField(self, field_name: typing_extensions.Literal["
|
|
189
|
+
def ClearField(self, field_name: typing_extensions.Literal["access_token",b"access_token","refresh_token",b"refresh_token"]) -> None: ...
|
|
217
190
|
global___GetAuthTokensResponse = GetAuthTokensResponse
|
|
218
191
|
|
|
219
192
|
class StopRunRequest(google.protobuf.message.Message):
|
flwr/proto/fab_pb2.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
3
|
# source: flwr/proto/fab.proto
|
|
4
|
-
# Protobuf Python Version: 4.25.
|
|
4
|
+
# Protobuf Python Version: 4.25.1
|
|
5
5
|
"""Generated protocol buffer code."""
|
|
6
6
|
from google.protobuf import descriptor as _descriptor
|
|
7
7
|
from google.protobuf import descriptor_pool as _descriptor_pool
|