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  }