decred.org/dcrdex@v1.0.5/dex/networks/zec/addr.go (about) 1 // This code is available on the terms of the project LICENSE.md file, 2 // also available online at https://blueoakcouncil.org 3 4 package zec 5 6 import ( 7 "crypto/sha256" 8 "fmt" 9 10 "github.com/btcsuite/btcd/btcutil" 11 "github.com/btcsuite/btcd/chaincfg" 12 "github.com/decred/base58" 13 ) 14 15 type AddressParams struct { 16 PubKeyHashAddrID [2]byte 17 ScriptHashAddrID [2]byte 18 } 19 20 // DecodeAddress decodes an address string into an internal btc address. 21 // Zcash uses a double SHA-256 checksum but with a 2-byte address ID, so 22 // a little customization is needed. 23 // TODO: There also appears to be a bech32 encoding and something called a 24 // "unified payment address", but for our use of this function client-side, 25 // we will never generate those addresses. 26 // Do we need to revisit before NU5? 27 func DecodeAddress(a string, addrParams *AddressParams, btcParams *chaincfg.Params) (btcutil.Address, error) { 28 b := base58.Decode(a) 29 if len(b) < 7 { 30 return nil, fmt.Errorf("invalid address") 31 } 32 33 var addrID [2]byte 34 copy(addrID[:], b[:2]) 35 36 var checkSum [4]byte 37 copy(checkSum[:], b[len(b)-4:]) 38 39 data := b[2 : len(b)-4] 40 hashDigest := b[:len(b)-4] 41 42 if checksum(hashDigest) != checkSum { 43 return nil, fmt.Errorf("invalid checksum") 44 } 45 46 switch addrID { 47 case addrParams.PubKeyHashAddrID: 48 return btcutil.NewAddressPubKeyHash(data, btcParams) 49 case addrParams.ScriptHashAddrID: 50 return btcutil.NewAddressScriptHashFromHash(data, btcParams) 51 } 52 53 return nil, fmt.Errorf("unknown zec address type %v %v %v", addrID, addrParams.PubKeyHashAddrID, addrParams.ScriptHashAddrID) 54 } 55 56 // RecodeAddress converts an internal btc address to a Zcash address string. 57 func RecodeAddress(addr string, addrParams *AddressParams, btcParams *chaincfg.Params) (string, error) { 58 btcAddr, err := btcutil.DecodeAddress(addr, btcParams) 59 if err != nil { 60 return "", err 61 } 62 63 return EncodeAddress(btcAddr, addrParams) 64 } 65 66 // EncodeAddress converts a btcutil.Address from the BTC backend into a Zcash 67 // address string. 68 func EncodeAddress(btcAddr btcutil.Address, addrParams *AddressParams) (string, error) { 69 switch btcAddr.(type) { 70 case *btcutil.AddressPubKeyHash: 71 return b58Encode(btcAddr.ScriptAddress(), addrParams.PubKeyHashAddrID), nil 72 case *btcutil.AddressScriptHash: 73 return b58Encode(btcAddr.ScriptAddress(), addrParams.ScriptHashAddrID), nil 74 } 75 76 return "", fmt.Errorf("unsupported address type %T", btcAddr) 77 } 78 79 // b58Encode base-58 encodes the address with the serialization 80 // 81 // addrID | input | 4-bytes of double-sha256 checksum 82 func b58Encode(input []byte, addrID [2]byte) string { 83 b := make([]byte, 0, 2+len(input)+4) 84 b = append(b, addrID[:]...) 85 b = append(b, input[:]...) 86 cksum := checksum(b) 87 b = append(b, cksum[:]...) 88 return base58.Encode(b) 89 } 90 91 // checksum computes a checksum, which is the first 4 bytes of a double-sha256 92 // hash of the input message. 93 func checksum(input []byte) (cksum [4]byte) { 94 h := sha256.Sum256(input) 95 h2 := sha256.Sum256(h[:]) 96 copy(cksum[:], h2[:4]) 97 return 98 }