Source Code

(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-constant ERR-LEFTOVER-DATA (err u1010))
(define-constant ERR-NOT-SEGWIT-TRANSACTION (err u1011))
(define-read-only (read-uint8 (ctx { txbuff: (buff 8192), 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 8192), 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 8192), 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 8192), 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 8192), 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 8192), 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 8192), 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 8192), index: uint },
					remaining: uint,
					txins: (list 32 {
						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)})
										u32)
									ERR-TOO-MANY-TXINS)}))
				(ok state))
		error
			(err error)))
(define-read-only (read-txins (ctx { txbuff: (buff 8192), 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 u32)
			ERR-TOO-MANY-TXINS
			(fold read-next-txin (unwrap-panic (slice? (list true true true true true true true true true true true true true true true true true true true true true true true true 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 8192), index: uint },
					txouts: (list 32 {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)})
							u32)
						ERR-TOO-MANY-TXOUTS)}))
		error (err error)))
(define-read-only (read-txouts (ctx { txbuff: (buff 8192), 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 u32)
			ERR-TOO-MANY-TXOUTS
			(fold read-next-txout (unwrap-panic (slice? (list true true true true true true true true true true true true true true true true true true true true true true true true 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 8192), index: uint }, elements: (list 32 (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))
							u32)
						ERR-TOO-MANY-TXOUTS)}))
		error
			(err error)))
(define-read-only (read-next-witness
		(ignored bool)
		(state-res (response
			{ctx: {txbuff: (buff 8192), index: uint}, witnesses: (list 32 (list 32 (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 true true true true true true true true true true true true true true true true 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)) u32)),
							ctx: (get ctx parsed-elements)}))
					(begin
						(ok {
							witnesses: (unwrap-panic (as-max-len? (append (get witnesses state) (list)) u32)),
							ctx: (get ctx parsed-num-items)}))))
		error (err u1)))
(define-read-only (read-witnesses (ctx { txbuff: (buff 8192), index: uint }) (num-txins uint))
	(fold read-next-witness
		(unwrap-panic (slice? (list true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true) u0 num-txins))
		(ok { ctx: ctx, witnesses: (list) })))
(define-read-only (is-segwit-tx (tx (buff 8192)))
	(let (
			(ctx { txbuff: tx, index: u0})
			(parsed-version (try! (read-uint32 ctx)))
			(parsed-segwit-marker (try! (read-uint8 (get ctx parsed-version)))))
			(ok (is-eq (get uint8 parsed-segwit-marker) u0))
	)
)
(define-read-only (parse-wtx (tx (buff 8192)))
	(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)))))
		(asserts! (and (is-eq (get uint8 parsed-segwit-marker) u0) (is-eq (get uint8 parsed-segwit-version) u1)) ERR-NOT-SEGWIT-TRANSACTION)
		(asserts! (is-eq (len tx) (get index (get ctx parsed-locktime))) ERR-LEFTOVER-DATA)
		(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 8192)))
	(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)))))
		(asserts! (is-eq (len tx) (get index (get ctx parsed-locktime))) ERR-LEFTOVER-DATA)
		(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-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 (is-eq chain-id u1) (get-burn-block-info? header-hash bh) (map-get? mock-burnchain-header-hashes 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 8192)))
	(sha256 (sha256 tx)))
(define-read-only (get-txid (tx (buff 8192)))
	(reverse-buff32 (get-reversed-txid tx)))
(define-read-only (get-reversed-segwit-txid (tx (buff 8192)))
	(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 8192)))
	(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 8192)) (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 8192)) (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 (15)

FunctionAccessArgs
inner-reverseread-onlytarget-index: uint, hash-input: (buff 32
reverse-buff32read-onlyinput: (buff 32
is-segwit-txread-onlytx: (buff 8192
parse-wtxread-onlytx: (buff 8192
parse-txread-onlytx: (buff 8192
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 8192
get-txidread-onlytx: (buff 8192
get-reversed-segwit-txidread-onlytx: (buff 8192
get-segwit-txidread-onlytx: (buff 8192
is-bit-setread-onlyval: uint, bit: uint
verify-merkle-proofread-onlyreversed-txid: (buff 32