Source Code

;; federated-learning - Clarity 4
;; Federated learning coordination for privacy-preserving genomic ML

(define-constant ERR-NOT-AUTHORIZED (err u100))
(define-constant ERR-SESSION-NOT-FOUND (err u101))
(define-constant ERR-INVALID-CONTRIBUTION (err u102))
(define-constant ERR-SESSION-CLOSED (err u103))

(define-map learning-sessions uint
  {
    coordinator: principal,
    model-hash: (buff 64),
    min-participants: uint,
    current-participants: uint,
    target-accuracy: uint,
    current-accuracy: uint,
    started-at: uint,
    deadline: uint,
    is-active: bool,
    aggregation-round: uint
  }
)

(define-map participant-contributions { session-id: uint, participant: principal }
  {
    model-update-hash: (buff 64),
    contribution-round: uint,
    data-samples: uint,
    local-accuracy: uint,
    submitted-at: uint,
    is-verified: bool
  }
)

(define-map participant-rewards principal
  {
    total-contributions: uint,
    total-rewards: uint,
    reputation-score: uint,
    last-contribution: uint
  }
)

(define-map aggregation-results uint
  {
    session-id: uint,
    round: uint,
    aggregated-model-hash: (buff 64),
    participants-count: uint,
    accuracy-improvement: uint,
    aggregated-at: uint,
    aggregator: principal
  }
)

(define-data-var session-counter uint u0)
(define-data-var aggregation-counter uint u0)
(define-data-var min-data-samples uint u100)

(define-public (create-learning-session
    (model-hash (buff 64))
    (min-participants uint)
    (target-accuracy uint)
    (duration uint))
  (let ((session-id (+ (var-get session-counter) u1)))
    (map-set learning-sessions session-id
      {
        coordinator: tx-sender,
        model-hash: model-hash,
        min-participants: min-participants,
        current-participants: u0,
        target-accuracy: target-accuracy,
        current-accuracy: u0,
        started-at: stacks-block-time,
        deadline: (+ stacks-block-time duration),
        is-active: true,
        aggregation-round: u0
      })
    (var-set session-counter session-id)
    (ok session-id)))

(define-public (submit-contribution
    (session-id uint)
    (model-update-hash (buff 64))
    (data-samples uint)
    (local-accuracy uint))
  (let ((session (unwrap! (map-get? learning-sessions session-id) ERR-SESSION-NOT-FOUND)))
    (asserts! (get is-active session) ERR-SESSION-CLOSED)
    (asserts! (< stacks-block-time (get deadline session)) ERR-SESSION-CLOSED)
    (asserts! (>= data-samples (var-get min-data-samples)) ERR-INVALID-CONTRIBUTION)
    (map-set participant-contributions { session-id: session-id, participant: tx-sender }
      {
        model-update-hash: model-update-hash,
        contribution-round: (get aggregation-round session),
        data-samples: data-samples,
        local-accuracy: local-accuracy,
        submitted-at: stacks-block-time,
        is-verified: false
      })
    (map-set learning-sessions session-id
      (merge session { current-participants: (+ (get current-participants session) u1) }))
    (update-participant-stats tx-sender)
    (ok true)))

(define-public (aggregate-models
    (session-id uint)
    (aggregated-model-hash (buff 64))
    (accuracy-improvement uint))
  (let ((session (unwrap! (map-get? learning-sessions session-id) ERR-SESSION-NOT-FOUND))
        (aggregation-id (+ (var-get aggregation-counter) u1)))
    (asserts! (is-eq tx-sender (get coordinator session)) ERR-NOT-AUTHORIZED)
    (asserts! (get is-active session) ERR-SESSION-CLOSED)
    (map-set aggregation-results aggregation-id
      {
        session-id: session-id,
        round: (get aggregation-round session),
        aggregated-model-hash: aggregated-model-hash,
        participants-count: (get current-participants session),
        accuracy-improvement: accuracy-improvement,
        aggregated-at: stacks-block-time,
        aggregator: tx-sender
      })
    (map-set learning-sessions session-id
      (merge session {
        aggregation-round: (+ (get aggregation-round session) u1),
        current-accuracy: (+ (get current-accuracy session) accuracy-improvement),
        current-participants: u0
      }))
    (var-set aggregation-counter aggregation-id)
    (ok aggregation-id)))

(define-public (verify-contribution (session-id uint) (participant principal))
  (let ((contribution (unwrap! (map-get? participant-contributions { session-id: session-id, participant: participant }) ERR-INVALID-CONTRIBUTION)))
    (ok (map-set participant-contributions { session-id: session-id, participant: participant }
      (merge contribution { is-verified: true })))))

(define-public (close-session (session-id uint))
  (let ((session (unwrap! (map-get? learning-sessions session-id) ERR-SESSION-NOT-FOUND)))
    (asserts! (is-eq tx-sender (get coordinator session)) ERR-NOT-AUTHORIZED)
    (ok (map-set learning-sessions session-id
      (merge session { is-active: false })))))

(define-private (update-participant-stats (participant principal))
  (let ((stats (default-to
                 { total-contributions: u0, total-rewards: u0, reputation-score: u50, last-contribution: u0 }
                 (map-get? participant-rewards participant))))
    (map-set participant-rewards participant
      {
        total-contributions: (+ (get total-contributions stats) u1),
        total-rewards: (get total-rewards stats),
        reputation-score: (+ (get reputation-score stats) u1),
        last-contribution: stacks-block-time
      })
    true))

(define-read-only (get-session (session-id uint))
  (ok (map-get? learning-sessions session-id)))

(define-read-only (get-contribution (session-id uint) (participant principal))
  (ok (map-get? participant-contributions { session-id: session-id, participant: participant })))

(define-read-only (get-participant-stats (participant principal))
  (ok (map-get? participant-rewards participant)))

(define-read-only (get-aggregation-result (aggregation-id uint))
  (ok (map-get? aggregation-results aggregation-id)))

(define-read-only (validate-principal (p principal))
  (principal-destruct? p))

(define-read-only (format-session-id (session-id uint))
  (ok (int-to-ascii session-id)))

(define-read-only (parse-session-id (id-str (string-ascii 20)))
  (string-to-uint? id-str))

(define-read-only (get-bitcoin-block)
  (ok burn-block-height))

Functions (14)

FunctionAccessArgs
create-learning-sessionpublicmodel-hash: (buff 64
submit-contributionpublicsession-id: uint, model-update-hash: (buff 64
aggregate-modelspublicsession-id: uint, aggregated-model-hash: (buff 64
verify-contributionpublicsession-id: uint, participant: principal
close-sessionpublicsession-id: uint
update-participant-statsprivateparticipant: principal
get-sessionread-onlysession-id: uint
get-contributionread-onlysession-id: uint, participant: principal
get-participant-statsread-onlyparticipant: principal
get-aggregation-resultread-onlyaggregation-id: uint
validate-principalread-onlyp: principal
format-session-idread-onlysession-id: uint
parse-session-idread-onlyid-str: (string-ascii 20
get-bitcoin-blockread-only