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