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  }