github.com/bchainhub/blockbook@v0.3.2/bchain/coins/bch/bcashparser.go (about) 1 package bch 2 3 import ( 4 "blockbook/bchain" 5 "blockbook/bchain/coins/btc" 6 "fmt" 7 8 "github.com/martinboehm/bchutil" 9 "github.com/martinboehm/btcutil" 10 "github.com/martinboehm/btcutil/chaincfg" 11 "github.com/martinboehm/btcutil/txscript" 12 "github.com/schancel/cashaddr-converter/address" 13 ) 14 15 // AddressFormat type is used to specify different formats of address 16 type AddressFormat = uint8 17 18 const ( 19 // Legacy AddressFormat is the same as Bitcoin 20 Legacy AddressFormat = iota 21 // CashAddr AddressFormat is new Bitcoin Cash standard 22 CashAddr 23 ) 24 25 const ( 26 // MainNetPrefix is CashAddr prefix for mainnet 27 MainNetPrefix = "bitcoincash:" 28 // TestNetPrefix is CashAddr prefix for testnet 29 TestNetPrefix = "bchtest:" 30 // RegTestPrefix is CashAddr prefix for regtest 31 RegTestPrefix = "bchreg:" 32 ) 33 34 var ( 35 // MainNetParams are parser parameters for mainnet 36 MainNetParams chaincfg.Params 37 // TestNetParams are parser parameters for testnet 38 TestNetParams chaincfg.Params 39 // RegtestParams are parser parameters for regtest 40 RegtestParams chaincfg.Params 41 ) 42 43 func init() { 44 MainNetParams = chaincfg.MainNetParams 45 MainNetParams.Net = bchutil.MainnetMagic 46 47 TestNetParams = chaincfg.TestNet3Params 48 TestNetParams.Net = bchutil.TestnetMagic 49 50 RegtestParams = chaincfg.RegressionNetParams 51 RegtestParams.Net = bchutil.Regtestmagic 52 } 53 54 // BCashParser handle 55 type BCashParser struct { 56 *btc.BitcoinParser 57 AddressFormat AddressFormat 58 } 59 60 // NewBCashParser returns new BCashParser instance 61 func NewBCashParser(params *chaincfg.Params, c *btc.Configuration) (*BCashParser, error) { 62 var format AddressFormat 63 switch c.AddressFormat { 64 case "": 65 fallthrough 66 case "cashaddr": 67 format = CashAddr 68 case "legacy": 69 format = Legacy 70 default: 71 return nil, fmt.Errorf("Unknown address format: %s", c.AddressFormat) 72 } 73 p := &BCashParser{ 74 BitcoinParser: btc.NewBitcoinParser(params, c), 75 AddressFormat: format, 76 } 77 p.OutputScriptToAddressesFunc = p.outputScriptToAddresses 78 return p, nil 79 } 80 81 // GetChainParams contains network parameters for the main Bitcoin Cash network, 82 // the regression test Bitcoin Cash network, the test Bitcoin Cash network and 83 // the simulation test Bitcoin Cash network, in this order 84 func GetChainParams(chain string) *chaincfg.Params { 85 if !chaincfg.IsRegistered(&MainNetParams) { 86 err := chaincfg.Register(&MainNetParams) 87 if err == nil { 88 err = chaincfg.Register(&TestNetParams) 89 } 90 if err == nil { 91 err = chaincfg.Register(&RegtestParams) 92 } 93 if err != nil { 94 panic(err) 95 } 96 } 97 switch chain { 98 case "test": 99 return &TestNetParams 100 case "regtest": 101 return &RegtestParams 102 default: 103 return &MainNetParams 104 } 105 } 106 107 // GetAddrDescFromAddress returns internal address representation of given address 108 func (p *BCashParser) GetAddrDescFromAddress(address string) (bchain.AddressDescriptor, error) { 109 return p.addressToOutputScript(address) 110 } 111 112 // addressToOutputScript converts bitcoin address to ScriptPubKey 113 func (p *BCashParser) addressToOutputScript(address string) ([]byte, error) { 114 if isCashAddr(address) { 115 da, err := bchutil.DecodeAddress(address, p.Params) 116 if err != nil { 117 return nil, err 118 } 119 script, err := bchutil.PayToAddrScript(da) 120 if err != nil { 121 return nil, err 122 } 123 return script, nil 124 } 125 da, err := btcutil.DecodeAddress(address, p.Params) 126 if err != nil { 127 return nil, err 128 } 129 script, err := txscript.PayToAddrScript(da) 130 if err != nil { 131 return nil, err 132 } 133 return script, nil 134 } 135 136 func isCashAddr(addr string) bool { 137 n := len(addr) 138 switch { 139 case n > len(MainNetPrefix) && addr[0:len(MainNetPrefix)] == MainNetPrefix: 140 return true 141 case n > len(TestNetPrefix) && addr[0:len(TestNetPrefix)] == TestNetPrefix: 142 return true 143 case n > len(RegTestPrefix) && addr[0:len(RegTestPrefix)] == RegTestPrefix: 144 return true 145 } 146 147 return false 148 } 149 150 // outputScriptToAddresses converts ScriptPubKey to bitcoin addresses 151 func (p *BCashParser) outputScriptToAddresses(script []byte) ([]string, bool, error) { 152 // convert possible P2PK script to P2PK, which bchutil can process 153 var err error 154 script, err = txscript.ConvertP2PKtoP2PKH(p.Params.Base58CksumHasher, script) 155 if err != nil { 156 return nil, false, err 157 } 158 a, err := bchutil.ExtractPkScriptAddrs(script, p.Params) 159 if err != nil { 160 // do not return unknown script type error as error 161 if err.Error() == "unknown script type" { 162 // try OP_RETURN script 163 or := p.TryParseOPReturn(script) 164 if or != "" { 165 return []string{or}, false, nil 166 } 167 return []string{}, false, nil 168 } 169 return nil, false, err 170 } 171 // EncodeAddress returns CashAddr address 172 addr := a.EncodeAddress() 173 if p.AddressFormat == Legacy { 174 da, err := address.NewFromString(addr) 175 if err != nil { 176 return nil, false, err 177 } 178 ca, err := da.Legacy() 179 if err != nil { 180 return nil, false, err 181 } 182 addr, err = ca.Encode() 183 if err != nil { 184 return nil, false, err 185 } 186 } 187 return []string{addr}, len(addr) > 0, nil 188 }