2020-10-30 14:45:38 +03:00
|
|
|
/- sur=btc
|
|
|
|
^?
|
|
|
|
=< [sur .]
|
|
|
|
=, sur
|
2020-10-04 11:56:38 +03:00
|
|
|
|%
|
2020-10-29 15:10:54 +03:00
|
|
|
++ xpub-type
|
2020-10-31 12:41:00 +03:00
|
|
|
|= =xpub
|
2020-10-29 15:10:54 +03:00
|
|
|
^- bipt
|
2020-10-31 12:41:00 +03:00
|
|
|
=/ prefix=tape (scag 4 (trip xpub))
|
2020-10-29 15:10:54 +03:00
|
|
|
?: =("xpub" prefix) %bip44
|
|
|
|
?: =("ypub" prefix) %bip49
|
|
|
|
?: =("zpub" prefix) %bip84
|
|
|
|
~|("invalid xpub: {<xpub>}" !!)
|
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
|
2020-10-20 11:28:19 +03:00
|
|
|
|= =byts ^- hash256
|
2020-10-15 13:55:41 +03:00
|
|
|
:: if there are leading 0s, lshift by their amount after flip to little endian to preserve
|
|
|
|
=/ pad=@ (sub wid.byts (met 3 dat.byts))
|
|
|
|
=/ little-endian=@
|
|
|
|
(lsh 3 pad (swp 3 dat.byts))
|
2020-10-20 11:28:19 +03:00
|
|
|
%- hash256
|
2020-10-15 13:55:41 +03:00
|
|
|
:- 32
|
2020-10-14 10:07:56 +03:00
|
|
|
%+ swp 3
|
2020-10-15 13:55:41 +03:00
|
|
|
(shay wid.byts little-endian)
|
2020-10-16 12:28:51 +03:00
|
|
|
::
|
|
|
|
++ dsha256
|
|
|
|
|= =byts
|
|
|
|
(sha256 (sha256 byts))
|
|
|
|
::
|
2020-10-04 17:16:35 +03:00
|
|
|
++ hash-160
|
2020-10-20 11:28:19 +03:00
|
|
|
|= pubkey=@ux ^- hash160
|
2020-10-04 19:03:22 +03:00
|
|
|
=, ripemd:crypto
|
2020-10-20 11:28:19 +03:00
|
|
|
%- hash160
|
2020-10-16 12:28:51 +03:00
|
|
|
:- 20
|
2020-10-15 13:55:41 +03:00
|
|
|
%- ripemd-160
|
2020-10-14 10:08:35 +03:00
|
|
|
%- sha256 [(met 3 pubkey) pubkey]
|
2020-10-15 13:55:41 +03:00
|
|
|
::
|
2020-10-16 13:21:51 +03:00
|
|
|
++ to-script-pubkey
|
2020-10-16 14:02:55 +03:00
|
|
|
|= script=^buffer ^- ^buffer
|
2020-10-16 13:21:51 +03:00
|
|
|
%- zing
|
|
|
|
:~ ~[0x19 0x76 0xa9 0x14]
|
2020-10-16 13:31:21 +03:00
|
|
|
script
|
2020-10-16 13:21:51 +03:00
|
|
|
~[0x88 0xac]
|
|
|
|
==
|
2020-10-16 12:28:51 +03:00
|
|
|
++ address-to-script-pubkey
|
2020-10-16 14:02:55 +03:00
|
|
|
|= =address ^- ^buffer
|
2020-10-19 11:36:57 +03:00
|
|
|
=/ hex=byts
|
|
|
|
?- -.address
|
|
|
|
%bech32
|
|
|
|
(to-hex:bech32 address)
|
|
|
|
%legacy
|
|
|
|
=/ h=@ux (@ux +.address)
|
|
|
|
[(met 3 h) h]
|
|
|
|
==
|
2020-10-16 12:28:51 +03:00
|
|
|
?. =(wid.hex 20)
|
2020-10-19 11:36:57 +03:00
|
|
|
~|("Only 20-byte addresses supported" !!)
|
2020-10-16 13:31:21 +03:00
|
|
|
(to-script-pubkey (from-byts:buffer hex))
|
2020-10-16 10:33:10 +03:00
|
|
|
:: list of @ux that is big endian for hashing purposes
|
|
|
|
:: used to preserve 0s when concatenating byte sequences
|
|
|
|
::
|
|
|
|
++ buffer
|
2020-10-15 13:55:41 +03:00
|
|
|
|%
|
2020-10-16 10:33:10 +03:00
|
|
|
++ from-byts
|
2020-10-16 14:02:55 +03:00
|
|
|
|= =byts ^- ^buffer
|
2020-10-16 10:33:10 +03:00
|
|
|
=/ b=(list @ux)
|
|
|
|
(flop (rip 3 dat.byts))
|
|
|
|
=/ pad=@ (sub wid.byts (lent b))
|
|
|
|
(weld (reap pad 0x0) b)
|
2020-10-16 12:28:51 +03:00
|
|
|
:: converts an atom to a little endian buffer with wid length (trailing 0s)
|
|
|
|
:: atom 1 with wid=4 becomes ~[0x1 0x0 0x0 0x0]
|
|
|
|
:: 0xff11 with wid=8 becomes ~[0x11 0xff 0x0 0x0 0x0 0x0 0x0 0x0]
|
|
|
|
::
|
|
|
|
++ from-atom-le
|
2020-10-16 14:02:55 +03:00
|
|
|
|= [wid=@ a=@] ^- ^buffer
|
2020-10-16 12:28:51 +03:00
|
|
|
=/ b=(list @ux) (rip 3 a)
|
|
|
|
=/ pad=@ (sub wid (lent b))
|
|
|
|
(weld b (reap pad 0x0))
|
2020-10-16 10:33:10 +03:00
|
|
|
::
|
|
|
|
++ to-byts
|
2020-10-16 14:02:55 +03:00
|
|
|
|= b=^buffer ^- byts
|
2020-10-16 10:33:10 +03:00
|
|
|
[(lent b) (rep 3 (flop b))]
|
2020-11-20 14:58:27 +03:00
|
|
|
::
|
2020-10-16 12:28:51 +03:00
|
|
|
++ concat-as-byts
|
2020-10-16 14:02:55 +03:00
|
|
|
|= bs=(list ^buffer) ^- byts
|
2020-10-16 12:28:51 +03:00
|
|
|
%- to-byts (zing bs)
|
2020-10-15 13:55:41 +03:00
|
|
|
--
|
2020-10-05 10:49:09 +03:00
|
|
|
:: Converts a list of bits to a list of n-bit numbers
|
|
|
|
:: input-bits should be big-endian
|
2020-10-04 11:56:38 +03:00
|
|
|
::
|
2020-10-05 10:49:09 +03:00
|
|
|
++ bits
|
|
|
|
|%
|
2020-10-16 12:28:51 +03:00
|
|
|
:: rip atom a with num-bits. Preserve leading 0s, big endian
|
2020-10-16 10:33:10 +03:00
|
|
|
:: returns a list of bits
|
2020-10-05 10:49:09 +03:00
|
|
|
::
|
|
|
|
++ zeros-brip
|
2020-10-16 12:28:51 +03:00
|
|
|
|= [num-bits=@ a=@]
|
2020-10-05 10:49:09 +03:00
|
|
|
^- (list @)
|
|
|
|
=/ bits=(list @) (flop (rip 0 a))
|
2020-10-16 12:28:51 +03:00
|
|
|
=/ pad=@ (sub num-bits (lent bits))
|
|
|
|
(weld (reap pad 0) bits)
|
2020-10-15 13:55:41 +03:00
|
|
|
:: converts from bit list to a list of atoms each with bitwidth d(est)
|
2020-10-05 10:49:09 +03:00
|
|
|
::
|
|
|
|
++ convert
|
2020-10-15 13:55:41 +03:00
|
|
|
|= [d=@ bits=(list @)]
|
2020-10-05 10:49:09 +03:00
|
|
|
^- (list @)
|
|
|
|
=| ret=(list @)
|
|
|
|
|- ?~ bits ret
|
|
|
|
=/ dest-bits (scag d ((list @) bits))
|
|
|
|
:: left-shift the "missing" number of bits
|
|
|
|
=/ num=@
|
|
|
|
%: lsh 0
|
|
|
|
(sub d (lent dest-bits))
|
|
|
|
(rep 0 (flop dest-bits))
|
|
|
|
==
|
|
|
|
$(ret (snoc ret num), bits (slag d ((list @) bits)))
|
2020-10-15 13:55:41 +03:00
|
|
|
:: Converts e.g. ~[0 0 31 31 31 31 0 0] in base32 (5 bitwidth)
|
|
|
|
:: to ~[0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0]
|
|
|
|
::
|
|
|
|
++ from-digits
|
|
|
|
|= [bitwidth=@ digits=(list @)]
|
|
|
|
^- (list @)
|
|
|
|
%- zing
|
|
|
|
%+ turn digits
|
|
|
|
|= d=@ (zeros-brip bitwidth d)
|
|
|
|
:: converts 40 bits: ~[0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0]
|
|
|
|
:: to 0x3fff.fc00 when cast to hex
|
|
|
|
::
|
|
|
|
++ to-atom
|
|
|
|
|= bits=(list @)
|
|
|
|
^- @
|
|
|
|
%+ rep 0
|
|
|
|
%- flop bits
|
2020-10-05 10:49:09 +03:00
|
|
|
--
|
2020-10-04 11:56:38 +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
|
|
|
|
=/ top (rsh 0 25 chk)
|
|
|
|
=. chk
|
|
|
|
(mix i.values (lsh 0 5 (dis chk 0x1ff.ffff)))
|
|
|
|
$(values t.values, chk (update-chk chk top gen))
|
|
|
|
::
|
|
|
|
++ update-chk
|
|
|
|
|= [chk=@ top=@ gen=(list @ux)]
|
|
|
|
=/ is (gulf 0 4)
|
|
|
|
|- ?~ is chk
|
|
|
|
?: =(1 (dis 1 (rsh 0 i.is top)))
|
|
|
|
$(is t.is, chk (mix chk (snag i.is gen)))
|
|
|
|
$(is t.is)
|
|
|
|
--
|
|
|
|
::
|
|
|
|
++ expand-hrp
|
|
|
|
|= hrp=tape
|
|
|
|
^- (list @)
|
|
|
|
=/ front (turn hrp |=(p=@tD (rsh 0 5 p)))
|
|
|
|
=/ back (turn hrp |=(p=@tD (dis 31 p)))
|
|
|
|
(zing ~[front ~[0] back])
|
|
|
|
::
|
|
|
|
++ verify-checksum
|
|
|
|
|= [hrp=tape data-and-checksum=(list @)]
|
|
|
|
^- ?
|
2020-10-05 10:55:10 +03:00
|
|
|
%+ |=([a=@ b=@] =(a b))
|
|
|
|
1
|
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
|
|
|
|
=/ pmod=@
|
|
|
|
%+ mix 1
|
|
|
|
%- polymod
|
|
|
|
(zing ~[(expand-hrp hrp) data (reap 6 0)])
|
|
|
|
%+ turn (gulf 0 5)
|
|
|
|
|=(i=@ (dis 31 (rsh 0 (mul 5 (sub 5 i)) pmod)))
|
|
|
|
::
|
|
|
|
++ 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 @)]
|
2020-10-16 14:02:55 +03:00
|
|
|
^- bech32-address
|
2020-10-05 10:49:09 +03:00
|
|
|
=/ combined=(list @)
|
2020-10-16 14:02:55 +03:00
|
|
|
(weld data (checksum hrp data))
|
|
|
|
:- %bech32
|
|
|
|
%- crip
|
2020-10-05 10:49:09 +03:00
|
|
|
(zing ~[hrp "1" (tape (murn combined value-to-charset))])
|
|
|
|
++ decode-raw
|
2020-10-16 14:02:55 +03:00
|
|
|
|= b=bech32-address
|
2020-10-05 10:49:09 +03:00
|
|
|
^- (unit raw-decoded)
|
2020-10-16 14:02:55 +03:00
|
|
|
=/ bech (cass (trip +.b)) :: 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)]
|
2020-10-15 16:08:29 +03:00
|
|
|
:: goes from a bech32 address to hex. Returns byts to preserve leading 0s
|
2020-10-15 13:55:41 +03:00
|
|
|
::
|
|
|
|
++ to-hex
|
2020-10-16 14:02:55 +03:00
|
|
|
|= b=bech32-address ^- hash
|
|
|
|
=/ d=(unit raw-decoded) (decode-raw b)
|
2020-10-15 13:55:41 +03:00
|
|
|
?~ d ~|("Invalid bech32 address" !!)
|
2020-10-15 16:08:29 +03:00
|
|
|
=/ bs=(list @)
|
|
|
|
(from-digits:bits 5 (slag 1 data.u.d))
|
2020-10-20 11:28:19 +03:00
|
|
|
=/ byt-len=@ (div (lent bs) 8)
|
2020-10-15 16:08:29 +03:00
|
|
|
?. =(0 (mod (lent bs) 8))
|
|
|
|
~|("Invalid bech32 address: not 8bit" !!)
|
2020-10-20 11:28:19 +03:00
|
|
|
?. ?|(?=(%20 byt-len) ?=(%32 byt-len))
|
|
|
|
~|("Invalid bech32 address: must be 20 (P2WPKH) or 32 (P2WSH) bytes" !!)
|
|
|
|
%- hash
|
|
|
|
[byt-len (to-atom:bits 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
|
|
|
|
|= [=network pubkey=@ux]
|
2020-10-16 14:02:55 +03:00
|
|
|
^- (unit bech32-address)
|
2020-10-05 10:55:10 +03:00
|
|
|
?. =(33 (met 3 pubkey))
|
|
|
|
~|('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
|
2020-10-16 12:28:51 +03:00
|
|
|
[0 (convert:bits 5 (zeros-brip:bits 160 dat:(hash-160 pubkey)))]
|
|
|
|
++ encode-hash-160
|
|
|
|
|= [=network h160=byts]
|
2020-10-16 14:02:55 +03:00
|
|
|
^- (unit bech32-address)
|
2020-10-16 12:28:51 +03:00
|
|
|
=/ prefix (~(get by prefixes) network)
|
|
|
|
?~ prefix ~
|
|
|
|
:- ~
|
|
|
|
%+ encode-raw u.prefix
|
|
|
|
[0 (convert:bits 5 (zeros-brip:bits 160 dat.h160))]
|
2020-10-05 10:49:09 +03:00
|
|
|
--
|
2020-10-04 11:56:38 +03:00
|
|
|
::
|
|
|
|
--
|