Source Code

(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u100))
(define-constant err-not-found (err u101))
(define-constant err-unauthorized (err u102))
(define-constant err-already-exists (err u103))
(define-constant err-invalid-amount (err u104))
(define-constant err-job-complete (err u105))
(define-constant err-insufficient-stake (err u106))

(define-data-var job-nonce uint u0)
(define-data-var node-nonce uint u0)

(define-map training-jobs
  uint
  {
    requester: principal,
    dataset-hash: (buff 32),
    model-config-hash: (buff 32),
    total-budget: uint,
    remaining-budget: uint,
    min-gpu-compute: uint,
    target-accuracy: uint,
    completed: bool,
    result-hash: (optional (buff 32))
  }
)

(define-map compute-nodes
  uint
  {
    provider: principal,
    gpu-type: (string-ascii 30),
    compute-power: uint,
    stake-amount: uint,
    total-jobs-completed: uint,
    active: bool,
    reputation: uint
  }
)

(define-map job-assignments
  {job-id: uint, node-id: uint}
  {
    assigned-block: uint,
    contribution-weight: uint,
    payment-amount: uint,
    work-verified: bool
  }
)

(define-map provider-nodes principal (list 50 uint))
(define-map node-earnings uint uint)

(define-public (register-compute-node (gpu-type (string-ascii 30)) (compute-power uint) (stake-amount uint))
  (let
    (
      (node-id (+ (var-get node-nonce) u1))
    )
    (asserts! (> stake-amount u0) err-invalid-amount)
    (try! (stx-transfer? stake-amount tx-sender (as-contract tx-sender)))
    (map-set compute-nodes node-id
      {
        provider: tx-sender,
        gpu-type: gpu-type,
        compute-power: compute-power,
        stake-amount: stake-amount,
        total-jobs-completed: u0,
        active: true,
        reputation: u100
      }
    )
    (map-set node-earnings node-id u0)
    (map-set provider-nodes tx-sender
      (unwrap-panic (as-max-len? (append (default-to (list) (map-get? provider-nodes tx-sender)) node-id) u50)))
    (var-set node-nonce node-id)
    (ok node-id)
  )
)

(define-public (submit-training-job (dataset-hash (buff 32)) (model-config-hash (buff 32)) (budget uint) (min-gpu-compute uint) (target-accuracy uint))
  (let
    (
      (job-id (+ (var-get job-nonce) u1))
    )
    (asserts! (> budget u0) err-invalid-amount)
    (try! (stx-transfer? budget tx-sender (as-contract tx-sender)))
    (map-set training-jobs job-id
      {
        requester: tx-sender,
        dataset-hash: dataset-hash,
        model-config-hash: model-config-hash,
        total-budget: budget,
        remaining-budget: budget,
        min-gpu-compute: min-gpu-compute,
        target-accuracy: target-accuracy,
        completed: false,
        result-hash: none
      }
    )
    (var-set job-nonce job-id)
    (ok job-id)
  )
)

(define-public (assign-node-to-job (job-id uint) (node-id uint) (contribution-weight uint))
  (let
    (
      (job (unwrap! (map-get? training-jobs job-id) err-not-found))
      (node (unwrap! (map-get? compute-nodes node-id) err-not-found))
    )
    (asserts! (is-eq tx-sender (get requester job)) err-unauthorized)
    (asserts! (not (get completed job)) err-job-complete)
    (asserts! (get active node) err-not-found)
    (asserts! (>= (get compute-power node) (get min-gpu-compute job)) err-insufficient-stake)
    (map-set job-assignments {job-id: job-id, node-id: node-id}
      {
        assigned-block: stacks-block-height,
        contribution-weight: contribution-weight,
        payment-amount: u0,
        work-verified: false
      }
    )
    (ok true)
  )
)

(define-public (verify-and-pay-node (job-id uint) (node-id uint) (payment-amount uint))
  (let
    (
      (job (unwrap! (map-get? training-jobs job-id) err-not-found))
      (node (unwrap! (map-get? compute-nodes node-id) err-not-found))
      (assignment (unwrap! (map-get? job-assignments {job-id: job-id, node-id: node-id}) err-not-found))
    )
    (asserts! (is-eq tx-sender (get requester job)) err-unauthorized)
    (asserts! (not (get work-verified assignment)) err-already-exists)
    (asserts! (<= payment-amount (get remaining-budget job)) err-invalid-amount)
    (try! (as-contract (stx-transfer? payment-amount tx-sender (get provider node))))
    (map-set job-assignments {job-id: job-id, node-id: node-id} (merge assignment {
      payment-amount: payment-amount,
      work-verified: true
    }))
    (map-set training-jobs job-id (merge job {
      remaining-budget: (- (get remaining-budget job) payment-amount)
    }))
    (map-set compute-nodes node-id (merge node {
      total-jobs-completed: (+ (get total-jobs-completed node) u1)
    }))
    (map-set node-earnings node-id
      (+ (default-to u0 (map-get? node-earnings node-id)) payment-amount))
    (ok true)
  )
)

(define-public (complete-training-job (job-id uint) (result-hash (buff 32)))
  (let
    (
      (job (unwrap! (map-get? training-jobs job-id) err-not-found))
    )
    (asserts! (is-eq tx-sender (get requester job)) err-unauthorized)
    (asserts! (not (get completed job)) err-already-exists)
    (map-set training-jobs job-id (merge job {
      completed: true,
      result-hash: (some result-hash)
    }))
    (ok true)
  )
)

(define-public (update-node-status (node-id uint) (active bool))
  (let
    (
      (node (unwrap! (map-get? compute-nodes node-id) err-not-found))
    )
    (asserts! (is-eq tx-sender (get provider node)) err-unauthorized)
    (map-set compute-nodes node-id (merge node {active: active}))
    (ok true)
  )
)

(define-read-only (get-training-job (job-id uint))
  (ok (map-get? training-jobs job-id))
)

(define-read-only (get-compute-node (node-id uint))
  (ok (map-get? compute-nodes node-id))
)

(define-read-only (get-job-assignment (job-id uint) (node-id uint))
  (ok (map-get? job-assignments {job-id: job-id, node-id: node-id}))
)

(define-read-only (get-provider-nodes (provider principal))
  (ok (map-get? provider-nodes provider))
)

(define-read-only (get-node-earnings (node-id uint))
  (ok (map-get? node-earnings node-id))
)

Functions (11)

FunctionAccessArgs
register-compute-nodepublicgpu-type: (string-ascii 30
submit-training-jobpublicdataset-hash: (buff 32
assign-node-to-jobpublicjob-id: uint, node-id: uint, contribution-weight: uint
verify-and-pay-nodepublicjob-id: uint, node-id: uint, payment-amount: uint
complete-training-jobpublicjob-id: uint, result-hash: (buff 32
update-node-statuspublicnode-id: uint, active: bool
get-training-jobread-onlyjob-id: uint
get-compute-noderead-onlynode-id: uint
get-job-assignmentread-onlyjob-id: uint, node-id: uint
get-provider-nodesread-onlyprovider: principal
get-node-earningsread-onlynode-id: uint