diff --git a/bippy-scratch.md b/bippy-scratch.md index 2a1fdcc52..b7f7e85ae 100644 --- a/bippy-scratch.md +++ b/bippy-scratch.md @@ -1,3 +1,5 @@ +## NOTE +The below requires norsyr's fix to `decompress-point` in order to work. ## base58 Converts a base58 zpub to hex @@ -9,18 +11,25 @@ Converts a base58 zpub to hex **Import lib; optionally set up env** ``` =btca -build-file %/lib/btc-address/hoon -=xpub "zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs" +=bip32 -build-file %/lib/bip32/hoon +=xpub2 "xpub6DnWFmBQfQm1wxvKkCJjXwE6H4v8FTwUuhjDQ9ZpJnFDfhA8Dwmg71yPKyjUE93D2CB6MdnWNvGmwsb3fpd4oRJ2YcyMZoMpLU3BjpmQAny" ``` **Test child public key from xpub** ``` -(child-from-xpub:btca xpub 1) +`@ux`(child-from-xpub:btca xpub2 0) +`@ux`(child-from-xpub:btca xpub 1) (child-from-xpub:btca xpub (dec (bex 31))) :: should error as index is too high (hardened key range) (child-from-xpub:btca xpub (bex 31)) ``` +**Same, with Jose's bip32 library** +``` +`@ux`(compress-point:secp256k1:secp:crypto pub:(derive-public:(from-extended:bip32 xpub2) 0)) +``` + **Test xpub parsing** ``` (parse-xpub:btca xpub) @@ -28,9 +37,10 @@ Converts a base58 zpub to hex **Test addition and ECC point checking** ``` -=px (parse-xpub:btca xpub) +=px (parse-xpub:btca xpub2) =pubk ?~ px ~ pubk.u.px (is-point:btca pubk) +(pubkey-to-point:btca pubk) =index 256 `@ux`(add (lsh 3 4 (big-endian-brap:btca pubk)) index) ``` diff --git a/lib/bip32.hoon b/lib/bip32.hoon new file mode 100644 index 000000000..ec2be68cd --- /dev/null +++ b/lib/bip32.hoon @@ -0,0 +1,243 @@ +:: bip32 implementation in hoon +:: +:: to use, call one of the core initialization arms. +:: using the produced core, derive as needed and take out the data you want. +:: +::NOTE tested to be correct against +:: https://en.bitcoin.it/wiki/BIP_0032_TestVectors +:: +=, hmac:crypto +=, secp:crypto +=+ ecc=secp256k1 +:: +:: prv: private key +:: pub: public key +:: cad: chain code +:: dep: depth in chain +:: ind: index at depth +:: pif: parent fingerprint (4 bytes) +|_ [prv=@ pub=pont cad=@ dep=@ud ind=@ud pif=@] +:: ++= keyc [key=@ cai=@] :: prv/pub key + chain code +:: +:: elliptic curve operations and values +:: +++ point priv-to-pub.ecc +:: +++ ser-p compress-point.ecc +:: +++ n ^n:ecc +:: +:: core initialization +:: +++ from-seed + |= byts + ^+ +> + =+ der=(hmac-sha512l [12 'dees nioctiB'] [wid dat]) + =+ pri=(cut 3 [32 32] der) + +>.$(prv pri, pub (point pri), cad (cut 3 [0 32] der)) +:: +++ from-private + |= keyc + +>(prv key, pub (point key), cad cai) +:: +++ from-public + |= keyc + +>(pub (decompress-point.ecc key), cad cai) +:: +++ from-public-point + |= [pon=pont cai=@] + +>(pub pon, cad cai) +:: +++ from-extended + |= t=tape + =+ x=(de-base58check 4 t) + => |% + ++ take + |= b=@ud + ^- [v=@ x=@] + :- (end 3 b x) + (rsh 3 b x) + -- + =^ k x (take 33) + =^ c x (take 32) + =^ i x (take 4) + =^ p x (take 4) + =^ d x (take 1) + ?> =(0 x) :: sanity check + %. [d i p] + =< set-metadata + =+ v=(swag [1 3] t) + ?: =("prv" v) (from-private k c) + ?: =("pub" v) (from-public k c) + !! +:: +++ set-metadata + |= [d=@ud i=@ud p=@] + +>(dep d, ind i, pif p) +:: +:: derivation +:: +++ derivation-path + ;~ pfix + ;~(pose (jest 'm/') (easy ~)) + %+ most net + ;~ pose + %+ cook + |=(i=@ (add i (bex 31))) + ;~(sfix dem say) + :: + dem + == == +:: +++ derive-path + |= t=tape + %- derive-sequence + (scan t derivation-path) +:: +++ derive-sequence + |= j=(list @u) + ?~ j +> + =. +> (derive i.j) + $(j t.j) +:: +++ derive + ?: =(0 prv) + derive-public + derive-private +:: +++ derive-private + |= i=@u + ^+ +> + :: we must have a private key to derive the next one + ?: =(0 prv) + ~| %know-no-private-key + !! + :: derive child at i + =/ [left=@ right=@] + =- [(cut 3 [32 32] -) (cut 3 [0 32] -)] + %+ hmac-sha512l [32 cad] + :- 37 + ?: (gte i (bex 31)) + :: hardened child + (can 3 ~[4^i 32^prv 1^0]) + :: normal child + (can 3 ~[4^i 33^(ser-p (point prv))]) + =+ key=(mod (add left prv) n) + :: rare exception, invalid key, go to the next one + ?: |(=(0 key) (gte left n)) $(i +(i)) + %_ +>.$ + prv key + pub (point key) + cad right + dep +(dep) + ind i + pif fingerprint + == +:: +++ derive-public + |= i=@u + ^+ +> + :: public keys can't be hardened + ?: (gte i (bex 31)) + ~| %cant-derive-hardened-public-key + !! + :: derive child at i + =/ [left=@ right=@] + =- [(cut 3 [32 32] -) (cut 3 [0 32] -)] + %+ hmac-sha512l [32 cad] + 37^(can 3 ~[4^i 33^(ser-p pub)]) + :: rare exception, invalid key, go to the next one + ?: (gte left n) $(i +(i)) ::TODO or child key is "point at infinity" + %_ +>.$ + pub (jc-add.ecc (point left) pub) + cad right + dep +(dep) + ind i + pif fingerprint + == +:: +:: rendering +:: +++ private-key ?.(=(0 prv) prv ~|(%know-no-private-key !!)) +++ public-key (ser-p pub) +++ chain-code cad +++ private-chain [private-key cad] +++ public-chain [public-key cad] +:: +++ identity (hash160 public-key) +++ fingerprint (cut 3 [16 4] identity) +:: +++ address + |= network=?(%main %regtest %testnet) + ^- @uc + :: removes checksum + :: + %^ rsh 3 4 + %+ en-base58check + [4 (version-bytes network %pub %.n)] + [20 identity] +:: +++ prv-extended + |= network=?(%main %regtest %testnet) + %+ en-b58c-bip32 (version-bytes network %prv %.y) + (build-extended private-key) +:: +++ pub-extended + |= network=?(%main %regtest %testnet) + %+ en-b58c-bip32 (version-bytes network %pub %.y) + (build-extended public-key) +:: +++ build-extended + |= key=@ + %+ can 3 + :~ 33^key + 32^cad + 4^ind + 4^pif + 1^dep + == +:: +++ en-b58c-bip32 + |= [v=@ k=@] + %- en-base58:mimes:html + (en-base58check [4 v] [74 k]) +:: +:: base58check +:: +++ en-base58check + :: v: version bytes + :: d: data + |= [v=byts d=byts] + =+ p=[(add wid.v wid.d) (can 3 ~[d v])] + =- (can 3 ~[4^- p]) + %^ rsh 3 28 + (sha-256l:sha 32 (sha-256l:sha p)) +:: +++ de-base58check + :: vw: amount of version bytes + |= [vw=@u t=tape] + =+ x=(de-base58:mimes:html t) + =+ hash=(sha-256l:sha 32 (sha-256:sha (rsh 3 4 x))) + ?> =((end 3 4 x) (rsh 3 28 hash)) + (cut 3 [vw (sub (met 3 x) (add 4 vw))] x) +:: +++ hash160 + |= d=@ + (ripemd-160:ripemd:crypto 32 (sha-256:sha d)) +:: +++ version-bytes + |= [network=?(%main %regtest %testnet) type=?(%pub %prv) bip32=?] + ^- @ux + |^ + ?- type + %pub ?:(bip32 xpub-key pay-to-pubkey) + %prv ?:(bip32 xprv-key private-key) + == + :: + ++ pay-to-pubkey ?:(=(network %main) 0x0 0x6f) + ++ private-key ?:(=(network %main) 0x80 0xef) + ++ xpub-key ?:(=(network %main) 0x488.b21e 0x435.87cf) + ++ xprv-key ?:(=(network %main) 0x488.ade4 0x435.8394) + -- +-- diff --git a/lib/btc-address.hoon b/lib/btc-address.hoon index b2c352db0..017478f98 100644 --- a/lib/btc-address.hoon +++ b/lib/btc-address.hoon @@ -19,7 +19,7 @@ ++ pubkey-to-point |= =pubkey ^- pont:secp:crypto - ~& (lent pubkey) + ~& >> "compressed pubkey length: {<(lent pubkey)>}" %- decompress-point:secp256k1:secp:crypto (big-endian-brap pubkey) :: @@ -34,6 +34,7 @@ (de-base58:mimes:html xpub) =/ bytes=(list @ux) (big-endian-brip as-atom) + ~& >> "parse-xpub, depth: {<(snag 4 bytes)>}" =/ pp=parsed-xpub [(swag [13 32] bytes) (swag [45 33] bytes)] ?: (is-point pubk.pp) @@ -59,9 +60,13 @@ [il ir] ++ child-from-xpub |= [xpub=tape index=@ud] - =/ is=(unit il-ir) - %+ bind - (parse-xpub xpub) - |=(px=parsed-xpub (compute-i px index)) - is + =, secp256k1:secp:crypto + =/ upx=(unit parsed-xpub) + (parse-xpub xpub) + ?~ upx ~ + =/ px=parsed-xpub u.upx + =/ is (compute-i px index) + (compress-point (jc-add (priv-to-pub (big-endian-brap il.is)) (pubkey-to-point pubk.px))) -- + +:: `@ux`(compress-point:secp256k1:secp:crypto (jc-add:secp256k1:secp:crypto (pubkey-to-point:btca pubk.u.px) x))