handshake.hpp
1 // ═══════════════════════════════════════════════════════════════════════════════ 2 // GENERATED BY CORNELL - DO NOT EDIT 3 // Source: Cornell.ExtractStateMachine 4 // 5 // This file is generated from the Lean specification in Cornell.StateMachine. 6 // The state machine types are extracted automatically via metaprogramming. 7 // The transition functions are direct translations of the Lean pattern matches. 8 // 9 // To regenerate: 10 // lake build cornell_sm_gen && lake exe cornell_sm_gen > cpp/include/cornell/handshake.hpp 11 // ═══════════════════════════════════════════════════════════════════════════════ 12 13 #pragma once 14 15 #include "cornell/protocol.hpp" 16 #include <algorithm> 17 #include <optional> 18 #include <set> 19 #include <string> 20 #include <utility> 21 #include <variant> 22 #include <vector> 23 24 namespace cornell { 25 26 // ═══════════════════════════════════════════════════════════════════════════════ 27 // HANDSHAKE CONFIGURATION 28 // Generated from: Cornell.StateMachine.HandshakeConfig 29 // ═══════════════════════════════════════════════════════════════════════════════ 30 31 struct HandshakeConfig { 32 ProtocolVersion serverVersion = CURRENT_PROTOCOL; 33 std::set<Feature> serverFeatures = {Feature::ReapiV2, Feature::CasSha256, Feature::StreamingNar}; 34 std::optional<ReapiConfig> reapiConfig; 35 std::string daemonVersion = "nix-serve-cas 0.1.0"; 36 TrustLevel trustLevel = TrustLevel::Trusted; 37 38 static HandshakeConfig default_config() { 39 HandshakeConfig cfg; 40 cfg.reapiConfig = ReapiConfig{ 41 "main", 42 DigestFunction::SHA256, 43 {Capability::CAS, Capability::ActionCache} 44 }; 45 return cfg; 46 } 47 }; 48 49 // ═══════════════════════════════════════════════════════════════════════════════ 50 // SERVER STATE 51 // Generated from: Cornell.StateMachine.ServerState 52 // ═══════════════════════════════════════════════════════════════════════════════ 53 54 struct SrvInit { 55 HandshakeConfig config; 56 }; 57 struct SrvVersioned { 58 HandshakeConfig config; 59 ProtocolVersion negotiated; 60 }; 61 struct SrvFeatures { 62 HandshakeConfig config; 63 ProtocolVersion negotiated; 64 std::set<Feature> active; 65 }; 66 struct SrvUpgrading { 67 HandshakeConfig config; 68 ProtocolVersion negotiated; 69 std::set<Feature> active; 70 }; 71 struct SrvNixReady { 72 ProtocolVersion version; 73 }; 74 struct SrvReapiReady { 75 ReapiConfig config; 76 }; 77 struct SrvFailed { 78 std::string reason; 79 }; 80 81 using ServerHandshake = std::variant< 82 SrvInit, 83 SrvVersioned, 84 SrvFeatures, 85 SrvUpgrading, 86 SrvNixReady, 87 SrvReapiReady, 88 SrvFailed 89 >; 90 91 // ═══════════════════════════════════════════════════════════════════════════════ 92 // SERVER EVENTS 93 // Generated from: Cornell.StateMachine.ServerEvent 94 // ═══════════════════════════════════════════════════════════════════════════════ 95 96 struct SrvEvClientHello { 97 ProtocolVersion clientVersion; 98 }; 99 struct SrvEvClientLegacy {}; 100 struct SrvEvClientFeatures { 101 std::set<Feature> features; 102 }; 103 struct SrvEvClientUpgradeResponse { 104 bool accept; 105 }; 106 107 using ServerEvent = std::variant< 108 SrvEvClientHello, 109 SrvEvClientLegacy, 110 SrvEvClientFeatures, 111 SrvEvClientUpgradeResponse 112 >; 113 114 // ═══════════════════════════════════════════════════════════════════════════════ 115 // SERVER ACTIONS 116 // Generated from: Cornell.StateMachine.ServerAction 117 // ═══════════════════════════════════════════════════════════════════════════════ 118 119 struct SrvActSendServerHello { 120 ProtocolVersion version; 121 }; 122 struct SrvActSendDaemonVersion { 123 std::string version; 124 }; 125 struct SrvActSendTrustLevel { 126 TrustLevel level; 127 }; 128 struct SrvActSendFeatures { 129 std::set<Feature> features; 130 }; 131 struct SrvActSendUpgradeOffer {}; 132 struct SrvActSendReapiConfig { 133 ReapiConfig config; 134 }; 135 struct SrvActReady {}; 136 struct SrvActFail { 137 std::string reason; 138 }; 139 140 using ServerAction = std::variant< 141 SrvActSendServerHello, 142 SrvActSendDaemonVersion, 143 SrvActSendTrustLevel, 144 SrvActSendFeatures, 145 SrvActSendUpgradeOffer, 146 SrvActSendReapiConfig, 147 SrvActReady, 148 SrvActFail 149 >; 150 151 // ═══════════════════════════════════════════════════════════════════════════════ 152 // CLIENT STATE 153 // Generated from: Cornell.StateMachine.ClientState 154 // ═══════════════════════════════════════════════════════════════════════════════ 155 156 struct CliInit { 157 ProtocolVersion clientVersion; 158 std::set<Feature> clientFeatures; 159 }; 160 struct CliSentHello { 161 ProtocolVersion clientVersion; 162 std::set<Feature> clientFeatures; 163 }; 164 struct CliVersioned { 165 ProtocolVersion negotiated; 166 std::set<Feature> clientFeatures; 167 }; 168 struct CliAwaitingUpgrade { 169 ProtocolVersion negotiated; 170 }; 171 struct CliNixReady { 172 ProtocolVersion version; 173 }; 174 struct CliReapiReady { 175 ReapiConfig config; 176 }; 177 struct CliFailed { 178 std::string reason; 179 }; 180 181 using ClientHandshake = std::variant< 182 CliInit, 183 CliSentHello, 184 CliVersioned, 185 CliAwaitingUpgrade, 186 CliNixReady, 187 CliReapiReady, 188 CliFailed 189 >; 190 191 // ═══════════════════════════════════════════════════════════════════════════════ 192 // CLIENT EVENTS 193 // Generated from: Cornell.StateMachine.ClientEvent 194 // ═══════════════════════════════════════════════════════════════════════════════ 195 196 struct CliEvServerHello { 197 ProtocolVersion version; 198 }; 199 struct CliEvDaemonVersion { 200 std::string version; 201 }; 202 struct CliEvTrustLevel { 203 TrustLevel level; 204 }; 205 struct CliEvServerFeatures { 206 std::set<Feature> features; 207 }; 208 struct CliEvUpgradeOffer {}; 209 struct CliEvReapiConfig { 210 ReapiConfig config; 211 }; 212 213 using ClientEvent = std::variant< 214 CliEvServerHello, 215 CliEvDaemonVersion, 216 CliEvTrustLevel, 217 CliEvServerFeatures, 218 CliEvUpgradeOffer, 219 CliEvReapiConfig 220 >; 221 222 // ═══════════════════════════════════════════════════════════════════════════════ 223 // CLIENT ACTIONS 224 // Generated from: Cornell.StateMachine.ClientAction 225 // ═══════════════════════════════════════════════════════════════════════════════ 226 227 struct CliActSendClientHello { 228 ProtocolVersion version; 229 }; 230 struct CliActSendLegacy {}; 231 struct CliActSendFeatures { 232 std::set<Feature> features; 233 }; 234 struct CliActSendUpgradeResponse { 235 bool accept; 236 }; 237 struct CliActReady {}; 238 struct CliActFail { 239 std::string reason; 240 }; 241 242 using ClientAction = std::variant< 243 CliActSendClientHello, 244 CliActSendLegacy, 245 CliActSendFeatures, 246 CliActSendUpgradeResponse, 247 CliActReady, 248 CliActFail 249 >; 250 251 // ═══════════════════════════════════════════════════════════════════════════════ 252 // SERVER TRANSITION FUNCTION 253 // Generated from: Cornell.StateMachine.serverTransition 254 // 255 // This is a direct translation of the Lean pattern match. Each case corresponds 256 // exactly to a case in the Lean definition. 257 // ═══════════════════════════════════════════════════════════════════════════════ 258 259 inline ServerHandshake init_server_handshake(const HandshakeConfig& config) { 260 return SrvInit{config}; 261 } 262 263 // Helper: compute feature intersection 264 // Generated from: Cornell.StateMachine.featureIntersection 265 inline std::set<Feature> feature_intersection( 266 const std::set<Feature>& server_features, 267 const std::set<Feature>& client_features 268 ) { 269 std::set<Feature> result; 270 for (auto f : server_features) { 271 if (client_features.count(f)) result.insert(f); 272 } 273 return result; 274 } 275 276 // Helper: check if upgrade should be offered 277 // Generated from: Cornell.StateMachine.shouldOfferUpgrade 278 inline bool should_offer_upgrade(const HandshakeConfig& config, const std::set<Feature>& active) { 279 return active.count(Feature::ReapiV2) && config.reapiConfig.has_value(); 280 } 281 282 inline std::pair<ServerHandshake, std::vector<ServerAction>> 283 server_step(const ServerHandshake& state, const ServerEvent& event) { 284 285 // Case: .init config, .clientHello clientVer 286 // Lean: | .init config, .clientHello clientVer => 287 // let negotiated := if clientVer.value < config.serverVersion.value 288 // then clientVer else config.serverVersion 289 // { next := .versioned config negotiated 290 // , actions := [.sendServerHello config.serverVersion] } 291 if (auto* s = std::get_if<SrvInit>(&state)) { 292 if (auto* e = std::get_if<SrvEvClientHello>(&event)) { 293 ProtocolVersion negotiated = (e->clientVersion.value < s->config.serverVersion.value) 294 ? e->clientVersion 295 : s->config.serverVersion; 296 return { 297 SrvVersioned{s->config, negotiated}, 298 {SrvActSendServerHello{s->config.serverVersion}} 299 }; 300 } 301 } 302 303 // Case: .versioned config negotiated, .clientLegacy 304 // Lean: | .versioned config negotiated, .clientLegacy => 305 // if negotiated.supports 38 then ... else ... 306 if (auto* s = std::get_if<SrvVersioned>(&state)) { 307 if (std::holds_alternative<SrvEvClientLegacy>(event)) { 308 if (s->negotiated.supports(38)) { 309 // Version 1.38+: expect features, stay in versioned 310 std::vector<ServerAction> actions; 311 if (s->negotiated.supports(33)) { 312 actions.push_back(SrvActSendDaemonVersion{s->config.daemonVersion}); 313 } 314 if (s->negotiated.supports(35)) { 315 actions.push_back(SrvActSendTrustLevel{s->config.trustLevel}); 316 } 317 return {*s, std::move(actions)}; 318 } else { 319 // Legacy: go straight to ready 320 std::vector<ServerAction> actions; 321 if (s->negotiated.supports(33)) { 322 actions.push_back(SrvActSendDaemonVersion{s->config.daemonVersion}); 323 } 324 if (s->negotiated.supports(35)) { 325 actions.push_back(SrvActSendTrustLevel{s->config.trustLevel}); 326 } 327 actions.push_back(SrvActReady{}); 328 return {SrvNixReady{s->negotiated}, std::move(actions)}; 329 } 330 } 331 332 // Case: .versioned config negotiated, .clientFeatures clientFeatures 333 // Lean: | .versioned config negotiated, .clientFeatures clientFeatures => 334 // let active := featureIntersection config.serverFeatures clientFeatures 335 // if shouldOfferUpgrade config active then ... else ... 336 if (auto* e = std::get_if<SrvEvClientFeatures>(&event)) { 337 auto active = feature_intersection(s->config.serverFeatures, e->features); 338 if (should_offer_upgrade(s->config, active)) { 339 return { 340 SrvUpgrading{s->config, s->negotiated, active}, 341 {SrvActSendFeatures{s->config.serverFeatures}, SrvActSendUpgradeOffer{}} 342 }; 343 } else { 344 return { 345 SrvNixReady{s->negotiated}, 346 {SrvActSendFeatures{s->config.serverFeatures}, SrvActReady{}} 347 }; 348 } 349 } 350 } 351 352 // Case: .upgrading config negotiated active, .clientUpgradeResponse accept 353 // Lean: | .upgrading config negotiated active, .clientUpgradeResponse accept => 354 // if accept then 355 // match config.reapiConfig with 356 // | some rc => ... 357 // | none => ... 358 // else ... 359 if (auto* s = std::get_if<SrvUpgrading>(&state)) { 360 if (auto* e = std::get_if<SrvEvClientUpgradeResponse>(&event)) { 361 if (e->accept) { 362 if (s->config.reapiConfig.has_value()) { 363 auto rc = *s->config.reapiConfig; 364 return { 365 SrvReapiReady{rc}, 366 {SrvActSendReapiConfig{rc}, SrvActReady{}} 367 }; 368 } else { 369 return { 370 SrvFailed{"REAPI config missing"}, 371 {SrvActFail{"REAPI config missing"}} 372 }; 373 } 374 } else { 375 return {SrvNixReady{s->negotiated}, {SrvActReady{}}}; 376 } 377 } 378 } 379 380 // Case: .nixReady _, _ (terminal) 381 // Lean: | .nixReady _, _ => 382 // { next := .failed "Already in terminal state", actions := [.fail "Already terminal"] } 383 if (std::holds_alternative<SrvNixReady>(state)) { 384 return {SrvFailed{"Already in terminal state"}, {SrvActFail{"Already terminal"}}}; 385 } 386 387 // Case: .reapiReady _, _ (terminal) 388 // Lean: | .reapiReady _, _ => ... 389 if (std::holds_alternative<SrvReapiReady>(state)) { 390 return {SrvFailed{"Already in terminal state"}, {SrvActFail{"Already terminal"}}}; 391 } 392 393 // Case: .failed reason, _ (absorb) 394 // Lean: | .failed reason, _ => { next := .failed reason, actions := [] } 395 if (auto* s = std::get_if<SrvFailed>(&state)) { 396 return {*s, {}}; 397 } 398 399 // Case: _, _ (invalid transition) 400 // Lean: | s, e => { next := .failed s!"Invalid event {repr e} in state" 401 // , actions := [.fail "Invalid transition"] } 402 return {SrvFailed{"Invalid transition"}, {SrvActFail{"Invalid transition"}}}; 403 } 404 405 inline bool is_server_terminal(const ServerHandshake& state) { 406 return std::holds_alternative<SrvNixReady>(state) || 407 std::holds_alternative<SrvReapiReady>(state) || 408 std::holds_alternative<SrvFailed>(state); 409 } 410 411 // ═══════════════════════════════════════════════════════════════════════════════ 412 // CLIENT TRANSITION FUNCTION 413 // Generated from: Cornell.StateMachine.clientTransition 414 // ═══════════════════════════════════════════════════════════════════════════════ 415 416 inline std::pair<ClientHandshake, std::vector<ClientAction>> 417 init_client_handshake(ProtocolVersion version, const std::set<Feature>& features) { 418 return { 419 CliSentHello{version, features}, 420 {CliActSendClientHello{version}} 421 }; 422 } 423 424 inline std::pair<ClientHandshake, std::vector<ClientAction>> 425 client_step(const ClientHandshake& state, const ClientEvent& event) { 426 427 // Case: .sentHello clientVersion clientFeatures, .serverHello serverVer 428 // Lean: | .sentHello clientVer clientFeatures, .serverHello serverVer => 429 // let negotiated := if clientVer.value < serverVer.value then clientVer else serverVer 430 // { next := .versioned negotiated clientFeatures 431 // , actions := [.sendLegacyFields] } 432 if (auto* s = std::get_if<CliSentHello>(&state)) { 433 if (auto* e = std::get_if<CliEvServerHello>(&event)) { 434 ProtocolVersion negotiated = (s->clientVersion.value < e->version.value) 435 ? s->clientVersion 436 : e->version; 437 return { 438 CliVersioned{negotiated, s->clientFeatures}, 439 {CliActSendLegacy{}} 440 }; 441 } 442 } 443 444 // Case: .versioned negotiated features, .serverDaemonVersion _ 445 // Lean: | .versioned negotiated features, .serverDaemonVersion _ => 446 // { next := .versioned negotiated features, actions := [] } 447 if (auto* s = std::get_if<CliVersioned>(&state)) { 448 if (std::holds_alternative<CliEvDaemonVersion>(event)) { 449 return {*s, {}}; 450 } 451 } 452 453 // Case: .versioned negotiated features, .serverTrustLevel _ 454 // Lean: | .versioned negotiated features, .serverTrustLevel _ => 455 // { next := .versioned negotiated features, actions := [] } 456 if (auto* s = std::get_if<CliVersioned>(&state)) { 457 if (std::holds_alternative<CliEvTrustLevel>(event)) { 458 return {*s, {}}; 459 } 460 } 461 462 // Case: .versioned negotiated clientFeatures, .serverFeatures serverFeatures 463 // Lean: | .versioned negotiated clientFeatures, .serverFeatures serverFeatures => 464 // let active := featureIntersection clientFeatures serverFeatures 465 // if active.contains .reapiV2 then ... else ... 466 if (auto* s = std::get_if<CliVersioned>(&state)) { 467 if (auto* e = std::get_if<CliEvServerFeatures>(&event)) { 468 auto active = feature_intersection(s->clientFeatures, e->features); 469 std::vector<ClientAction> actions; 470 actions.push_back(CliActSendFeatures{s->clientFeatures}); 471 472 if (active.count(Feature::ReapiV2)) { 473 return {CliAwaitingUpgrade{s->negotiated}, std::move(actions)}; 474 } else { 475 actions.push_back(CliActReady{}); 476 return {CliNixReady{s->negotiated}, std::move(actions)}; 477 } 478 } 479 } 480 481 // Case: .awaitingUpgrade negotiated, .upgradeOffer 482 // Lean: | .awaitingUpgrade negotiated, .upgradeOffer => 483 // { next := .awaitingUpgrade negotiated, actions := [.sendUpgradeResponse true] } 484 if (auto* s = std::get_if<CliAwaitingUpgrade>(&state)) { 485 if (std::holds_alternative<CliEvUpgradeOffer>(event)) { 486 return {*s, {CliActSendUpgradeResponse{true}}}; 487 } 488 } 489 490 // Case: .awaitingUpgrade _, .reapiConfig config 491 // Lean: | .awaitingUpgrade _, .reapiConfig config => 492 // { next := .reapiReady config, actions := [.ready] } 493 if (auto* s = std::get_if<CliAwaitingUpgrade>(&state)) { 494 if (auto* e = std::get_if<CliEvReapiConfig>(&event)) { 495 return {CliReapiReady{e->config}, {CliActReady{}}}; 496 } 497 } 498 499 // Terminal states 500 // Lean: | .nixReady _, _ => { next := .failed "Already terminal", actions := [] } 501 if (std::holds_alternative<CliNixReady>(state)) { 502 return {CliFailed{"Already terminal"}, {}}; 503 } 504 505 // Lean: | .reapiReady _, _ => { next := .failed "Already terminal", actions := [] } 506 if (std::holds_alternative<CliReapiReady>(state)) { 507 return {CliFailed{"Already terminal"}, {}}; 508 } 509 510 // Lean: | .failed reason, _ => { next := .failed reason, actions := [] } 511 if (auto* s = std::get_if<CliFailed>(&state)) { 512 return {*s, {}}; 513 } 514 515 // Catch-all: invalid transition 516 // Lean: | _, _ => { next := .failed "Invalid transition", actions := [.fail "Invalid transition"] } 517 return {CliFailed{"Invalid transition"}, {CliActFail{"Invalid transition"}}}; 518 } 519 520 inline bool is_client_terminal(const ClientHandshake& state) { 521 return std::holds_alternative<CliNixReady>(state) || 522 std::holds_alternative<CliReapiReady>(state) || 523 std::holds_alternative<CliFailed>(state); 524 } 525 526 inline ProtocolVersion negotiate_version(ProtocolVersion client, ProtocolVersion server) { 527 return std::min(client, server); 528 } 529 530 } // namespace cornell 531