decred.org/dcrdex@v1.0.3/client/asset/firo/exx.go (about)

     1  package firo
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/sha256"
     6  	"errors"
     7  	"fmt"
     8  
     9  	"decred.org/dcrdex/client/asset/btc"
    10  	dexfiro "decred.org/dcrdex/dex/networks/firo"
    11  	"github.com/btcsuite/btcd/btcutil"
    12  	"github.com/btcsuite/btcd/btcutil/base58"
    13  	"github.com/btcsuite/btcd/chaincfg"
    14  	"github.com/btcsuite/btcd/txscript"
    15  )
    16  
    17  // An EXX Address, also called an Exchange Address, is a re-encoding of a
    18  // transparent Firo P2PKH address. It is required in order to send funds to
    19  // Binance and some other centralized exchanges.
    20  
    21  // OP_EXCHANGEADDR is an unused bitcoin script opcode used to 'mark' the output
    22  // as an exchange address for the recipient.
    23  const (
    24  	ExxMainnet      byte = 0xbb
    25  	ExxTestnet      byte = 0xb1
    26  	ExxSimnet       byte = 0xac
    27  	OP_EXCHANGEADDR byte = 0xe0
    28  )
    29  
    30  var (
    31  	ExxVersionedPrefix = [2]byte{0x01, 0xb9}
    32  )
    33  
    34  // isExxAddress determines whether the address encoding is an EXX, EXT address
    35  // for mainnet, testnet or regtest networks.
    36  func isExxAddress(addr string) bool {
    37  	b, ver, err := base58.CheckDecode(addr)
    38  	switch {
    39  	case err != nil:
    40  		return false
    41  	case ver != ExxVersionedPrefix[0]:
    42  		return false
    43  	case len(b) != ripemd160HashSize+2:
    44  		return false
    45  	case b[0] != ExxVersionedPrefix[1]:
    46  		return false
    47  	}
    48  	return true
    49  }
    50  
    51  func checksum(input []byte) (csum [4]byte) {
    52  	h0 := sha256.Sum256(input)
    53  	h1 := sha256.Sum256(h0[:])
    54  	copy(csum[:], h1[:])
    55  	return
    56  }
    57  
    58  // decodeExxAddress decodes a Firo exchange address.
    59  func decodeExxAddress(encodedAddr string, net *chaincfg.Params) (btcutil.Address, error) {
    60  	const (
    61  		checksumLength = 4
    62  		prefixLength   = 3
    63  		decodedLen     = prefixLength + ripemd160HashSize + checksumLength // exx prefix + hash + checksum
    64  	)
    65  
    66  	decoded := base58.Decode(encodedAddr)
    67  
    68  	if len(decoded) != decodedLen {
    69  		return nil, fmt.Errorf("base 58 decoded to incorrect length. %d != %d", len(decoded), decodedLen)
    70  	}
    71  	netID := decoded[2]
    72  	var expNet string
    73  	switch netID {
    74  	case ExxMainnet:
    75  		expNet = dexfiro.MainNetParams.Name
    76  	case ExxTestnet:
    77  		expNet = dexfiro.TestNetParams.Name
    78  	case ExxSimnet:
    79  		expNet = dexfiro.RegressionNetParams.Name
    80  	default:
    81  		return nil, fmt.Errorf("unrecognized network id %x", netID)
    82  	}
    83  	if net.Name != expNet {
    84  		return nil, fmt.Errorf("wrong network. expected %s, got %s", net.Name, expNet)
    85  	}
    86  	csum := decoded[decodedLen-checksumLength:]
    87  	expectedCsum := checksum(decoded[:decodedLen-checksumLength])
    88  	if !bytes.Equal(csum, expectedCsum[:]) {
    89  		return nil, errors.New("checksum mismatch")
    90  	}
    91  	var h [ripemd160HashSize]byte
    92  	copy(h[:], decoded[prefixLength:decodedLen-checksumLength])
    93  	return &addressEXX{
    94  		hash:  h,
    95  		netID: netID,
    96  	}, nil
    97  }
    98  
    99  const ripemd160HashSize = 20
   100  
   101  // addressEXX implements btcutil.Address and btc.PaymentScripter
   102  type addressEXX struct {
   103  	hash  [ripemd160HashSize]byte
   104  	netID byte
   105  }
   106  
   107  var _ btcutil.Address = (*addressEXX)(nil)
   108  var _ btc.PaymentScripter = (*addressEXX)(nil)
   109  
   110  func (a *addressEXX) String() string {
   111  	return a.EncodeAddress()
   112  }
   113  
   114  func (a *addressEXX) EncodeAddress() string {
   115  	return base58.CheckEncode(append([]byte{ExxVersionedPrefix[1], a.netID}, a.hash[:]...), ExxVersionedPrefix[0])
   116  }
   117  
   118  func (a *addressEXX) ScriptAddress() []byte {
   119  	return a.hash[:]
   120  }
   121  
   122  func (a *addressEXX) IsForNet(chainParams *chaincfg.Params) bool {
   123  	switch a.netID {
   124  	case ExxMainnet:
   125  		return chainParams.Name == dexfiro.MainNetParams.Name
   126  	case ExxTestnet:
   127  		return chainParams.Name == dexfiro.TestNetParams.Name
   128  	case ExxSimnet:
   129  		return chainParams.Name == dexfiro.RegressionNetParams.Name
   130  	}
   131  	return false
   132  }
   133  
   134  func (a *addressEXX) PaymentScript() ([]byte, error) {
   135  	// OP_EXCHANGEADDR << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
   136  	return txscript.NewScriptBuilder().
   137  		AddOp(OP_EXCHANGEADDR).
   138  		AddOp(txscript.OP_DUP).
   139  		AddOp(txscript.OP_HASH160).
   140  		AddData(a.hash[:]).
   141  		AddOp(txscript.OP_EQUALVERIFY).
   142  		AddOp(txscript.OP_CHECKSIG).
   143  		Script()
   144  }