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