tests passing for addresses

This commit is contained in:
timlucmiptev 2021-01-27 15:09:25 +02:00 committed by ixv
parent b485955df0
commit 5322c66d43
4 changed files with 194 additions and 132 deletions

View File

@ -2,3 +2,4 @@
*.sh
*.md
*.txt
*.js

View File

@ -45,56 +45,71 @@
%- ripemd-160
(sha256 val)
::
++ base58check
|%
++ checksum dsha256
++ encode
|= body=hexb
^- tape
=> :- .
%- cat:byt
:~ body
(take:byt 4 (checksum body))
++ pubkey-to-address
|= [=bipt =network pubkey=hexb]
^- address
?- bipt
%44
:- %base58
=< ^-(@uc dat)
%- cat:byt
:- ?- network
%main 1^0x0
%testnet 1^0x6f
==
(en-base58:mimes:html dat)
::
++ decode
|= b=tape
^- hexb
~| "Invalid base58check input: {<b>}"
=/ h=@ux
(de-base58:mimes:html b)
:: handle leading 0 ('1' in base68)
=/ len=@
?: =('1' (snag 0 b))
(add 1 (met 3 h))
(met 3 h)
=/ body=hexb (take (sub len 4) len^h)
=/ check=hexb (drop:byt (sub len 4) len^h)
?> =(check (take:byt 4 (checksum body)))
body
--
~[(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))
==
::
++ script-pubkey
|= =address
^- hexb
?- -.address
:: TODO: make work for P2WSH (32 byte bech32)
%bech32
=+ h=(to-hex:bech32 address)
=+ h=(from-address:bech32 +.address)
%- cat:byt
:~ 1^0x0
1^wid.h
h
==
:: TODO switch on 1/3 below
:: https://bitcoinops.org/en/tools/calc-size/
:: above link shows how to make P2PKH and P2SH, which I'd need here
::
%base58
~|("base58 not supported" !!)
:: (decode:base58check (trip +.address))
=/ 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]
?: =(0x6f lead-byt) [%49 %testnet]
~|("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
==
==
:: +txu: tx utility functions
::
++ txu
|%
++ en
@ -451,6 +466,7 @@
::
++ take
|= [n=@ b=byts]
^- byts
?: (gth n wid.b)
[n dat.b]
[n (rsh [3 (sub wid.b n)] dat.b)]
@ -464,50 +480,52 @@
0^0x0
=+ n-take=(sub wid.b n)
[n-take (end [3 n-take] dat.b)]
:: Converts a list of bits to a list of n-bit numbers
:: input-bits should be big-endian
::
++ bit
|%
:: rip atom a with num-bits. Preserve leading 0s, big endian
:: returns a list of bits
++ cat
|= bs=(list bits)
^- bits
:- (roll (turn bs |=(b=bits wid.b)) add)
(can 0 (flop bs))
::
++ zeros-brip
|= [num-bits=@ a=@]
^- (list @)
=/ bits=(list @) (flop (rip 0 a))
=/ pad=@ (sub num-bits (lent bits))
(weld (reap pad 0) bits)
:: +convert: list of bits to a list of atoms each with bitwidth d(est)
++ take
|= [n=@ b=bits]
^- bits
?: (gth n wid.b)
[n dat.b]
[n (rsh [0 (sub wid.b n)] dat.b)]
::
++ convert
|= [d=@ bits=(list @)]
^- (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)))
:: 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]
++ drop
|= [n=@ b=byts]
^- bits
?: (gte n wid.b)
0^0b0
=+ n-take=(sub wid.b n)
[n-take (end [0 n-take] dat.b)]
:: +from-atoms: convert atoms of bitwidth to bits
::
++ from-digits
++ from-atoms
|= [bitwidth=@ digits=(list @)]
^- (list @)
%- zing
^- bits
%- cat:bit
%+ 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
|= a=@
?> (lte (met 0 a) bitwidth)
[bitwidth `@ub`a]
:: +to-atoms: convert bits to atoms of bitwidth
::
++ to-atom
|= bits=(list @)
^- @
%+ rep 0
%- flop bits
++ to-atoms
|= [bitwidth=@ bs=bits]
^- (list @)
=| res=(list @)
?> =(0 (mod wid.bs bitwidth))
|-
?: =(0 wid.bs) res
%= $
res (snoc res dat:(take bitwidth bs))
bs (drop bitwidth bs)
==
--
::
++ bech32
@ -550,8 +568,7 @@
++ verify-checksum
|= [hrp=tape data-and-checksum=(list @)]
^- ?
%+ |=([a=@ b=@] =(a b))
1
%- |=(a=@ =(1 a))
%- polymod
(weld (expand-hrp hrp) data-and-checksum)
::
@ -559,6 +576,7 @@
|= [hrp=tape data=(list @)]
^- (list @)
:: xor 1 with the polymod
::
=/ pmod=@
%+ mix 1
%- polymod
@ -589,16 +607,15 @@
::
++ encode-raw
|= [hrp=tape data=(list @)]
^- bech32-a
^- cord
=/ combined=(list @)
(weld data (checksum hrp data))
:- %bech32
%- crip
(zing ~[hrp "1" (tape (murn combined value-to-charset))])
++ decode-raw
|= b=bech32-a
|= body=cord
^- (unit raw-decoded)
=/ bech (cass (trip +.b)) :: to lowercase
=/ bech (cass (trip body)) :: to lowercase
=/ pos (flop (fand "1" bech))
?~ pos ~
=/ last-1=@ i.pos
@ -616,41 +633,35 @@
~
=/ checksum-pos (sub (lent data-and-checksum) 6)
`[hrp (scag checksum-pos data-and-checksum) (slag checksum-pos data-and-checksum)]
:: goes from a bech32 address to hex. Returns byts to preserve leading 0s
:: +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)
::
++ to-hex
|= b=bech32-a
++ from-address
|= body=cord
^- hexb
=/ d=(unit raw-decoded) (decode-raw b)
?~ d ~|("Invalid bech32 address" !!)
=/ bs=(list @)
(from-digits:bit 5 (slag 1 data.u.d))
=/ byt-len=@ (div (lent bs) 8)
?. =(0 (mod (lent bs) 8))
~|("Invalid bech32 address: not 8bit" !!)
?. ?|(?=(%20 byt-len) ?=(%32 byt-len))
~|("Invalid bech32 address: must be 20 (P2WPKH) or 32 (P2WSH) bytes" !!)
[byt-len (to-atom:bit bs)]
~| "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))]
:: pubkey is the 33 byte ECC compressed public key
::
++ encode-pubkey
|= [=network pubkey=byts]
^- (unit bech32-a)
^- (unit cord)
?. =(33 wid.pubkey)
~|('pubkey must be a 33 byte ECC compressed public key' !!)
=/ prefix (~(get by prefixes) network)
?~ prefix ~
:- ~
%+ encode-raw u.prefix
[0 (convert:bit 5 (zeros-brip:bit 160 dat:(hash-160 pubkey)))]
++ encode-hash-160
|= [=network h160=byts]
^- (unit bech32-a)
=/ prefix (~(get by prefixes) network)
?~ prefix ~
:- ~
%+ encode-raw u.prefix
[0 (convert:bit 5 (zeros-brip:bit 160 dat.h160))]
[0v0 (to-atoms:bit 5 [160 `@ub`dat:(hash-160 pubkey)])]
--
::
--

View File

@ -3,10 +3,12 @@
|%
+$ network ?(%main %testnet)
+$ hexb [wid=@ dat=@ux] :: hex byts
+$ bits [wid=@ dat=@ub]
+$ xpub @ta
+$ address ?(base58-a bech32-a)
+$ base58-a $%([%base58 cord])
+$ bech32-a $%([%bech32 cord])
+$ address
$% [%base58 @uc]
[%bech32 cord]
==
+$ fprint hexb
+$ bipt $?(%44 %49 %84)
+$ chyg $?(%0 %1)
@ -26,7 +28,7 @@
|%
+$ data
$: is=(list input)
os=(list output)
os=(list output)
locktime=@ud
nversion=@ud
segwit=(unit @ud)

View File

@ -1,40 +1,88 @@
/+ *test, *btc
|%
+$ key-address [pubkey=hexb =address]
+$ pubkey-vec [=bipt =network pubkey=hexb =address]
+$ script-pubkey-vec [=address spk=hexb]
++ vectors
|%
++ p2wsh1
:: abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about
^- key-address
:* 33^0x3.9b3b.694b.8fc5.b5e0.7fb0.69c7.83ca.c754.f5d3.8c3e.08be.d196.0e31.fdb1.dda3.5c24
[%base58 '37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf']
++ base32
:* atoms=~[0 31 31 0 31 0]
bs=[30 0b1.1111.1111.1000.0011.1110.0000]
==
:: below use mnemonic:
:: abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about
::
++ pubkeys
^- (list pubkey-vec)
:~ :* %84
%main
33^0x3.30d5.4fd0.dd42.0a6e.5f8d.3624.f5f3.482c.ae35.0f79.d5f0.753b.f5be.ef9c.2d91.af3c
[%bech32 'bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu']
==
::
:* %44
%main
33^0x3.aaeb.52dd.7494.c361.049d.e67c.c680.e83e.bcbb.bdbe.b136.37d9.2cd8.45f7.0308.af5e
[%base58 0c1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA]
==
::
:* %49
%main
33^0x3.9b3b.694b.8fc5.b5e0.7fb0.69c7.83ca.c754.f5d3.8c3e.08be.d196.0e31.fdb1.dda3.5c24
[%base58 0c37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf]
==
::
:* %84
%testnet
33^0x2.e7ab.2537.b5d4.9e97.0309.aae0.6e9e.49f3.6ce1.c9fe.bbd4.4ec8.e0d1.cca0.b4f9.c319
[%bech32 'tb1q6rz28mcfaxtmd6v789l9rrlrusdprr9pqcpvkl']
==
::
:* %44
%testnet
33^0x2.a745.1395.7353.69f2.ecdf.c829.c0f7.74e8.8ef1.303d.fe5b.2f04.dbaa.b30a.535d.fdd6
[%base58 0cmkpZhYtJu2r87Js3pDiWJDmPte2NRZ8bJV]
==
::
:* %49
%testnet
33^0x3.a1af.804a.c108.a8a5.1782.198c.2d03.4b28.bf90.c880.3f5a.53f7.6276.fa69.a4ea.e77f
[%base58 0c2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2]
==
==
::
++ script-pubkeys
^- (list script-pubkey-vec)
:~ :* [%bech32 'bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu']
[wid=22 dat=0x14.c0ce.bcd6.c3d3.ca8c.75dc.5ec6.2ebe.5533.0ef9.10e2]
==
::
:* [%bech32 'bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3']
[wid=34 dat=0x20.1863.143c.14c5.1668.04bd.1920.3356.da13.6c98.5678.cd4d.27a1.b8c6.3296.0490.3262]
==
::
==
++ psbt1
^- base64:psbt
'cHNidP8BAHEBAAAAAeECXCgB7Co1v8MkjfseiHiAmD6f5IGHggCodBxdcE6DAQAAAAD/////AtAHAAAAAAAAFgAUmEQgSFlZ0Noj5/6QK/MVgUzHtksw/AAAAAAAABYAFCj6E0N2r50pGUjGeWDtDrP3JxeQAAAAAAABAR+EJQEAAAAAABYAFDi7rsJdW0ystnpbESNGOx1ypK7lIgYDXS/0FkRiWJny6+uNNGt7tpCWk96YQDJnUKqZLWGx9JYYvu/erVQAAIAAAACAAAAAgAEAAAADAAAAAAAiAgOQCPy++fOAW9XIFBqcaec8QU0qPeSDFLD1PR/QrWTSrBi+796tVAAAgAAAAIAAAACAAQAAAAQAAAAA'
--
++ pk-to-p2wsh
|= pubkey=hexb
^- hexb
%- hash-160
%- cat:byt
:~ 1^0x0
1^0x14
(hash-160 pubkey)
==
::
++ run
:: base58check encode/decode
:: bit manipulation
::
=/ p2wsh1=key-address
p2wsh1:vectors
=/ with-version=hexb
(cat:byt ~[1^0x5 (pk-to-p2wsh pubkey.p2wsh1)])
=/ encoding=tape
(encode:base58check with-version)
?. =(address.p2wsh1 [%base58 (crip encoding)])
~|("pubkey doesn't encode to base58" !!)
?. =(with-version (decode:base58check encoding))
~|("'can't decode base58 encoding" !!)
=/ atoms=(list @) -:base32:vectors
=/ =bits +:base32:vectors
?. ?& =((from-atoms:bit 5 atoms) bits)
=((to-atoms:bit 5 bits) atoms)
==
~|("base32 bit manipulation failed" !!)
:: pubkey to address
?. %+ levy pubkeys:vectors
|= [=bipt =network pubkey=hexb =address]
=(address (pubkey-to-address bipt network pubkey))
~|("pubkey doesn't encode to address" !!)
::
:: script-pubkey from address
::
?. %+ levy script-pubkeys:vectors
|= [a=address spk=hexb]
=(spk (script-pubkey a))
~|("script-pubkey doesn't encode from address" !!)
"All tests passed."
--