clarity-bitcoin-dev-preview-1

SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9

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-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))))

Functions (13)

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
get-bc-h-hashread-onlyburnheight: 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