github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/cryptocurrency.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package libkb 5 6 import ( 7 "crypto/sha256" 8 "errors" 9 "strings" 10 11 "github.com/btcsuite/btcutil/bech32" 12 ) 13 14 type CryptocurrencyType int 15 16 type CryptocurrencyFamily string 17 18 const ( 19 CryptocurrencyTypeNone CryptocurrencyType = -1 20 CryptocurrencyTypeBTC CryptocurrencyType = 0 // 0x0 21 CryptocurrencyTypeBTCMultiSig CryptocurrencyType = 5 // 0x5 22 CryptocurrencyTypeBTCSegwit CryptocurrencyType = 0x6263 // "bc" 23 CryptocurrencyTypeZCashShielded CryptocurrencyType = 5786 // 0x169a 24 CryptocurrencyTypeZCashTransparentP2PKH CryptocurrencyType = 7352 // 0x1cb8 25 CryptocurrencyTypeZCashTransparentP2SH CryptocurrencyType = 7357 // 0x1cbd 26 CryptocurrencyTypeZCashSapling CryptocurrencyType = 0x7a73 // "zs" 27 ) 28 29 const ( 30 CryptocurrencyFamilyNone CryptocurrencyFamily = "" 31 CryptocurrencyFamilyBitcoin CryptocurrencyFamily = "bitcoin" 32 CryptocurrencyFamilyZCash CryptocurrencyFamily = "zcash" 33 ) 34 35 // Wallet and cryptocurrency are separate systems. 36 // Wallet links have reverse signatures, and the control secrets are in keybase. 37 // Whereas Cryptocurrency links are generally public only and have no reverse sigs. 38 // CryptocurrencyFamily and WalletNetwork are defined next to each other so that 39 // someone will notice if they start to overlap. 40 type WalletNetwork string 41 42 const ( 43 WalletNetworkStellar WalletNetwork = "stellar" 44 ) 45 46 type CryptocurrencyPrefix struct { 47 Type CryptocurrencyType 48 Prefix []byte 49 Len int 50 } 51 52 func (p CryptocurrencyType) String() string { 53 switch p { 54 case CryptocurrencyTypeBTC, CryptocurrencyTypeBTCMultiSig, CryptocurrencyTypeBTCSegwit: 55 return "bitcoin" 56 case CryptocurrencyTypeZCashShielded: 57 return "zcash.z" 58 case CryptocurrencyTypeZCashTransparentP2PKH, CryptocurrencyTypeZCashTransparentP2SH: 59 return "zcash.t" 60 case CryptocurrencyTypeZCashSapling: 61 return "zcash.s" 62 default: 63 return "" 64 } 65 } 66 67 func (p CryptocurrencyType) ToCryptocurrencyFamily() CryptocurrencyFamily { 68 switch p { 69 case CryptocurrencyTypeBTC, CryptocurrencyTypeBTCMultiSig, CryptocurrencyTypeBTCSegwit: 70 return CryptocurrencyFamilyBitcoin 71 case CryptocurrencyTypeZCashShielded, CryptocurrencyTypeZCashTransparentP2PKH, CryptocurrencyTypeZCashTransparentP2SH, CryptocurrencyTypeZCashSapling: 72 return CryptocurrencyFamilyZCash 73 default: 74 return CryptocurrencyFamilyNone 75 } 76 } 77 78 type BtcOpts struct{} 79 80 var knownPrefixes = []CryptocurrencyPrefix{ 81 {CryptocurrencyTypeBTC, []byte{0x0}, 25}, 82 {CryptocurrencyTypeBTCMultiSig, []byte{0x5}, 25}, 83 {CryptocurrencyTypeZCashShielded, []byte{0x16, 0x9a}, 70}, 84 {CryptocurrencyTypeZCashTransparentP2PKH, []byte{0x1c, 0xb8}, 26}, 85 {CryptocurrencyTypeZCashTransparentP2SH, []byte{0x1c, 0xbd}, 26}, 86 } 87 88 func safeBufPrefix(b []byte, n int) []byte { 89 if len(b) > n { 90 return b[0:n] 91 } 92 return b 93 } 94 95 func addressToType(b []byte) (CryptocurrencyType, error) { 96 for _, kp := range knownPrefixes { 97 if FastByteArrayEq(safeBufPrefix(b, len(kp.Prefix)), kp.Prefix) { 98 if len(b) != kp.Len { 99 return CryptocurrencyTypeNone, errors.New("wrong address length") 100 } 101 return kp.Type, nil 102 } 103 } 104 return CryptocurrencyTypeNone, errors.New("address type not known") 105 } 106 107 func cryptocurrencyParseZCashSapling(s string) (CryptocurrencyType, []byte, error) { 108 hrp, decoded, err := bech32.Decode(s) 109 if err != nil { 110 return CryptocurrencyTypeNone, nil, err 111 } 112 if strings.ToLower(hrp) != "zs" { 113 return CryptocurrencyTypeNone, nil, errors.New("bad prefix after bech32 parse") 114 } 115 return CryptocurrencyTypeZCashSapling, decoded, nil 116 } 117 118 func cryptocurrencyIsZCashSaplingViaPrefix(s string) bool { 119 return len(s) > 3 && strings.ToLower(s[0:3]) == "zs1" 120 } 121 122 func cryptocurrencyParseBTCSegwit(s string) (CryptocurrencyType, []byte, error) { 123 hrp, decoded, err := bech32.Decode(s) 124 if err != nil { 125 return CryptocurrencyTypeNone, nil, err 126 } 127 if strings.ToLower(hrp) != "bc" { 128 return CryptocurrencyTypeNone, nil, errors.New("bad prefix after bech32 parse") 129 } 130 return CryptocurrencyTypeBTCSegwit, decoded, nil 131 } 132 133 func cryptocurrencyIsBTCSegwitViaPrefix(s string) bool { 134 return len(s) > 3 && strings.ToLower(s[0:3]) == "bc1" 135 } 136 137 func CryptocurrencyParseAndCheck(s string) (CryptocurrencyType, []byte, error) { 138 139 switch { 140 case cryptocurrencyIsBTCSegwitViaPrefix(s): 141 return cryptocurrencyParseBTCSegwit(s) 142 case cryptocurrencyIsZCashSaplingViaPrefix(s): 143 return cryptocurrencyParseZCashSapling(s) 144 } 145 146 buf, err := Base58.DecodeString(s) 147 if err != nil { 148 return CryptocurrencyTypeNone, nil, err 149 } 150 l := len(buf) 151 if l < 8 { 152 return CryptocurrencyTypeNone, nil, errors.New("truncated cryptocurrency address") 153 } 154 if l > 256 { 155 return CryptocurrencyTypeNone, nil, errors.New("cryptocurrency address too long") 156 } 157 typ, err := addressToType(buf) 158 if err != nil { 159 return typ, nil, err 160 } 161 pkhash := buf[0:(l - 4)] 162 c1 := buf[(l - 4):] 163 tmp := sha256.Sum256(pkhash) 164 tmp2 := sha256.Sum256(tmp[:]) 165 c2 := tmp2[0:4] 166 167 if !FastByteArrayEq(c1, c2) { 168 return CryptocurrencyTypeNone, nil, errors.New("bad checksum in address") 169 } 170 return typ, pkhash, nil 171 } 172 173 func BtcAddrCheck(s string, _ *BtcOpts) (version int, pkhash []byte, err error) { 174 var typ CryptocurrencyType 175 typ, pkhash, err = CryptocurrencyParseAndCheck(s) 176 if err != nil { 177 return version, pkhash, err 178 } 179 if typ != CryptocurrencyTypeBTC && typ != CryptocurrencyTypeBTCMultiSig && typ != CryptocurrencyTypeBTCSegwit { 180 return int(CryptocurrencyTypeNone), nil, errors.New("unrecognizable btc address") 181 } 182 return int(typ), pkhash, nil 183 }