/ programs / forge_alpha / src / main.adl
main.adl
  1  // ACDC Forge - Alpha Chain Tech Governor Voting
  2  // Controls: alphavm, alphaos, acdc-core (shared), sdk (shared)
  3  
  4  program forge_alpha.alpha {
  5      // ============================================
  6      // STRUCTS
  7      // ============================================
  8  
  9      // Contributor reputation for rewards/punishment system
 10      struct Contributor {
 11          prs_submitted: u32,
 12          prs_merged: u32,
 13          prs_rejected: u32,
 14          behavior_flags: u32,
 15          reward_points: u64,
 16          registered_at: u64
 17      }
 18  
 19      // Tech Governor - nominated by Alpha Governors
 20      struct TechGovernor {
 21          nominating_governor: address,
 22          registered_at: u64,
 23          vote_count: u32,
 24          status: u8  // 0=inactive, 1=active
 25      }
 26  
 27      // Pull Request metadata
 28      struct PullRequest {
 29          commit_hash: field,
 30          submitter: address,
 31          repo_id: field,
 32          sponsor: address,
 33          sponsored_at: u64,
 34          vote_deadline: u64,
 35          yes_votes: u32,
 36          no_votes: u32,
 37          status: u8,  // 0=draft, 1=voting, 2=passed, 3=failed, 4=proposal_created, 5=executed
 38          behavior_flags: u8,
 39          governance_proposal_id: u128,  // Linked governance.alpha proposal
 40          is_shared_repo: bool           // Requires Delta vote too
 41      }
 42  
 43      // Resubmission tracking
 44      struct ResubmissionCooldown {
 45          original_pr_hash: field,
 46          failed_at: u64,
 47          cooldown_until: u64,  // 48 hours after failure
 48          submitter: address
 49      }
 50  
 51      // Cross-chain attestation for shared repos
 52      struct CrossChainAttestation {
 53          pr_hash: field,
 54          alpha_passed: bool,
 55          delta_passed: bool,
 56          alpha_proposal_id: u128,
 57          delta_proposal_id: u128,
 58          finalized_at: u64
 59      }
 60  
 61      // Individual vote record
 62      struct Vote {
 63          pr_hash: field,
 64          voter: address,
 65          vote_type: u8,  // 0=no, 1=yes, 2=abstain
 66          flags: u8,      // bit flags: security|political|economic
 67          cast_at: u64
 68      }
 69  
 70      // Code checkpoint with signatures
 71      struct Checkpoint {
 72          commit_hash: field,
 73          radicle_oid: field,
 74          approver_count: u8,
 75          finalized_at: u64
 76      }
 77  
 78      // ============================================
 79      // MAPPINGS (On-Chain Storage)
 80      // ============================================
 81  
 82      mapping tech_governors: address => TechGovernor;
 83      mapping pr_registry: field => PullRequest;
 84      mapping votes: field => Vote;
 85      mapping checkpoints: field => Checkpoint;
 86      mapping contributors: address => Contributor;
 87  
 88      // Counter for total governors (for threshold calculation)
 89      mapping governor_count: u8 => u32;
 90  
 91      // Governance integration
 92      mapping resubmission_cooldowns: field => ResubmissionCooldown;  // commit_hash => cooldown
 93      mapping cross_chain_attestations: field => CrossChainAttestation;  // pr_hash => attestation
 94      mapping proposal_to_pr: u128 => field;  // governance proposal_id => pr_hash
 95  
 96      // Shared repo IDs (alphavm, alphaos, acdc-core, sdk, adl)
 97      // These require votes on both Alpha and Delta chains
 98      mapping shared_repos: field => bool;
 99  
100      // ============================================
101      // TRANSITIONS
102      // ============================================
103  
104      // Register a new Tech Governor (called by Alpha Governor)
105      transition register_tech_governor(
106          public tech_gov_address: address,
107          public registered_at: u64
108      ) -> TechGovernor {
109          let gov: TechGovernor = TechGovernor {
110              nominating_governor: self.caller,
111              registered_at: registered_at,
112              vote_count: 0u32,
113              status: 1u8
114          };
115          return gov then finalize(tech_gov_address, gov);
116      }
117  
118      finalize register_tech_governor(
119          tech_gov_address: address,
120          gov: TechGovernor
121      ) {
122          // Store the governor
123          Mapping::set(tech_governors, tech_gov_address, gov);
124  
125          // Increment governor count
126          let current_count: u32 = Mapping::get_or_use(governor_count, 0u8, 0u32);
127          Mapping::set(governor_count, 0u8, current_count + 1u32);
128      }
129  
130      // Submit a new PR (registers it on-chain)
131      transition submit_pr(
132          public commit_hash: field,
133          public repo_id: field,
134          public submitted_at: u64,
135          public is_shared_repo: bool
136      ) -> PullRequest {
137          let pr: PullRequest = PullRequest {
138              commit_hash: commit_hash,
139              submitter: self.caller,
140              repo_id: repo_id,
141              sponsor: self.caller,  // Placeholder, updated on sponsor
142              sponsored_at: 0u64,
143              vote_deadline: 0u64,
144              yes_votes: 0u32,
145              no_votes: 0u32,
146              status: 0u8,  // draft
147              behavior_flags: 0u8,
148              governance_proposal_id: 0u128,
149              is_shared_repo: is_shared_repo
150          };
151  
152          // PR hash is the commit hash for lookup
153          return pr then finalize(commit_hash, pr, self.caller, submitted_at);
154      }
155  
156      finalize submit_pr(
157          pr_hash: field,
158          pr: PullRequest,
159          submitter: address,
160          submitted_at: u64
161      ) {
162          // Check resubmission cooldown
163          let cooldown: ResubmissionCooldown = Mapping::get_or_use(
164              resubmission_cooldowns,
165              pr_hash,
166              ResubmissionCooldown {
167                  original_pr_hash: 0field,
168                  failed_at: 0u64,
169                  cooldown_until: 0u64,
170                  submitter: submitter
171              }
172          );
173  
174          // If cooldown exists and hasn't expired, reject submission
175          assert(cooldown.cooldown_until == 0u64 || submitted_at >= cooldown.cooldown_until);
176  
177          // Store the PR
178          Mapping::set(pr_registry, pr_hash, pr);
179  
180          // Update contributor stats
181          let contributor: Contributor = Mapping::get_or_use(
182              contributors,
183              submitter,
184              Contributor {
185                  prs_submitted: 0u32,
186                  prs_merged: 0u32,
187                  prs_rejected: 0u32,
188                  behavior_flags: 0u32,
189                  reward_points: 0u64,
190                  registered_at: 0u64
191              }
192          );
193  
194          let updated_contributor: Contributor = Contributor {
195              prs_submitted: contributor.prs_submitted + 1u32,
196              prs_merged: contributor.prs_merged,
197              prs_rejected: contributor.prs_rejected,
198              behavior_flags: contributor.behavior_flags,
199              reward_points: contributor.reward_points,
200              registered_at: contributor.registered_at
201          };
202  
203          Mapping::set(contributors, submitter, updated_contributor);
204      }
205  
206      // Sponsor a PR (push to vote) - only Tech Governors can sponsor
207      transition sponsor_pr(
208          public pr_hash: field,
209          public sponsored_at: u64
210      ) -> field {
211          // Vote deadline is 7 days (604800 seconds) from sponsor time
212          let vote_deadline: u64 = sponsored_at + 604800u64;
213          return pr_hash then finalize(pr_hash, self.caller, sponsored_at, vote_deadline);
214      }
215  
216      finalize sponsor_pr(
217          pr_hash: field,
218          sponsor: address,
219          sponsored_at: u64,
220          vote_deadline: u64
221      ) {
222          // Verify sponsor is a registered Tech Governor
223          let gov: TechGovernor = Mapping::get(tech_governors, sponsor);
224          assert_eq(gov.status, 1u8);  // Must be active
225  
226          // Get current PR
227          let pr: PullRequest = Mapping::get(pr_registry, pr_hash);
228          assert_eq(pr.status, 0u8);  // Must be in draft status
229  
230          // Update PR with sponsor info
231          let updated_pr: PullRequest = PullRequest {
232              commit_hash: pr.commit_hash,
233              submitter: pr.submitter,
234              repo_id: pr.repo_id,
235              sponsor: sponsor,
236              sponsored_at: sponsored_at,
237              vote_deadline: vote_deadline,
238              yes_votes: 0u32,
239              no_votes: 0u32,
240              status: 1u8,  // voting
241              behavior_flags: 0u8,
242              governance_proposal_id: 0u128,
243              is_shared_repo: pr.is_shared_repo
244          };
245  
246          Mapping::set(pr_registry, pr_hash, updated_pr);
247      }
248  
249      // Cast a vote on a PR
250      transition cast_vote(
251          public pr_hash: field,
252          public vote_type: u8,
253          public flags: u8,
254          public cast_at: u64
255      ) -> Vote {
256          let vote: Vote = Vote {
257              pr_hash: pr_hash,
258              voter: self.caller,
259              vote_type: vote_type,
260              flags: flags,
261              cast_at: cast_at
262          };
263  
264          // Vote key combines PR hash and voter for uniqueness
265          let vote_key: field = BHP256::hash_to_field(Vote {
266              pr_hash: pr_hash,
267              voter: self.caller,
268              vote_type: 0u8,
269              flags: 0u8,
270              cast_at: 0u64
271          });
272  
273          return vote then finalize(vote_key, vote, pr_hash, vote_type, flags);
274      }
275  
276      finalize cast_vote(
277          vote_key: field,
278          vote: Vote,
279          pr_hash: field,
280          vote_type: u8,
281          flags: u8
282      ) {
283          // Verify voter is a registered Tech Governor
284          let gov: TechGovernor = Mapping::get(tech_governors, vote.voter);
285          assert_eq(gov.status, 1u8);  // Must be active
286  
287          // Get current PR
288          let pr: PullRequest = Mapping::get(pr_registry, pr_hash);
289          assert_eq(pr.status, 1u8);  // Must be in voting status
290  
291          // Store the vote
292          Mapping::set(votes, vote_key, vote);
293  
294          // Update vote counts
295          let new_yes: u32 = pr.yes_votes + (vote_type == 1u8 ? 1u32 : 0u32);
296          let new_no: u32 = pr.no_votes + (vote_type == 0u8 ? 1u32 : 0u32);
297          let new_flags: u8 = pr.behavior_flags | flags;
298  
299          let updated_pr: PullRequest = PullRequest {
300              commit_hash: pr.commit_hash,
301              submitter: pr.submitter,
302              repo_id: pr.repo_id,
303              sponsor: pr.sponsor,
304              sponsored_at: pr.sponsored_at,
305              vote_deadline: pr.vote_deadline,
306              yes_votes: new_yes,
307              no_votes: new_no,
308              status: 1u8,
309              behavior_flags: new_flags,
310              governance_proposal_id: pr.governance_proposal_id,
311              is_shared_repo: pr.is_shared_repo
312          };
313  
314          Mapping::set(pr_registry, pr_hash, updated_pr);
315  
316          // Update governor vote count
317          let updated_gov: TechGovernor = TechGovernor {
318              nominating_governor: gov.nominating_governor,
319              registered_at: gov.registered_at,
320              vote_count: gov.vote_count + 1u32,
321              status: gov.status
322          };
323  
324          Mapping::set(tech_governors, vote.voter, updated_gov);
325      }
326  
327      // Finalize voting and determine outcome
328      transition finalize_vote(
329          public pr_hash: field,
330          public finalized_at: u64
331      ) -> u8 {
332          return 0u8 then finalize(pr_hash, finalized_at);
333      }
334  
335      finalize finalize_vote(
336          pr_hash: field,
337          finalized_at: u64
338      ) {
339          let pr: PullRequest = Mapping::get(pr_registry, pr_hash);
340          assert_eq(pr.status, 1u8);  // Must be in voting status
341  
342          // Get total governor count for threshold calculation
343          let total_govs: u32 = Mapping::get_or_use(governor_count, 0u8, 1u32);
344  
345          // Calculate 67% threshold
346          let threshold: u32 = (total_govs * 67u32) / 100u32;
347  
348          // Determine outcome
349          let passed: bool = pr.yes_votes >= threshold;
350          let new_status: u8 = passed ? 2u8 : 3u8;  // 2=passed, 3=failed
351  
352          let updated_pr: PullRequest = PullRequest {
353              commit_hash: pr.commit_hash,
354              submitter: pr.submitter,
355              repo_id: pr.repo_id,
356              sponsor: pr.sponsor,
357              sponsored_at: pr.sponsored_at,
358              vote_deadline: pr.vote_deadline,
359              yes_votes: pr.yes_votes,
360              no_votes: pr.no_votes,
361              status: new_status,
362              behavior_flags: pr.behavior_flags,
363              governance_proposal_id: pr.governance_proposal_id,
364              is_shared_repo: pr.is_shared_repo
365          };
366  
367          Mapping::set(pr_registry, pr_hash, updated_pr);
368  
369          // If failed, set 48-hour cooldown for resubmission
370          if (!passed) {
371              let cooldown: ResubmissionCooldown = ResubmissionCooldown {
372                  original_pr_hash: pr_hash,
373                  failed_at: finalized_at,
374                  cooldown_until: finalized_at + 172800u64,  // 48 hours
375                  submitter: pr.submitter
376              };
377              Mapping::set(resubmission_cooldowns, pr.commit_hash, cooldown);
378          }
379  
380          // Update contributor stats
381          let contributor: Contributor = Mapping::get(contributors, pr.submitter);
382  
383          let updated_contributor: Contributor = Contributor {
384              prs_submitted: contributor.prs_submitted,
385              prs_merged: contributor.prs_merged + (passed ? 1u32 : 0u32),
386              prs_rejected: contributor.prs_rejected + (passed ? 0u32 : 1u32),
387              behavior_flags: contributor.behavior_flags + (pr.behavior_flags as u32),
388              reward_points: contributor.reward_points + (passed ? 100u64 : 0u64),
389              registered_at: contributor.registered_at
390          };
391  
392          Mapping::set(contributors, pr.submitter, updated_contributor);
393  
394          // For shared repos, initialize cross-chain attestation
395          if (pr.is_shared_repo && passed) {
396              let attestation: CrossChainAttestation = CrossChainAttestation {
397                  pr_hash: pr_hash,
398                  alpha_passed: true,
399                  delta_passed: false,
400                  alpha_proposal_id: 0u128,
401                  delta_proposal_id: 0u128,
402                  finalized_at: 0u64
403              };
404              Mapping::set(cross_chain_attestations, pr_hash, attestation);
405          }
406      }
407  
408      // Create governance proposal for passed PR
409      // Called after finalize_vote when PR passes and isn't a shared repo
410      // (shared repos wait for both chains to pass)
411      transition create_governance_proposal(
412          public pr_hash: field,
413          public proposal_id: u128
414      ) -> u128 {
415          return proposal_id then finalize(pr_hash, proposal_id);
416      }
417  
418      finalize create_governance_proposal(
419          pr_hash: field,
420          proposal_id: u128
421      ) {
422          let pr: PullRequest = Mapping::get(pr_registry, pr_hash);
423          assert_eq(pr.status, 2u8);  // Must be passed
424  
425          // For shared repos, check if both chains have passed
426          if (pr.is_shared_repo) {
427              let attestation: CrossChainAttestation = Mapping::get(cross_chain_attestations, pr_hash);
428              assert(attestation.alpha_passed && attestation.delta_passed);
429          }
430  
431          // Link proposal to PR
432          let updated_pr: PullRequest = PullRequest {
433              commit_hash: pr.commit_hash,
434              submitter: pr.submitter,
435              repo_id: pr.repo_id,
436              sponsor: pr.sponsor,
437              sponsored_at: pr.sponsored_at,
438              vote_deadline: pr.vote_deadline,
439              yes_votes: pr.yes_votes,
440              no_votes: pr.no_votes,
441              status: 4u8,  // proposal_created
442              behavior_flags: pr.behavior_flags,
443              governance_proposal_id: proposal_id,
444              is_shared_repo: pr.is_shared_repo
445          };
446  
447          Mapping::set(pr_registry, pr_hash, updated_pr);
448          Mapping::set(proposal_to_pr, proposal_id, pr_hash);
449      }
450  
451      // Receive cross-chain attestation from Delta
452      transition receive_delta_attestation(
453          public pr_hash: field,
454          public delta_passed: bool,
455          public delta_proposal_id: u128,
456          public finalized_at: u64
457      ) -> bool {
458          return delta_passed then finalize(pr_hash, delta_passed, delta_proposal_id, finalized_at);
459      }
460  
461      finalize receive_delta_attestation(
462          pr_hash: field,
463          delta_passed: bool,
464          delta_proposal_id: u128,
465          finalized_at: u64
466      ) {
467          let attestation: CrossChainAttestation = Mapping::get(cross_chain_attestations, pr_hash);
468  
469          let updated_attestation: CrossChainAttestation = CrossChainAttestation {
470              pr_hash: pr_hash,
471              alpha_passed: attestation.alpha_passed,
472              delta_passed: delta_passed,
473              alpha_proposal_id: attestation.alpha_proposal_id,
474              delta_proposal_id: delta_proposal_id,
475              finalized_at: finalized_at
476          };
477  
478          Mapping::set(cross_chain_attestations, pr_hash, updated_attestation);
479      }
480  
481      // Mark PR as executed after governance proposal passes
482      transition mark_executed(
483          public pr_hash: field,
484          public executed_at: u64
485      ) -> bool {
486          return true then finalize(pr_hash, executed_at);
487      }
488  
489      finalize mark_executed(
490          pr_hash: field,
491          executed_at: u64
492      ) {
493          let pr: PullRequest = Mapping::get(pr_registry, pr_hash);
494          assert_eq(pr.status, 4u8);  // Must have proposal created
495  
496          let updated_pr: PullRequest = PullRequest {
497              commit_hash: pr.commit_hash,
498              submitter: pr.submitter,
499              repo_id: pr.repo_id,
500              sponsor: pr.sponsor,
501              sponsored_at: pr.sponsored_at,
502              vote_deadline: pr.vote_deadline,
503              yes_votes: pr.yes_votes,
504              no_votes: pr.no_votes,
505              status: 5u8,  // executed
506              behavior_flags: pr.behavior_flags,
507              governance_proposal_id: pr.governance_proposal_id,
508              is_shared_repo: pr.is_shared_repo
509          };
510  
511          Mapping::set(pr_registry, pr_hash, updated_pr);
512      }
513  
514      // Create checkpoint for merged code
515      transition checkpoint_commit(
516          public commit_hash: field,
517          public radicle_oid: field,
518          public finalized_at: u64
519      ) -> Checkpoint {
520          let checkpoint: Checkpoint = Checkpoint {
521              commit_hash: commit_hash,
522              radicle_oid: radicle_oid,
523              approver_count: 1u8,
524              finalized_at: finalized_at
525          };
526  
527          return checkpoint then finalize(commit_hash, checkpoint);
528      }
529  
530      finalize checkpoint_commit(
531          commit_hash: field,
532          checkpoint: Checkpoint
533      ) {
534          Mapping::set(checkpoints, commit_hash, checkpoint);
535      }
536  }