Source Code

(define-constant ERR-OUT-OF-BOUNDS u1)
(define-constant ERR-TOO-MANY-TXINS u2)
(define-constant ERR-TOO-MANY-TXOUTS u3)
(define-constant ERR-VARSLICE-TOO-LONG u4)
(define-constant ERR-BAD-HEADER u5)
(define-constant ERR-PROOF-TOO-SHORT u6)
(define-constant ERR-TOO-MANY-WITNESSES u7)
(define-constant ERR-INVALID-COMMITMENT u8)
(define-constant ERR-WITNESS-TX-NOT-IN-COMMITMENT u9)
(define-read-only (read-uint8 (ctx { txbuff: (buff 4096), index: uint}))
    (let ((data (get txbuff ctx))
          (base (get index ctx)))
        (ok {uint8: (buff-to-uint-le (unwrap-panic (as-max-len? (unwrap! (slice? data base (+ base u1)) (err ERR-OUT-OF-BOUNDS)) u1))),
             ctx: { txbuff: data, index: (+ u1 base)}})))
(define-read-only (read-uint16 (ctx { txbuff: (buff 4096), index: uint}))
    (let ((data (get txbuff ctx))
          (base (get index ctx)))
        (ok {uint16: (buff-to-uint-le (unwrap-panic (as-max-len? (unwrap! (slice? data base (+ base u2)) (err ERR-OUT-OF-BOUNDS)) u2))),
             ctx: { txbuff: data, index: (+ u2 base)}})))
(define-read-only (read-uint32 (ctx { txbuff: (buff 4096), index: uint}))
    (let ((data (get txbuff ctx))
          (base (get index ctx)))
        (ok {uint32: (buff-to-uint-le (unwrap-panic (as-max-len? (unwrap! (slice? data base (+ base u4)) (err ERR-OUT-OF-BOUNDS)) u4))),
             ctx: { txbuff: data, index: (+ u4 base)}})))
(define-read-only (read-uint64 (ctx { txbuff: (buff 4096), index: uint}))
    (let ((data (get txbuff ctx))
          (base (get index ctx)))
        (ok {uint64: (buff-to-uint-le (unwrap-panic (as-max-len? (unwrap! (slice? data base (+ base u8)) (err ERR-OUT-OF-BOUNDS)) u8))),
             ctx: { txbuff: data, index: (+ u8 base)}})))
(define-read-only (read-varint (ctx { txbuff: (buff 4096), index: uint}))
    (let ((ptr (get index ctx))
          (tx (get txbuff ctx))
          (byte (buff-to-uint-le (unwrap! (element-at tx ptr)
                                  (err ERR-OUT-OF-BOUNDS)))))
     (if (<= byte u252)
         ;; given byte is the varint
         (ok { varint: byte, ctx: { txbuff: tx, index: (+ u1 ptr)}})
         (if (is-eq byte u253)
             (let (
                 ;; next two bytes is the varint
                   (parsed-u16 (try! (read-uint16 { txbuff: tx, index: (+ u1 ptr)}))))
                 (ok { varint: (get uint16 parsed-u16), ctx: (get ctx parsed-u16)}))
             (if (is-eq byte u254)
                 (let (
                     ;; next four bytes is the varint
                       (parsed-u32 (try! (read-uint32 { txbuff: tx, index: (+ u1 ptr)}))))
                     (ok { varint: (get uint32 parsed-u32), ctx: (get ctx parsed-u32)}))
                 (let (
                     ;; next eight bytes is the varint
                       (parsed-u64 (try! (read-uint64 { txbuff: tx, index: (+ u1 ptr)}))))
                  (ok { varint: (get uint64 parsed-u64), ctx: (get ctx parsed-u64)})))))))
(define-read-only (read-varslice (old-ctx { txbuff: (buff 4096), index: uint}))
    (let ((parsed (try! (read-varint old-ctx)))
          (ctx (get ctx parsed))
          (slice-start (get index ctx))
          (target-index (+ slice-start (get varint parsed)))
          (txbuff (get txbuff ctx)))
     (ok {varslice: (unwrap! (slice? txbuff slice-start target-index) (err ERR-OUT-OF-BOUNDS)),
          ctx: { txbuff: txbuff, index: target-index}})))
(define-read-only (inner-reverse (target-index uint) (hash-input (buff 32)))
    (unwrap-panic 
        (replace-at? 
            (unwrap-panic 
                (replace-at? 
                    hash-input 
                    target-index 
                    (unwrap-panic (element-at? hash-input (- u31 target-index)))))
            (- u31 target-index) 
            (unwrap-panic (element-at? hash-input  target-index)))))
(define-read-only (reverse-buff32 (input (buff 32)))
    (fold inner-reverse
        (list u31 u30 u29 u28 u27 u26 u25 u24 u23 u22 u21 u20 u19 u18 u17 u16)
        input))
(define-read-only (read-hashslice (old-ctx { txbuff: (buff 4096), index: uint}))
    (let ((slice-start (get index old-ctx))
          (target-index (+ u32 slice-start))
          (txbuff (get txbuff old-ctx))
          (hash-le (unwrap-panic
                      (as-max-len? (unwrap!
                                      (slice? txbuff slice-start target-index) (err ERR-OUT-OF-BOUNDS))
                       u32))))
     (ok {hashslice: (reverse-buff32 hash-le),
          ctx: { txbuff: txbuff, index: target-index}})))
(define-read-only (read-next-txin (ignored bool)
                                  (state-res (response {ctx: { txbuff: (buff 4096), index: uint },
                                                        remaining: uint,
                                                        txins: (list 8 {outpoint: {
                                                                                   hash: (buff 32),
                                                                                   index: uint},
                                                                        scriptSig: (buff 256),      ;; just big enough to hold a 2-of-3 multisig script
                                                                        sequence: uint})}
                                              uint)))
    (match state-res
        state
            (if (< u0 (get remaining state))
                (let ((remaining (get remaining state))
                      (ctx (get ctx state))
                      (parsed-hash (try! (read-hashslice ctx)))
                      (parsed-index (try! (read-uint32 (get ctx parsed-hash))))
                      (parsed-scriptSig (try! (read-varslice (get ctx parsed-index))))
                      (parsed-sequence (try! (read-uint32 (get ctx parsed-scriptSig))))
                      (new-ctx (get ctx parsed-sequence)))
                 (ok {ctx: new-ctx,
                      remaining: (- remaining u1),
                      txins: (unwrap!
                              (as-max-len?
                                  (append (get txins state)
                                      {   outpoint: {
                                                     hash: (get hashslice parsed-hash),
                                                     index: (get uint32 parsed-index) },
                                          scriptSig: (unwrap! (as-max-len? (get varslice parsed-scriptSig) u256) (err ERR-VARSLICE-TOO-LONG)),
                                          sequence: (get uint32 parsed-sequence)})
                               u8)
                              (err ERR-TOO-MANY-TXINS))}))
                (ok state))
        error
            (err error)))
(define-read-only (read-txins (ctx { txbuff: (buff 4096), index: uint}))
    (let ((parsed-num-txins (try! (read-varint ctx)))
          (num-txins (get varint parsed-num-txins))
          (new-ctx (get ctx parsed-num-txins)))
     (if (> num-txins u8)
         (err ERR-TOO-MANY-TXINS)
         (fold read-next-txin (unwrap-panic (slice? (list true true true true true true true true) u0 num-txins)) (ok { ctx: new-ctx, remaining: num-txins, txins: (list)})))))
(define-read-only (read-next-txout (ignored bool)
                                   (state-res (response {ctx: { txbuff: (buff 4096), index: uint },
                                                         txouts: (list 8 {value: uint,
                                                                          scriptPubKey: (buff 128)})}
                                               uint)))
    (match state-res
        state
          (let ((parsed-value (try! (read-uint64 (get ctx state))))
                (parsed-script (try! (read-varslice (get ctx parsed-value))))
                (new-ctx (get ctx parsed-script)))
            (ok {ctx: new-ctx,
                txouts: (unwrap!
                          (as-max-len?
                              (append (get txouts state)
                                  {   value: (get uint64 parsed-value),
                                      scriptPubKey: (unwrap! (as-max-len? (get varslice parsed-script) u128) (err ERR-VARSLICE-TOO-LONG))})
                          u8)
                          (err ERR-TOO-MANY-TXOUTS))}))
        error (err error)))
(define-read-only (read-txouts (ctx { txbuff: (buff 4096), index: uint}))
    (let ((parsed-num-txouts (try! (read-varint ctx)))
          (num-txouts (get varint parsed-num-txouts))
          (new-ctx (get ctx parsed-num-txouts)))
     (if (> num-txouts u8)
         (err ERR-TOO-MANY-TXOUTS)
         (fold read-next-txout (unwrap-panic (slice? (list true true true true true true true true) u0 num-txouts)) (ok { ctx: new-ctx, txouts: (list)})))))
(define-read-only (read-next-element (ignored bool)
                                   (state-res (response {ctx: { txbuff: (buff 4096), index: uint },
                                                         elements: (list 8 (buff 256))}
                                               uint)))
    (match state-res
        state
          (let ((parsed-script (try! (read-varslice (get ctx state))))
                (new-ctx (get ctx parsed-script)))
            (ok {ctx: new-ctx,
                elements: (unwrap!
                          (as-max-len?
                              (append (get elements state) (unwrap! (as-max-len? (get varslice parsed-script) u256) (err ERR-VARSLICE-TOO-LONG)))
                          u8)
                          (err ERR-TOO-MANY-TXOUTS))}))
        error
            (err error)))
(define-read-only (read-next-witness (ignored bool)
  (state-res (response
    { ctx: {txbuff: (buff 4096), index: uint}, witnesses: (list 8 (list 8 (buff 256))) } uint)))
  (match state-res
    state (let (
      (parsed-num-items (try! (read-varint (get ctx state))))
      (ctx (get ctx parsed-num-items))
      (varint (get varint parsed-num-items)))
        (if (> varint u0)
          (let ((parsed-elements (try! (fold read-next-element (unwrap-panic (slice? (list true true true true true true true true) u0 varint)) (ok { ctx: ctx, elements: (list)})))))
            (ok {
              witnesses: (unwrap-panic (as-max-len? (append (get witnesses state) (get elements parsed-elements)) u8)),
              ctx: (get ctx parsed-elements)
            })
          )
          (begin
            (ok {
              witnesses: (unwrap-panic (as-max-len? (append (get witnesses state) (list)) u8)),
              ctx: (get ctx parsed-num-items)
            })
          )
        )
      )
    error (err u1)
  )
)
(define-read-only (read-witnesses (ctx { txbuff: (buff 4096), index: uint }) (num-txins uint))
  (fold read-next-witness (unwrap-panic (slice? (list true true true true true true true true) u0 num-txins)) (ok { ctx: ctx, witnesses: (list) }))
)
(define-read-only (parse-wtx (tx (buff 4096)))
    (let ((ctx { txbuff: tx, index: u0})
          (parsed-version (try! (read-uint32 ctx)))
          (parsed-segwit-marker (try! (read-uint8 (get ctx parsed-version))))
          (parsed-segwit-version (try! (read-uint8 (get ctx parsed-segwit-marker))))
          (parsed-txins (try! (read-txins (get ctx parsed-segwit-version))))
          (parsed-txouts (try! (read-txouts (get ctx parsed-txins))))
          (parsed-witnesses (try! (read-witnesses (get ctx parsed-txouts) (len (get txins parsed-txins)))))
          (parsed-locktime (try! (read-uint32 (get ctx parsed-witnesses))))
          )
     (ok {version: (get uint32 parsed-version),
          segwit-marker: (get uint8 parsed-segwit-marker),
          segwit-version: (get uint8 parsed-segwit-version),
          ins: (get txins parsed-txins),
          outs: (get txouts parsed-txouts),
          witnesses: (get witnesses parsed-witnesses),
          locktime: (get uint32 parsed-locktime)
        }
      )
  )
)
(define-read-only (parse-tx (tx (buff 4096)))
    (let ((ctx { txbuff: tx, index: u0})
          (parsed-version (try! (read-uint32 ctx)))
          (parsed-txins (try! (read-txins (get ctx parsed-version))))
          (parsed-txouts (try! (read-txouts (get ctx parsed-txins))))
          (parsed-locktime (try! (read-uint32 (get ctx parsed-txouts)))))
     (ok {version: (get uint32 parsed-version),
          ins: (get txins parsed-txins),
          outs: (get txouts parsed-txouts),
          locktime: (get uint32 parsed-locktime)})))
(define-read-only (parse-block-header (headerbuff (buff 80)))
    (let ((ctx { txbuff: headerbuff, index: u0})
          (parsed-version (try! (read-uint32 ctx)))
          (parsed-parent-hash (try! (read-hashslice (get ctx parsed-version))))
          (parsed-merkle-root (try! (read-hashslice (get ctx parsed-parent-hash))))
          (parsed-timestamp (try! (read-uint32 (get ctx parsed-merkle-root))))
          (parsed-nbits (try! (read-uint32 (get ctx parsed-timestamp))))
          (parsed-nonce (try! (read-uint32 (get ctx parsed-nbits)))))
     (ok {version: (get uint32 parsed-version),
          parent: (get hashslice parsed-parent-hash),
          merkle-root: (get hashslice parsed-merkle-root),
          timestamp: (get uint32 parsed-timestamp),
          nbits: (get uint32 parsed-nbits),
          nonce: (get uint32 parsed-nonce)})))
(define-constant DEBUG-MODE true)
(define-map mock-burnchain-header-hashes uint (buff 32))
(define-public (mock-add-burnchain-block-header-hash (burn-height uint) (hash (buff 32)))
 (ok (map-set mock-burnchain-header-hashes burn-height hash))
)
(define-read-only (get-bc-h-hash (bh uint))
   (if DEBUG-MODE (map-get? mock-burnchain-header-hashes bh) (get-burn-block-info? header-hash bh)))
(define-read-only (verify-block-header (headerbuff (buff 80)) (expected-block-height uint))
    (match (get-bc-h-hash expected-block-height)
        bhh 
        (is-eq bhh (reverse-buff32 (sha256 (sha256 headerbuff))))
        false
    )
)
(define-read-only (get-reversed-txid (tx (buff 4096)))
    (sha256 (sha256 tx)))
(define-read-only (get-txid (tx (buff 4096)))
    (reverse-buff32 (get-reversed-txid tx))
)
(define-read-only (get-reversed-segwit-txid (tx (buff 4096)))
    (let 
      (
        (ctx { txbuff: tx, index: u0})
        (parsed-version (unwrap-panic (read-uint32 ctx)))
        (parsed-segwit-marker (unwrap-panic (read-uint8 (get ctx parsed-version))))
        (parsed-segwit-version (unwrap-panic (read-uint8 (get ctx parsed-segwit-marker))))
        (parsed-txins (unwrap-panic (read-txins (get ctx parsed-segwit-version))))
        (parsed-txouts (unwrap-panic (read-txouts (get ctx parsed-txins))))        
        (parsed-witnesses (unwrap-panic (read-witnesses (get ctx parsed-txouts) (len (get txins parsed-txins)))))
        (parsed-locktime (unwrap-panic (read-uint32 (get ctx parsed-witnesses))))
        (dropped-tx 
          (concat
            (unwrap-panic (slice? tx u0 (get index (get ctx parsed-version))))
          (concat 
            (unwrap-panic (slice? tx (get index (get ctx parsed-segwit-version)) (get index (get ctx parsed-txouts))))           
            (unwrap-panic (slice? tx (get index (get ctx parsed-witnesses)) (len tx)))
          ))
        )
      )      
      (sha256 (sha256 dropped-tx))
    )
)
(define-read-only (get-segwit-txid (tx (buff 4096)))
  (reverse-buff32 (get-reversed-segwit-txid  tx))
)
(define-read-only (is-bit-set (val uint) (bit uint))
  (> (bit-and val (bit-shift-left u1 bit)) u0))
(define-read-only (inner-merkle-proof-verify (ctr uint) (state { path: uint, root-hash: (buff 32), proof-hashes: (list 14 (buff 32)), tree-depth: uint, cur-hash: (buff 32), verified: bool}))
    (if (get verified state)
        state
        (if (>= ctr (get tree-depth state))
            (merge state { verified: false})
            (let ((path (get path state))
                  (is-left (is-bit-set path ctr))
                  (proof-hashes (get proof-hashes state))
                  (cur-hash (get cur-hash state))
                  (root-hash (get root-hash state))
                  (h1 (if is-left (unwrap-panic (element-at proof-hashes ctr)) cur-hash))
                  (h2 (if is-left cur-hash (unwrap-panic (element-at proof-hashes ctr))))
                  (next-hash (sha256 (sha256 (concat h1 h2))))
                  (is-verified (and (is-eq (+ u1 ctr) (len proof-hashes)) (is-eq next-hash root-hash))))
             (merge state { cur-hash: next-hash, verified: is-verified})))))
(define-read-only (verify-merkle-proof (reversed-txid (buff 32)) (merkle-root (buff 32)) (proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint}))
    (if (> (get tree-depth proof) (len (get hashes proof)))
        (err ERR-PROOF-TOO-SHORT)
        (ok
          (get verified
              (fold inner-merkle-proof-verify
                  (list u0 u1 u2 u3 u4 u5 u6 u7 u8 u9 u10 u11 u12 u13)
                  { path: (+ (pow u2 (get tree-depth proof)) (get tx-index proof)), root-hash: merkle-root, proof-hashes: (get hashes proof), cur-hash: reversed-txid, tree-depth: (get tree-depth proof), verified: false})))))
(define-read-only (was-tx-mined? (block { header: (buff 80), height: uint }) (tx (buff 1024)) (proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint }))
  (let
    (
      (header-valid (verify-block-header (get header block) (get height block)))
      (reversed-txid (get-reversed-txid tx))
      (parsed-header (try! (parse-block-header (get header block))))
      (merkle-root (reverse-buff32 (get merkle-root parsed-header)))
      (merkle-valid (verify-merkle-proof reversed-txid merkle-root proof))
    )
    (if header-valid
      merkle-valid
      (ok false)
    )
  )
)
(define-read-only (was-segwit-tx-mined? (block { header: (buff 80), height: uint }) (tx (buff 4096)) (proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint }))
  (let
    (
      (header-valid (verify-block-header (get header block) (get height block)))
      (reversed-txid (get-reversed-segwit-txid tx))
      (parsed-header (try! (parse-block-header (get header block))))
      (merkle-root (reverse-buff32 (get merkle-root parsed-header)))
      (merkle-valid (verify-merkle-proof reversed-txid merkle-root proof))
    )
    (if header-valid
      merkle-valid
      (ok false)
    )
  )
)

Functions (14)

FunctionAccessArgs
inner-reverseread-onlytarget-index: uint, hash-input: (buff 32
reverse-buff32read-onlyinput: (buff 32
parse-wtxread-onlytx: (buff 4096
parse-txread-onlytx: (buff 4096
parse-block-headerread-onlyheaderbuff: (buff 80
mock-add-burnchain-block-header-hashpublicburn-height: uint, hash: (buff 32
get-bc-h-hashread-onlybh: uint
verify-block-headerread-onlyheaderbuff: (buff 80
get-reversed-txidread-onlytx: (buff 4096
get-txidread-onlytx: (buff 4096
get-reversed-segwit-txidread-onlytx: (buff 4096
get-segwit-txidread-onlytx: (buff 4096
is-bit-setread-onlyval: uint, bit: uint
verify-merkle-proofread-onlyreversed-txid: (buff 32