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  }