Generate random number
Create pseudo-random numbers using block hashes for randomness in smart contracts
(define-read-only (generate-random (block-height uint))(let (;; Get block header hash(block-hash (unwrap! (get-stacks-block-info? id-header-hash block-height) (err u1001)));; Take a slice of the hash for randomness(hash-slice (unwrap-panic (slice? block-hash u16 u32)));; Convert to uint(random-value (buff-to-uint-be (unwrap-panic (as-max-len? hash-slice u16)))))(ok random-value)));; Generate random number in range(define-read-only (random-in-range (block-height uint) (min uint) (max uint))(let ((random (try! (generate-random block-height)))(range (- max min)))(ok (+ min (mod random (+ u1 range))))));; Example: Random between 1-100(random-in-range block-height u1 u100)
Use cases
- Lottery and gaming contracts
- Random NFT trait generation
- Fair distribution mechanisms
- Random selection from lists
Key concepts
Blockchain randomness is deterministic but unpredictable:
- Block hashes: Use historical block data as entropy source
- Future blocks: Cannot predict future block hashes
- Commitment schemes: Combine with commit-reveal for fairness
Advanced random generation
;; Random with multiple sources of entropy(define-read-only (enhanced-random (block-height uint) (user-seed uint))(let (;; Combine block hash with user seed(block-hash (unwrap! (get-stacks-block-info? id-header-hash block-height) (err u1001)))(combined (sha256 (concat block-hash (unwrap-panic (to-consensus-buff? user-seed)))));; Use multiple slices for better distribution(slice1 (buff-to-uint-be (unwrap-panic (slice? combined u0 u8))))(slice2 (buff-to-uint-be (unwrap-panic (slice? combined u8 u16)))))(ok (+ slice1 slice2))));; Shuffle a list using randomness(define-read-only (shuffle-list (items (list 10 uint)) (block-height uint))(let ((seed (default-to u0 (generate-random block-height))))(fold shuffle-item items { result: (list), seed: seed })))
Fair lottery implementation
(define-map lottery-entries principal uint)(define-data-var lottery-block uint u0)(define-data-var total-tickets uint u0);; Enter lottery(define-public (enter-lottery (tickets uint))(let ((current-tickets (default-to u0 (map-get? lottery-entries tx-sender))))(map-set lottery-entries tx-sender (+ current-tickets tickets))(var-set total-tickets (+ (var-get total-tickets) tickets))(ok true)));; Set lottery drawing block (must be future block)(define-public (set-lottery-block (block uint))(begin(asserts! (> block block-height) (err u1))(var-set lottery-block block)(ok true)));; Draw winner after lottery block is mined(define-read-only (draw-winner)(let ((draw-block (var-get lottery-block))(total (var-get total-tickets))(random (try! (random-in-range draw-block u0 total))));; In practice, iterate through entries to find winner(ok random)))
Security considerations
;; Commit-reveal pattern for user-influenced randomness(define-map commitments principal (buff 32))(define-map reveals principal uint);; Commit phase(define-public (commit-random (commitment (buff 32)))(begin(map-set commitments tx-sender commitment)(ok true)));; Reveal phase (after target block)(define-public (reveal-random (secret uint) (target-block uint))(let ((commitment (unwrap! (map-get? commitments tx-sender) (err u1)))(calculated (sha256 (unwrap-panic (to-consensus-buff? secret)))))(asserts! (is-eq commitment calculated) (err u2))(asserts! (>= block-height target-block) (err u3))(map-set reveals tx-sender secret);; Generate final random using block + all reveals(ok true)))
Randomness warning
Block hashes provide economic security but not cryptographic randomness. For high-value applications, use commit-reveal schemes or external randomness beacons with proper verification.