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