shrub/lib/bitcoin-utils.hoon

713 lines
16 KiB
Plaintext
Raw Normal View History

2021-02-19 17:11:01 +03:00
:: lib/bitcoin-utils.hoon
2021-02-01 11:49:54 +03:00
:: Utilities for working with BTC data types and transactions
::
2021-02-18 15:13:06 +03:00
/- sur=bitcoin
2021-02-19 17:11:01 +03:00
:: ^?
:: =< [sur .]
2020-10-30 14:45:38 +03:00
=, sur
2020-10-04 11:56:38 +03:00
|%
2021-02-19 15:27:12 +03:00
::
:: TODO: move this to zuse
:: bit/byte utilities
::
::
:: +blop: munge bit and byt sequences (cat, flip, take, drop)
::
++ blop
|_ =bloq
+$ biyts [wid=@ud dat=@]
++ cat
|= bs=(list biyts)
^- biyts
:- (roll (turn bs |=(b=biyts -.b)) add)
(can bloq (flop bs))
:: +flip: flip endianness while preserving lead/trail zeroes
::
++ flip
|= b=biyts
^- biyts
[wid.b (rev bloq b)]
:: +take: take n bloqs from front
:: pads front with extra zeroes if n is longer than input
::
++ take
|= [n=@ b=biyts]
^- biyts
?: (gth n wid.b)
[n dat.b]
[n (rsh [bloq (sub wid.b n)] dat.b)]
:: +drop: drop n bloqs from front
:: returns 0^0 if n >= width
::
++ drop
|= [n=@ b=biyts]
^- biyts
?: (gte n wid.b)
0^0x0
=+ n-take=(sub wid.b n)
[n-take (end [bloq n-take] dat.b)]
--
++ byt
|%
++ bl ~(. blop 3)
++ cat cat:bl:byt
++ flip flip:bl:byt
++ take take:bl:byt
++ drop drop:bl:byt
--
::
++ bit
|%
++ bl ~(. blop 0)
++ cat cat:bl:bit
++ flip flip:bl:bit
++ take take:bl:bit
++ drop drop:bl:bit
++ from-atoms
|= [bitwidth=@ digits=(list @)]
^- bits
%- cat:bit
%+ turn digits
|= a=@
?> (lte (met 0 a) bitwidth)
[bitwidth `@ub`a]
:: +to-atoms: convert bits to atoms of bitwidth
::
++ to-atoms
|= [bitwidth=@ bs=bits]
^- (list @)
=| res=(list @)
?> =(0 (mod wid.bs bitwidth))
|-
?: =(0 wid.bs) res
%= $
res (snoc res dat:(take:bit bitwidth bs))
bs (drop:bit bitwidth bs)
==
--
2021-01-26 21:36:21 +03:00
++ to-hexb
|= h=@t
2021-01-26 21:36:21 +03:00
^- hexb
?: =('' h) 1^0x0
:: Add leading 00
::
=+ (lsh [3 2] h)
:: Group by 4-size block
::
=+ (rsh [3 2] -)
:: Parse hex to atom
::
2021-01-26 21:36:21 +03:00
:- (div (lent (trip h)) 2)
`@ux`(rash - hex)
::
++ overhead-weight ^-(vbytes 11)
++ input-weight
|= =bipt
^- vbytes
?- bipt
%44 148
%49 91
%84 68
==
++ output-weight
|= =bipt
^- vbytes
?- bipt
%44 34
%49 32
%84 31
==
::
2020-10-29 15:10:54 +03:00
++ xpub-type
2020-10-31 12:41:00 +03:00
|= =xpub
2021-01-27 16:40:21 +03:00
^- [=bipt =network]
2020-10-31 12:41:00 +03:00
=/ prefix=tape (scag 4 (trip xpub))
2021-01-27 16:40:21 +03:00
?: =("tpub" prefix) [%44 %testnet]
?: =("upub" prefix) [%49 %testnet]
?: =("vpub" prefix) [%84 %testnet]
?: =("xpub" prefix) [%44 %main]
?: =("ypub" prefix) [%49 %main]
?: =("zpub" prefix) [%84 %main]
2020-10-29 15:10:54 +03:00
~|("invalid xpub: {<xpub>}" !!)
2021-01-29 14:09:54 +03:00
::
++ address-bipt
|= a=address
^- bipt
=/ spk=hexb (script-pubkey a)
?: =(25 wid.spk) %44
?: =(23 wid.spk) %49
?: =(22 wid.spk) %84
?: =(34 wid.spk) %84
~|("Invalid address" !!)
2020-10-14 10:08:35 +03:00
:: big endian sha256: input and output are both MSB first (big endian)
2020-10-15 13:55:41 +03:00
::
2020-10-14 10:08:35 +03:00
++ sha256
|= =byts
2021-01-26 21:36:21 +03:00
^- hexb
%- flip:byt
[32 (shay (flip:byt byts))]
::
++ dsha256
|= =byts
(sha256 (sha256 byts))
::
2020-10-04 17:16:35 +03:00
++ hash-160
2021-01-25 16:01:36 +03:00
|= val=byts
2021-01-26 21:36:21 +03:00
^- hexb
=, ripemd:crypto
:- 20
2020-10-15 13:55:41 +03:00
%- ripemd-160
2021-01-25 16:01:36 +03:00
(sha256 val)
2021-02-09 08:02:46 +03:00
:: +csiz: handle compact-size integers
:: - encode: big endian to little endian
:: - decode: little endian to big endian
2021-02-08 22:21:19 +03:00
::
2021-02-09 08:02:46 +03:00
++ csiz
|%
++ en
|= a=@
^- hexb
=/ l=@ (met 3 a)
?: =(l 1) 1^a
?: =(l 2) (cat:byt ~[1^0xfd (flip:byt 2^a)])
?: (lte l 4) (cat:byt ~[1^0xfe (flip:byt 4^a)])
?: (lte l 8) (cat:byt ~[1^0xff (flip:byt 8^a)])
~|("Cannot encode CompactSize longer than 8 bytes" !!)
2021-02-09 12:29:51 +03:00
::
2021-02-09 08:02:46 +03:00
++ de
|= h=hexb
^- [n=hexb rest=hexb]
=/ s=@ux dat:(take:byt 1 h)
?: (lth s 0xfd) [1^s (drop:byt 1 h)]
~| "Invalid compact-size at start of {<h>}"
=/ len=bloq
2021-02-08 22:21:19 +03:00
?+ s !!
2021-02-09 08:02:46 +03:00
%0xfd 1
%0xfe 2
%0xff 3
2021-02-08 22:21:19 +03:00
==
2021-02-09 12:29:51 +03:00
:_ (drop:byt (add 1 len) h)
%- flip:byt
(take:byt (bex len) (drop:byt 1 h))
2021-02-09 13:25:38 +03:00
:: +dea: atom instead of hexb for parsed CompactSize
2021-02-09 12:29:51 +03:00
::
++ dea
|= h=hexb
^- [a=@ rest=hexb]
=> (de h)
[dat.n rest]
2021-02-09 08:02:46 +03:00
--
2021-01-25 16:01:36 +03:00
::
2021-01-27 16:09:25 +03:00
++ pubkey-to-address
|= [=bipt =network pubkey=hexb]
^- address
?- bipt
%44
:- %base58
=< ^-(@uc dat)
%- cat:byt
:- ?- network
%main 1^0x0
%testnet 1^0x6f
2021-01-25 16:01:36 +03:00
==
2021-01-27 16:09:25 +03:00
~[(hash-160 pubkey)]
::
%49
:- %base58
=< ^-(@uc dat)
%- cat:byt
:~ ?- network
%main 1^0x5
%testnet 1^0xc4
==
%- hash-160
(cat:byt ~[2^0x14 (hash-160 pubkey)])
==
::
%84
:- %bech32
(need (encode-pubkey:bech32 network pubkey))
==
2020-10-15 13:55:41 +03:00
::
++ script-pubkey
|= =address
2021-01-26 21:36:21 +03:00
^- hexb
?- -.address
%bech32
2021-01-27 16:09:25 +03:00
=+ h=(from-address:bech32 +.address)
2021-01-26 21:36:21 +03:00
%- cat:byt
:~ 1^0x0
1^wid.h
h
2020-10-19 11:36:57 +03:00
==
2021-01-27 16:09:25 +03:00
::
2021-01-26 21:36:21 +03:00
%base58
2021-01-27 16:09:25 +03:00
=/ h=hexb [21 `@ux`+.address]
=+ lead-byt=dat:(take:byt 1 h)
=/ version-network=[bipt network]
?: =(0x0 lead-byt) [%44 %main]
?: =(0x6f lead-byt) [%44 %testnet]
?: =(0x5 lead-byt) [%49 %main]
2021-01-27 16:25:31 +03:00
?: =(0xc4 lead-byt) [%49 %testnet]
2021-01-27 16:09:25 +03:00
~|("Invalid base58 address: {<+.address>}" !!)
%- cat:byt
?: ?=(%44 -.version-network)
:~ 3^0x76.a914
(drop:byt 1 h)
2^0x88ac
==
:~ 2^0xa914
(drop:byt 1 h)
1^0x87
==
==
2021-02-09 12:29:51 +03:00
:: +txu: transaction utility core
:: - primarily used for calculating txids
:: - ignores signatures in inputs
2021-01-27 16:09:25 +03:00
::
2020-12-13 23:33:08 +03:00
++ txu
|%
++ en
|%
++ input
|= i=input:tx
2021-01-26 21:36:21 +03:00
^- hexb
%- cat:byt
:~ (flip:byt txid.i)
(flip:byt 4^pos.i)
2021-02-09 12:29:51 +03:00
?~ script-sig.i 1^0x0
%- cat:byt
~[(en:csiz wid.u.script-sig.i) u.script-sig.i]
(flip:byt sequence.i)
==
::
++ output
|= o=output:tx
2021-01-26 21:36:21 +03:00
^- hexb
%- cat:byt
:~ (flip:byt 8^value.o)
1^wid.script-pubkey.o
script-pubkey.o
==
2020-12-14 15:13:24 +03:00
--
::
++ de
|%
++ nversion
2021-02-08 22:21:19 +03:00
|= b=hexb
^- [nversion=@ud rest=hexb]
:- dat:(flip:byt (take:byt 4 b))
(drop:byt 4 b)
2020-12-14 15:13:24 +03:00
::
++ segwit
2021-02-08 22:21:19 +03:00
|= b=hexb
^- [segwit=(unit @ud) rest=hexb]
?. =(1^0x0 (take:byt 1 b))
2020-12-14 17:40:31 +03:00
[~ b]
2021-02-08 22:21:19 +03:00
:- [~ dat:(take:byt 2 b)]
(drop:byt 2 b)
2020-12-14 15:13:24 +03:00
::
2021-02-09 12:29:51 +03:00
++ script-sig
2021-02-08 22:21:19 +03:00
|= b=hexb
2021-02-09 12:29:51 +03:00
^- [sig=hexb rest=hexb]
=^ siglen=hexb b (de:csiz b)
:- (take:byt dat.siglen b)
(drop:byt dat.siglen b)
2020-12-14 15:13:24 +03:00
::
2021-02-09 12:29:51 +03:00
++ sequence
2021-02-08 22:21:19 +03:00
|= b=hexb
2021-02-09 12:29:51 +03:00
^- [seq=hexb rest=hexb]
[(flip:byt (take:byt 4 b)) (drop:byt 4 b)]
2020-12-14 15:13:24 +03:00
::
++ inputs
2021-02-08 22:21:19 +03:00
|= b=hexb
^- [is=(list input:tx) rest=hexb]
2021-02-09 12:29:51 +03:00
|^
2020-12-14 15:13:24 +03:00
=| acc=(list input:tx)
2021-02-09 12:29:51 +03:00
=^ count b (dea:csiz b)
2020-12-14 15:13:24 +03:00
|-
?: =(0 count) [acc b]
2021-02-09 12:29:51 +03:00
=^ i b (input b)
$(acc (snoc acc i), count (dec count))
::
++ input
|= b=hexb
^- [i=input:tx rest=hexb]
=/ txid (flip:byt (take:byt 32 b))
=/ pos dat:(flip:byt (take:byt 4 (drop:byt 32 b)))
=^ sig=hexb b (script-sig (drop:byt 36 b))
=^ seq=hexb b (sequence b)
:_ b
[txid pos seq ?:((gth wid.sig 0) `sig ~) ~ 0]
--
2020-12-14 15:13:24 +03:00
::
++ outputs
2021-02-08 22:21:19 +03:00
|= b=hexb
^- [os=(list output:tx) rest=hexb]
2020-12-14 15:13:24 +03:00
=| acc=(list output:tx)
2021-02-09 12:29:51 +03:00
=^ count b (dea:csiz b)
2020-12-14 15:13:24 +03:00
|-
?: =(0 count) [acc b]
2021-02-09 12:29:51 +03:00
=/ value (flip:byt (take:byt 8 b))
=^ scriptlen b (dea:csiz (drop:byt 8 b))
2020-12-14 15:13:24 +03:00
%= $
acc %+ snoc acc
2021-02-09 12:29:51 +03:00
:- (take:byt scriptlen b)
dat.value
b (drop:byt scriptlen b)
2020-12-14 15:13:24 +03:00
count (dec count)
==
--
2021-02-09 12:29:51 +03:00
:: +basic-encode: encodes data in a format suitable for hashing
2020-12-14 15:13:24 +03:00
::
2021-02-09 12:29:51 +03:00
++ basic-encode
|= =data:tx
2021-01-26 21:36:21 +03:00
^- hexb
%- cat:byt
%- zing
2021-02-09 12:29:51 +03:00
:~ ~[(flip:byt 4^nversion.data)]
~[(en:csiz (lent is.data))]
(turn is.data input:en)
2021-02-09 12:29:51 +03:00
~[(en:csiz (lent os.data))]
(turn os.data output:en)
~[(flip:byt 4^locktime.data)]
==
2020-12-13 23:33:08 +03:00
++ get-id
2020-12-14 19:18:23 +03:00
|= =data:tx
2021-01-26 21:36:21 +03:00
^- hexb
%- flip:byt
2020-12-14 19:18:23 +03:00
%- dsha256
2021-02-09 12:29:51 +03:00
(basic-encode data)
2020-12-14 15:13:24 +03:00
::
2020-12-14 17:40:31 +03:00
++ decode
2021-01-26 21:36:21 +03:00
|= b=hexb
^- data:tx
2021-02-08 22:21:19 +03:00
=^ nversion b
(nversion:de b)
=^ segwit b
(segwit:de b)
=^ inputs b
(inputs:de b)
=^ outputs b
(outputs:de b)
2020-12-15 10:36:46 +03:00
=/ locktime=@ud
2021-02-08 22:21:19 +03:00
dat:(take:byt 4 (flip:byt b))
2020-12-15 10:36:46 +03:00
[inputs outputs locktime nversion segwit]
2020-12-13 23:33:08 +03:00
--
:: core to handle BIP174 PSBTs
2020-12-07 14:56:50 +03:00
::
++ pbt
2020-12-07 14:56:50 +03:00
|%
2020-12-09 20:33:50 +03:00
++ en
|%
++ globals
2021-01-26 21:36:21 +03:00
|= rawtx=hexb
^- map:psbt
2020-12-10 16:22:48 +03:00
:~ [[1 0x0] rawtx]
==
::
++ input
2020-12-15 12:35:33 +03:00
|= [only-witness=? i=in:psbt]
^- map:psbt
2020-12-15 12:35:33 +03:00
%+ weld
?: only-witness ~
~[[1^0x0 rawtx.i]]
:~ (witness-tx i)
2020-12-10 16:22:48 +03:00
(hdkey %input hdkey.i)
==
::
++ output
|= =out:psbt
^- map:psbt
2020-12-10 16:22:48 +03:00
?~ hk.out ~
:~ (hdkey %output u.hk.out)
==
::
++ witness-tx
|= i=in:psbt
^- keyval:psbt
:- [1 0x1]
%- cat:byt
2020-12-11 13:10:40 +03:00
:~ (flip:byt 8^value.utxo.i)
2020-12-10 16:22:48 +03:00
1^0x16
2^0x14
2021-01-25 16:01:36 +03:00
(hash-160 pubkey.hdkey.i)
2020-12-10 16:22:48 +03:00
==
::
++ hdkey
2020-12-10 16:22:48 +03:00
|= [=target:psbt h=^hdkey]
^- keyval:psbt
=/ typ=@ux
?- target
%input 0x6
%output 0x2
==
=/ coin-type=hexb
?- network.h
%main
1^0x0
%testnet
1^0x1
==
2020-12-10 16:22:48 +03:00
:- (cat:byt ~[1^typ pubkey.h])
%- cat:byt
2020-12-10 12:14:03 +03:00
:~ fprint.h
1^`@ux`bipt.h 3^0x80
coin-type 3^0x80
4^0x80
1^`@ux`chyg.h 3^0x0
2020-12-11 13:10:40 +03:00
(flip:byt 4^idx.h)
==
2020-12-10 18:54:39 +03:00
::
++ keyval-byts
|= kv=keyval:psbt
2021-01-26 21:36:21 +03:00
^- hexb
2020-12-10 18:54:39 +03:00
%- cat:byt
:~ 1^wid.key.kv
key.kv
1^wid.val.kv
val.kv
==
::
++ map-byts
|= m=map:psbt
2021-01-26 21:36:21 +03:00
^- (unit hexb)
2020-12-10 18:54:39 +03:00
?~ m ~
:- ~
%- cat:byt
(turn m keyval-byts)
2020-12-10 16:22:48 +03:00
--
2020-12-11 13:10:40 +03:00
++ base64
2021-01-26 21:36:21 +03:00
|= b=hexb
2020-12-11 13:10:40 +03:00
^- base64:psbt
%- en:base64:mimes:html
(flip:byt b)
2020-12-09 20:33:50 +03:00
:: +encode: make base64 cord of PSBT
2020-12-15 12:35:33 +03:00
:: - only-witness: don't include non-witness UTXO
2020-12-09 20:33:50 +03:00
::
++ encode
2020-12-15 12:35:33 +03:00
|= $: only-witness=?
2021-01-26 21:36:21 +03:00
rawtx=hexb
txid=hexb
2020-12-15 12:35:33 +03:00
inputs=(list in:psbt)
outputs=(list out:psbt)
==
2020-12-10 19:15:25 +03:00
^- base64:psbt
2021-01-26 21:36:21 +03:00
=/ sep=(unit hexb) `1^0x0
=/ final=(list (unit hexb))
2020-12-11 14:12:53 +03:00
%+ join sep
2020-12-10 18:54:39 +03:00
%+ turn
%- zing
:~ ~[(globals:en rawtx)]
2020-12-15 12:35:33 +03:00
(turn inputs (cury input:en only-witness))
2020-12-10 18:54:39 +03:00
(turn outputs output:en)
==
map-byts:en
2020-12-11 13:10:40 +03:00
%- base64:en
2021-02-19 15:27:12 +03:00
^- byts
2020-12-10 19:15:25 +03:00
%- cat:byt
%+ weld ~[[5 0x70.7362.74ff]]
2020-12-11 14:12:53 +03:00
(murn (snoc final sep) same)
2020-12-08 20:31:21 +03:00
::
2020-12-08 15:47:51 +03:00
++ parse
|= psbt-base64=cord
^- (list map:psbt)
2021-02-08 22:21:19 +03:00
=/ todo=hexb
2021-02-09 12:29:51 +03:00
(drop:byt 5 (to-byts psbt-base64))
=| acc=(list map:psbt)
=| m=map:psbt
2020-12-08 17:48:40 +03:00
|-
2021-02-08 22:21:19 +03:00
?: =(wid.todo 0)
(snoc acc m)
2020-12-08 17:48:40 +03:00
:: 0x0: map separator
2021-02-08 22:21:19 +03:00
::
?: =(1^0x0 (take:byt 1 todo))
$(acc (snoc acc m), m *map:psbt, todo (drop:byt 1 todo))
=^ kv todo (next-keyval todo)
$(m (snoc m kv))
2020-12-08 17:48:40 +03:00
:: +get-txid: extract txid from a valid PSBT
::
2020-12-08 15:32:23 +03:00
++ get-txid
2020-12-08 15:47:51 +03:00
|= psbt-base64=cord
2021-01-26 21:36:21 +03:00
^- hexb
=/ tx=hexb
2020-12-07 14:56:50 +03:00
%- raw-tx
2021-02-08 22:21:19 +03:00
%+ drop:byt 5
(to-byts psbt-base64)
%- flip:byt
2021-01-29 14:09:54 +03:00
(dsha256 tx)
2020-12-07 14:56:50 +03:00
:: +raw-tx: extract hex transaction
:: looks for key 0x0 in global map
2021-02-08 22:21:19 +03:00
:: crashes if tx not in hex
2020-12-07 14:56:50 +03:00
::
++ raw-tx
2021-02-08 22:21:19 +03:00
|= b=hexb
^- hexb
|-
?: =(wid.b 0) !!
?: =(1^0x0 (take:byt 1 b)) !!
=/ nk (next-keyval b)
2020-12-08 17:48:40 +03:00
?: =(0x0 dat.key.kv.nk)
val.kv.nk
$(b rest.nk)
:: +next-keyval: returns next key-val in a PSBT map
2021-02-08 22:21:19 +03:00
:: input first byte must be a map key length
2020-12-07 14:56:50 +03:00
::
2020-12-08 17:48:40 +03:00
++ next-keyval
2021-02-08 22:21:19 +03:00
|= b=hexb
^- [kv=keyval:psbt rest=hexb]
=/ klen dat:(take:byt 1 b)
=/ k (take:byt klen (drop:byt 1 b))
=/ vlen dat:(take:byt 1 (drop:byt (add 1 klen) b))
=/ v (take:byt vlen (drop:byt (add 2 klen) b))
?> ?&((gth wid.k 0) (gth wid.v 0))
:- [k v]
(drop:byt ;:(add 2 klen vlen) b)
2020-12-08 15:42:39 +03:00
::
2021-02-08 22:21:19 +03:00
++ to-byts
2020-12-08 15:47:51 +03:00
|= psbt-base64=cord
2021-02-08 22:21:19 +03:00
^- hexb
2020-12-08 15:36:51 +03:00
~| "Invalid PSBT"
2020-12-09 12:10:11 +03:00
=+ p=(de:base64:mimes:html psbt-base64)
2020-12-08 15:36:51 +03:00
?~ p !!
2021-02-08 22:21:19 +03:00
(flip:byt u.p)
2020-12-07 14:56:50 +03:00
--
2020-10-16 10:33:10 +03:00
::
2020-10-05 10:49:09 +03:00
++ bech32
|%
++ prefixes
^- (map network tape)
(my [[%main "bc"] [%testnet "tb"] ~])
++ charset "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
+$ raw-decoded [hrp=tape data=(list @) checksum=(list @)]
2020-10-30 14:45:38 +03:00
:: below is a port of: https://github.com/bitcoinjs/bech32/blob/master/index.js
2020-10-05 10:49:09 +03:00
::
++ polymod
|= values=(list @)
|^ ^- @
=/ gen=(list @ux)
~[0x3b6a.57b2 0x2650.8e6d 0x1ea1.19fa 0x3d42.33dd 0x2a14.62b3]
=/ chk=@ 1
|- ?~ values chk
2020-12-09 12:10:11 +03:00
=/ top (rsh [0 25] chk)
2020-10-05 10:49:09 +03:00
=. chk
2020-12-09 12:10:11 +03:00
(mix i.values (lsh [0 5] (dis chk 0x1ff.ffff)))
2020-10-05 10:49:09 +03:00
$(values t.values, chk (update-chk chk top gen))
::
++ update-chk
|= [chk=@ top=@ gen=(list @ux)]
=/ is (gulf 0 4)
|- ?~ is chk
2020-12-09 12:10:11 +03:00
?: =(1 (dis 1 (rsh [0 i.is] top)))
2020-10-05 10:49:09 +03:00
$(is t.is, chk (mix chk (snag i.is gen)))
$(is t.is)
--
::
++ expand-hrp
|= hrp=tape
^- (list @)
2020-12-09 12:10:11 +03:00
=/ front (turn hrp |=(p=@tD (rsh [0 5] p)))
2020-10-05 10:49:09 +03:00
=/ back (turn hrp |=(p=@tD (dis 31 p)))
(zing ~[front ~[0] back])
::
++ verify-checksum
|= [hrp=tape data-and-checksum=(list @)]
^- ?
2021-01-27 16:09:25 +03:00
%- |=(a=@ =(1 a))
2020-10-05 10:49:09 +03:00
%- polymod
(weld (expand-hrp hrp) data-and-checksum)
::
++ checksum
|= [hrp=tape data=(list @)]
^- (list @)
:: xor 1 with the polymod
2021-01-27 16:09:25 +03:00
::
2020-10-05 10:49:09 +03:00
=/ pmod=@
%+ mix 1
%- polymod
(zing ~[(expand-hrp hrp) data (reap 6 0)])
%+ turn (gulf 0 5)
2020-12-09 12:10:11 +03:00
|=(i=@ (dis 31 (rsh [0 (mul 5 (sub 5 i))] pmod)))
2020-10-05 10:49:09 +03:00
::
++ charset-to-value
|= c=@tD
^- (unit @)
(find ~[c] charset)
++ value-to-charset
|= value=@
^- (unit @tD)
?: (gth value 31) ~
`(snag value charset)
::
++ is-valid
|= [bech=tape last-1-pos=@] ^- ?
?& ?|(=((cass bech) bech) =((cuss bech) bech)) :: to upper or to lower is same as bech
(gte last-1-pos 1)
(lte (add last-1-pos 7) (lent bech))
(lte (lent bech) 90)
(levy bech |=(c=@tD (gte c 33)))
(levy bech |=(c=@tD (lte c 126)))
==
:: data should be 5bit words
::
++ encode-raw
|= [hrp=tape data=(list @)]
2021-01-27 16:09:25 +03:00
^- cord
2020-10-05 10:49:09 +03:00
=/ combined=(list @)
2020-10-16 14:02:55 +03:00
(weld data (checksum hrp data))
%- crip
2020-10-05 10:49:09 +03:00
(zing ~[hrp "1" (tape (murn combined value-to-charset))])
++ decode-raw
2021-01-27 16:09:25 +03:00
|= body=cord
2020-10-05 10:49:09 +03:00
^- (unit raw-decoded)
2021-01-27 16:09:25 +03:00
=/ bech (cass (trip body)) :: to lowercase
2020-10-05 10:49:09 +03:00
=/ pos (flop (fand "1" bech))
?~ pos ~
=/ last-1=@ i.pos
?. (is-valid bech last-1) :: check bech32 validity (not segwit validity or checksum)
~
=/ hrp (scag last-1 bech)
=/ encoded-data-and-checksum=(list @)
(slag +(last-1) bech)
=/ data-and-checksum=(list @)
%+ murn encoded-data-and-checksum
charset-to-value
?. =((lent encoded-data-and-checksum) (lent data-and-checksum)) :: ensure all were in CHARSET
~
?. (verify-checksum hrp data-and-checksum)
~
=/ checksum-pos (sub (lent data-and-checksum) 6)
`[hrp (scag checksum-pos data-and-checksum) (slag checksum-pos data-and-checksum)]
2021-01-27 16:09:25 +03:00
:: +from-address: BIP173 bech32 address encoding to hex
:: https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
:: expects to drop a leading 5-bit 0 (the witness version)
2020-10-15 13:55:41 +03:00
::
2021-01-27 16:09:25 +03:00
++ from-address
|= body=cord
2021-01-26 21:36:21 +03:00
^- hexb
2021-01-27 16:09:25 +03:00
~| "Invalid bech32 address"
=/ d=(unit raw-decoded) (decode-raw body)
?> ?=(^ d)
=/ bs=bits (from-atoms:bit 5 data.u.d)
=/ byt-len=@ (div (sub wid.bs 5) 8)
?> =(5^0b0 (take:bit 5 bs))
?> ?| =(20 byt-len)
=(32 byt-len)
==
[byt-len `@ux`dat:(take:bit (mul 8 byt-len) (drop:bit 5 bs))]
2020-10-05 10:55:10 +03:00
:: pubkey is the 33 byte ECC compressed public key
2020-10-15 13:55:41 +03:00
::
2020-10-05 10:49:09 +03:00
++ encode-pubkey
2021-01-25 16:01:36 +03:00
|= [=network pubkey=byts]
2021-01-27 16:09:25 +03:00
^- (unit cord)
2021-01-25 16:01:36 +03:00
?. =(33 wid.pubkey)
2020-10-05 10:55:10 +03:00
~|('pubkey must be a 33 byte ECC compressed public key' !!)
2020-10-05 10:49:09 +03:00
=/ prefix (~(get by prefixes) network)
?~ prefix ~
:- ~
%+ encode-raw u.prefix
2021-01-27 16:09:25 +03:00
[0v0 (to-atoms:bit 5 [160 `@ub`dat:(hash-160 pubkey)])]
2020-10-05 10:49:09 +03:00
--
2020-10-04 11:56:38 +03:00
::
--