Source Code

;; fraud-prevention.clar
;; Advanced fraud prevention and view verification system for ad campaigns
;; Version: 1.0.0

;; ============================================
;; Constants and Error Codes
;; ============================================

(define-constant contract-owner tx-sender)
(define-constant ERR-NOT-AUTHORIZED (err u401))
(define-constant ERR-INVALID-PARAMS (err u400))
(define-constant ERR-NOT-FOUND (err u404))
(define-constant ERR-DUPLICATE-VIEW (err u405))
(define-constant ERR-RATE-LIMIT-EXCEEDED (err u406))
(define-constant ERR-INVALID-PROOF (err u407))
(define-constant ERR-SUSPICIOUS-ACTIVITY (err u408))
(define-constant ERR-VERIFICATION-FAILED (err u409))

;; ============================================
;; Data Variables
;; ============================================

(define-data-var verification-threshold uint u80)  ;; 80% confidence threshold
(define-data-var max-views-per-block uint u10)    ;; Rate limiting
(define-data-var suspicious-threshold uint u100)   ;; Suspicious activity threshold
(define-data-var verify-counter uint u0)          ;; Total verifications processed

;; ============================================
;; Data Maps
;; ============================================

;; View Records
(define-map ViewRecords
    { view-id: (buff 32) }
    {
        campaign-id: uint,
        publisher: principal,
        viewer: principal,
        timestamp: uint,
        verification-score: uint,
        proof-data: (buff 32),
        verified: bool
    }
)

;; Publisher View Tracking
(define-map PublisherViews
    { publisher: principal, block: uint }
    { view-count: uint }
)

;; Viewer Rate Limiting
(define-map ViewerRateLimit
    { viewer: principal, block: uint }
    { view-count: uint }
)

;; Suspicious Activity Tracking
(define-map SuspiciousActivity
    { publisher: principal }
    {
        suspicious-views: uint,
        last-flagged: uint,
        total-flags: uint,
        status: (string-ascii 20)
    }
)

;; View Verification Cache
(define-map VerificationCache
    { campaign-id: uint, day: uint }
    {
        total-views: uint,
        verified-views: uint,
        rejected-views: uint,
        average-score: uint
    }
)

;; ============================================
;; Private Functions
;; ============================================

(define-private (is-contract-owner)
    (is-eq tx-sender contract-owner)
)

(define-private (generate-view-id (campaign-id uint) (publisher principal) (viewer principal))
    (sha256 (concat (unwrap-panic (to-consensus-buff? campaign-id))
                   (concat (unwrap-panic (to-consensus-buff? publisher))
                          (unwrap-panic (to-consensus-buff? viewer)))))
)

(define-private (check-rate-limit (viewer principal))
    (let
        ((current-count (default-to { view-count: u0 }
            (map-get? ViewerRateLimit { viewer: viewer, block: stacks-block-time }))))
        (< (get view-count current-count) (var-get max-views-per-block))
    )
)

(define-private (update-rate-limit (viewer principal))
    (let
        ((current-count (default-to { view-count: u0 }
            (map-get? ViewerRateLimit { viewer: viewer, block: stacks-block-time }))))
        (map-set ViewerRateLimit
            { viewer: viewer, block: stacks-block-time }
            { view-count: (+ u1 (get view-count current-count)) }
        )
    )
)

(define-private (calculate-verification-score 
    (campaign-id uint) 
    (publisher principal) 
    (proof-data (buff 32)))
    
    (let
        ((base-score u80)  ;; Start with 80% base confidence
         (proof-factor (if (is-some (some proof-data)) u10 u0))
         (publisher-factor u10))
        
        ;; Add various factors to determine final score
        (+ (+ base-score proof-factor) publisher-factor)
    )
)

(define-private (update-verification-cache 
    (campaign-id uint) 
    (verification-score uint) 
    (verified bool))
    
    (let
        ((current-day (/ stacks-block-time u86400))
         (cache (default-to {
            total-views: u0,
            verified-views: u0,
            rejected-views: u0,
            average-score: u0
         } (map-get? VerificationCache { campaign-id: campaign-id, day: current-day }))))
        
        (map-set VerificationCache
            { campaign-id: campaign-id, day: current-day }
            {
                total-views: (+ (get total-views cache) u1),
                verified-views: (+ (get verified-views cache) (if verified u1 u0)),
                rejected-views: (+ (get rejected-views cache) (if verified u0 u1)),
                average-score: (/ (+ (* (get average-score cache) 
                                      (get total-views cache)) 
                                   verification-score)
                                (+ (get total-views cache) u1))
            }
        )
    )
)

;; ============================================
;; Public Functions
;; ============================================

;; Verify View
(define-public (verify-view
    (campaign-id uint)
    (publisher principal)
    (viewer principal)
    (proof-data (optional (buff 32))))
    
    (let
        (
            (view-id (generate-view-id campaign-id publisher viewer))
            (verification-score (calculate-verification-score 
                campaign-id 
                publisher 
                (default-to 0x00 proof-data)))
        )
        
        ;; Validations
        (asserts! (check-rate-limit viewer) ERR-RATE-LIMIT-EXCEEDED)
        (asserts! (is-none (map-get? ViewRecords { view-id: view-id })) 
                 ERR-DUPLICATE-VIEW)
        
        ;; Update rate limit
        (update-rate-limit viewer)
        
        ;; Check verification score against threshold
        (if (>= verification-score (var-get verification-threshold))
            (begin
                ;; Record verified view
                (map-set ViewRecords
                    { view-id: view-id }
                    {
                        campaign-id: campaign-id,
                        publisher: publisher,
                        viewer: viewer,
                        timestamp: stacks-block-time,
                        verification-score: verification-score,
                        proof-data: (default-to 0x00 proof-data),
                        verified: true
                    }
                )
                
                ;; Update verification cache
                (update-verification-cache campaign-id verification-score true)
                
                ;; Increment verify counter
                (var-set verify-counter (+ (var-get verify-counter) u1))
                
                (ok true)
            )
            (begin
                ;; Record rejected view
                (map-set ViewRecords
                    { view-id: view-id }
                    {
                        campaign-id: campaign-id,
                        publisher: publisher,
                        viewer: viewer,
                        timestamp: stacks-block-time,
                        verification-score: verification-score,
                        proof-data: (default-to 0x00 proof-data),
                        verified: false
                    }
                )
                
                ;; Update verification cache
                (update-verification-cache campaign-id verification-score false)
                
                ERR-VERIFICATION-FAILED
            )
        )
    )
)

;; Flag Suspicious Activity
(define-public (flag-suspicious-activity
    (publisher principal)
    (reason (string-ascii 50)))
    
    (let
        ((current-flags (default-to {
            suspicious-views: u0,
            last-flagged: u0,
            total-flags: u0,
            status: "active"
        } (map-get? SuspiciousActivity { publisher: publisher }))))
        
        (map-set SuspiciousActivity
            { publisher: publisher }
            {
                suspicious-views: (+ (get suspicious-views current-flags) u1),
                last-flagged: stacks-block-time,
                total-flags: (+ (get total-flags current-flags) u1),
                status: (if (> (+ (get suspicious-views current-flags) u1) 
                             (var-get suspicious-threshold))
                          "suspended"
                          "active")
            }
        )
        (ok true)
    )
)

;; ============================================
;; Admin Functions
;; ============================================

;; Update Verification Threshold
(define-public (set-verification-threshold (new-threshold uint))
    (begin
        (asserts! (is-contract-owner) ERR-NOT-AUTHORIZED)
        (asserts! (and (>= new-threshold u50) (<= new-threshold u100)) 
                 ERR-INVALID-PARAMS)
        (var-set verification-threshold new-threshold)
        (ok true)
    )
)

;; Update Rate Limit
(define-public (set-rate-limit (new-limit uint))
    (begin
        (asserts! (is-contract-owner) ERR-NOT-AUTHORIZED)
        (var-set max-views-per-block new-limit)
        (ok true)
    )
)

;; ============================================
;; Read-Only Functions
;; ============================================

;; Get View Details
(define-read-only (get-view-details (view-id (buff 32)))
    (match (map-get? ViewRecords { view-id: view-id })
        record (ok record)
        ERR-NOT-FOUND
    )
)

;; Get Publisher Verification Stats
(define-read-only (get-publisher-verification-stats 
    (campaign-id uint) 
    (publisher principal))
    
    (let
        ((current-day (/ stacks-block-time u86400)))
        (match (map-get? VerificationCache 
            { campaign-id: campaign-id, day: current-day })
            stats (ok stats)
            ERR-NOT-FOUND
        )
    )
)

;; Get Suspicious Activity Status
(define-read-only (get-suspicious-activity-status (publisher principal))
    (match (map-get? SuspiciousActivity { publisher: publisher })
        status (ok status)
        ERR-NOT-FOUND
    )
)

Functions (13)

FunctionAccessArgs
is-contract-ownerprivate
generate-view-idprivatecampaign-id: uint, publisher: principal, viewer: principal
check-rate-limitprivateviewer: principal
update-rate-limitprivateviewer: principal
calculate-verification-scoreprivatecampaign-id: uint, publisher: principal, proof-data: (buff 32
update-verification-cacheprivatecampaign-id: uint, verification-score: uint, verified: bool
verify-viewpubliccampaign-id: uint, publisher: principal, viewer: principal, proof-data: (optional (buff 32
flag-suspicious-activitypublicpublisher: principal, reason: (string-ascii 50
set-verification-thresholdpublicnew-threshold: uint
set-rate-limitpublicnew-limit: uint
get-view-detailsread-onlyview-id: (buff 32
get-publisher-verification-statsread-onlycampaign-id: uint, publisher: principal
get-suspicious-activity-statusread-onlypublisher: principal