(define-constant ERR-OUT-OF-BOUNDS (err u1001))
(define-constant ERR-TOO-MANY-TXINS (err u1002))
(define-constant ERR-TOO-MANY-TXOUTS (err u1003))
(define-constant ERR-VARSLICE-TOO-LONG (err u1004))
(define-constant ERR-BAD-HEADER (err u1005))
(define-constant ERR-PROOF-TOO-SHORT (err u1006))
(define-constant ERR-TOO-MANY-WITNESSES (err u1007))
(define-constant ERR-INVALID-COMMITMENT (err u1008))
(define-constant ERR-WITNESS-TX-NOT-IN-COMMITMENT (err u1009))
(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-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-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-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-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-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-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-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-VARSLICE-TOO-LONG),
sequence: (get uint32 parsed-sequence)})
u8)
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-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-VARSLICE-TOO-LONG)})
u8)
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-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-VARSLICE-TOO-LONG))
u8)
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-read-only (get-bc-h-hash (burnheight uint))
(get-burn-block-info? header-hash burnheight)
)
(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-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))))