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 }