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  }