blockbook/bchain/coins/bch/bcashparser.go

191 lines
4.3 KiB
Go

package bch
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"fmt"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcutil"
"github.com/cpacia/bchutil"
"github.com/schancel/cashaddr-converter/address"
)
type AddressFormat = uint8
const (
Legacy AddressFormat = iota
CashAddr
)
const (
MainNetPrefix = "bitcoincash:"
TestNetPrefix = "bchtest:"
RegTestPrefix = "bchreg:"
)
// BCashParser handle
type BCashParser struct {
*btc.BitcoinParser
AddressFormat AddressFormat
}
// NewBCashParser returns new BCashParser instance
func NewBCashParser(params *chaincfg.Params, c *btc.Configuration) (*BCashParser, error) {
var format AddressFormat
switch c.AddressFormat {
case "":
fallthrough
case "cashaddr":
format = CashAddr
case "legacy":
format = Legacy
default:
return nil, fmt.Errorf("Unknown address format: %s", c.AddressFormat)
}
p := &BCashParser{
BitcoinParser: &btc.BitcoinParser{
BaseParser: &bchain.BaseParser{
AddressFactory: func(addr string) (bchain.Address, error) { return newBCashAddress(addr, format) },
BlockAddressesToKeep: c.BlockAddressesToKeep,
},
Params: params,
OutputScriptToAddressesFunc: outputScriptToAddresses,
},
AddressFormat: format,
}
return p, nil
}
// GetChainParams contains network parameters for the main Bitcoin Cash network,
// the regression test Bitcoin Cash network, the test Bitcoin Cash network and
// the simulation test Bitcoin Cash network, in this order
func GetChainParams(chain string) *chaincfg.Params {
var params *chaincfg.Params
switch chain {
case "test":
params = &chaincfg.TestNet3Params
params.Net = bchutil.TestnetMagic
case "regtest":
params = &chaincfg.RegressionNetParams
params.Net = bchutil.Regtestmagic
default:
params = &chaincfg.MainNetParams
params.Net = bchutil.MainnetMagic
}
return params
}
// GetAddrIDFromAddress returns internal address representation of given address
func (p *BCashParser) GetAddrIDFromAddress(address string) ([]byte, error) {
return p.AddressToOutputScript(address)
}
// AddressToOutputScript converts bitcoin address to ScriptPubKey
func (p *BCashParser) AddressToOutputScript(address string) ([]byte, error) {
if isCashAddr(address) {
da, err := bchutil.DecodeAddress(address, p.Params)
if err != nil {
return nil, err
}
script, err := bchutil.PayToAddrScript(da)
if err != nil {
return nil, err
}
return script, nil
} else {
da, err := btcutil.DecodeAddress(address, p.Params)
if err != nil {
return nil, err
}
script, err := txscript.PayToAddrScript(da)
if err != nil {
return nil, err
}
return script, nil
}
}
func isCashAddr(addr string) bool {
n := len(addr)
switch {
case n > len(MainNetPrefix) && addr[0:len(MainNetPrefix)] == MainNetPrefix:
return true
case n > len(TestNetPrefix) && addr[0:len(TestNetPrefix)] == TestNetPrefix:
return true
case n > len(RegTestPrefix) && addr[0:len(RegTestPrefix)] == RegTestPrefix:
return true
}
return false
}
// outputScriptToAddresses converts ScriptPubKey to bitcoin addresses
func outputScriptToAddresses(script []byte, params *chaincfg.Params) ([]string, error) {
a, err := bchutil.ExtractPkScriptAddrs(script, params)
if err != nil {
return nil, err
}
return []string{a.EncodeAddress()}, nil
}
type bcashAddress struct {
addr string
}
func newBCashAddress(addr string, format AddressFormat) (*bcashAddress, error) {
if isCashAddr(addr) && format == CashAddr {
return &bcashAddress{addr: addr}, nil
}
da, err := address.NewFromString(addr)
if err != nil {
return nil, err
}
var ea string
switch format {
case CashAddr:
if a, err := da.CashAddress(); err != nil {
return nil, err
} else {
ea, err = a.Encode()
if err != nil {
return nil, err
}
}
case Legacy:
if a, err := da.Legacy(); err != nil {
return nil, err
} else {
ea, err = a.Encode()
if err != nil {
return nil, err
}
}
default:
return nil, fmt.Errorf("Unknown address format: %d", format)
}
return &bcashAddress{addr: ea}, nil
}
func (a *bcashAddress) String() string {
return a.addr
}
func (a *bcashAddress) AreEqual(addr string) bool {
return a.String() == addr
}
func (a *bcashAddress) InSlice(addrs []string) bool {
ea := a.String()
for _, addr := range addrs {
if ea == addr {
return true
}
}
return false
}