Merge branch 'bcash-cashaddr'
commit
1385313908
|
@ -109,6 +109,12 @@
|
|||
revision = "3247c84500bff8d9fb6d579d800f20b3e091582c"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/mr-tron/base58"
|
||||
packages = ["base58"]
|
||||
revision = "c1bdf7c52f59d6685ca597b9955a443ff95eeee6"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/pebbe/zmq4"
|
||||
|
@ -145,6 +151,12 @@
|
|||
revision = "feef513b9575b32f84bafa580aad89b011259019"
|
||||
version = "v1.3.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/schancel/cashaddr-converter"
|
||||
packages = ["address","baseconv","cashaddress","legacy"]
|
||||
revision = "0a38f5822f795dc3727b4caacc298e02938d9eb1"
|
||||
version = "v9"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/syndtr/goleveldb"
|
||||
|
@ -190,6 +202,6 @@
|
|||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "3a5adb167369a6d82fec8664beb93b7e7c8109b31c9f19ecd8ce1142755d621f"
|
||||
inputs-digest = "97b5e11b3aa46e6b54a5c3fd7835c49f324f9821a1c641e5682f60ee6716d8c2"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
|
|
@ -3,7 +3,6 @@ package bchain
|
|||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/juju/errors"
|
||||
|
@ -20,11 +19,6 @@ func (p *BaseParser) AddressToOutputScript(address string) ([]byte, error) {
|
|||
return nil, errors.New("AddressToOutputScript: not implemented")
|
||||
}
|
||||
|
||||
// OutputScriptToAddresses converts ScriptPubKey to addresses - currently not implemented
|
||||
func (p *BaseParser) OutputScriptToAddresses(script []byte) ([]string, error) {
|
||||
return nil, errors.New("OutputScriptToAddresses: not implemented")
|
||||
}
|
||||
|
||||
// ParseBlock parses raw block to our Block struct - currently not implemented
|
||||
func (p *BaseParser) ParseBlock(b []byte) (*Block, error) {
|
||||
return nil, errors.New("ParseBlock: not implemented")
|
||||
|
@ -215,30 +209,16 @@ func (a baseAddress) String() string {
|
|||
return a.addr
|
||||
}
|
||||
|
||||
func (a baseAddress) EncodeAddress(format AddressFormat) (string, error) {
|
||||
if format != DefaultAddress {
|
||||
return "", fmt.Errorf("Unknown address format: %d", format)
|
||||
}
|
||||
return a.addr, nil
|
||||
func (a baseAddress) AreEqual(addr string) bool {
|
||||
return a.String() == addr
|
||||
}
|
||||
|
||||
func (a baseAddress) AreEqual(addr string) (bool, error) {
|
||||
ea, err := a.EncodeAddress(0)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return ea == addr, nil
|
||||
}
|
||||
|
||||
func (a baseAddress) InSlice(addrs []string) (bool, error) {
|
||||
func (a baseAddress) InSlice(addrs []string) bool {
|
||||
ea := a.String()
|
||||
for _, addr := range addrs {
|
||||
eq, err := a.AreEqual(addr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if eq {
|
||||
return true, nil
|
||||
if ea == addr {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -4,32 +4,58 @@ import (
|
|||
"blockbook/bchain"
|
||||
"blockbook/bchain/coins/btc"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/cpacia/bchutil"
|
||||
"github.com/schancel/cashaddr-converter/address"
|
||||
)
|
||||
|
||||
var prefixes = []string{"bitcoincash", "bchtest", "bchreg"}
|
||||
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 {
|
||||
return &BCashParser{
|
||||
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, params) },
|
||||
AddressFactory: func(addr string) (bchain.Address, error) { return newBCashAddress(addr, format) },
|
||||
BlockAddressesToKeep: c.BlockAddressesToKeep,
|
||||
},
|
||||
Params: params,
|
||||
Params: params,
|
||||
OutputScriptToAddresses: outputScriptToAddresses,
|
||||
},
|
||||
AddressFormat: format,
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// GetChainParams contains network parameters for the main Bitcoin Cash network,
|
||||
|
@ -83,128 +109,82 @@ func (p *BCashParser) AddressToOutputScript(address string) ([]byte, error) {
|
|||
}
|
||||
|
||||
func isCashAddr(addr string) bool {
|
||||
slice := strings.Split(addr, ":")
|
||||
if len(slice) != 2 {
|
||||
return false
|
||||
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
|
||||
}
|
||||
for _, prefix := range prefixes {
|
||||
if slice[0] == prefix {
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (p *BCashParser) UnpackTx(buf []byte) (tx *bchain.Tx, height uint32, err error) {
|
||||
tx, height, err = p.BitcoinParser.UnpackTx(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for i, vout := range tx.Vout {
|
||||
if len(vout.ScriptPubKey.Addresses) == 1 {
|
||||
a, err := newBCashAddress(vout.ScriptPubKey.Addresses[0], p.Params)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
tx.Vout[i].Address = a
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type bcashAddress struct {
|
||||
addr btcutil.Address
|
||||
net *chaincfg.Params
|
||||
}
|
||||
|
||||
func newBCashAddress(addr string, net *chaincfg.Params) (*bcashAddress, error) {
|
||||
var (
|
||||
da btcutil.Address
|
||||
err error
|
||||
)
|
||||
if isCashAddr(addr) {
|
||||
// for cashaddr we need to convert it to the legacy form (i.e. to btcutil's Address)
|
||||
// because bchutil doesn't allow later conversions
|
||||
da, err = bchutil.DecodeAddress(addr, net)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch ca := da.(type) {
|
||||
case *bchutil.CashAddressPubKeyHash:
|
||||
da, err = btcutil.NewAddressPubKeyHash(ca.Hash160()[:], net)
|
||||
case *bchutil.CashAddressScriptHash:
|
||||
da, err = btcutil.NewAddressScriptHash(ca.Hash160()[:], net)
|
||||
default:
|
||||
err = fmt.Errorf("Unknown address type: %T", da)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
da, err = btcutil.DecodeAddress(addr, net)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &bcashAddress{addr: da, net: net}, nil
|
||||
}
|
||||
|
||||
func (a *bcashAddress) String() string {
|
||||
return a.addr.String()
|
||||
}
|
||||
|
||||
func (a *bcashAddress) EncodeAddress(format bchain.AddressFormat) (string, error) {
|
||||
switch format {
|
||||
case bchain.DefaultAddress:
|
||||
return a.String(), nil
|
||||
case bchain.BCashAddress:
|
||||
var (
|
||||
ca btcutil.Address
|
||||
err error
|
||||
)
|
||||
switch da := a.addr.(type) {
|
||||
case *btcutil.AddressPubKeyHash:
|
||||
ca, err = bchutil.NewCashAddressPubKeyHash(da.Hash160()[:], a.net)
|
||||
case *btcutil.AddressScriptHash:
|
||||
ca, err = bchutil.NewCashAddressScriptHash(da.Hash160()[:], a.net)
|
||||
default:
|
||||
err = fmt.Errorf("Unknown address type: %T", da)
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return ca.String(), nil
|
||||
|
||||
default:
|
||||
return "", fmt.Errorf("Unknown address format: %d", format)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *bcashAddress) AreEqual(addr string) (bool, error) {
|
||||
var format bchain.AddressFormat
|
||||
if isCashAddr(addr) {
|
||||
format = bchain.BCashAddress
|
||||
} else {
|
||||
format = bchain.DefaultAddress
|
||||
}
|
||||
ea, err := a.EncodeAddress(format)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return ea == addr, nil
|
||||
}
|
||||
|
||||
func (a *bcashAddress) InSlice(addrs []string) (bool, error) {
|
||||
for _, addr := range addrs {
|
||||
eq, err := a.AreEqual(addr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if eq {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
|
|
@ -10,111 +10,90 @@ import (
|
|||
)
|
||||
|
||||
func TestBcashAddressEncodeAddress(t *testing.T) {
|
||||
addr, err := newBCashAddress("13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji", GetChainParams("main"))
|
||||
addr1, err := newBCashAddress("13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji", Legacy)
|
||||
if err != nil {
|
||||
t.Errorf("newBCashAddress() error = %v", err)
|
||||
return
|
||||
}
|
||||
got1, err := addr.EncodeAddress(bchain.DefaultAddress)
|
||||
if err != nil {
|
||||
t.Errorf("EncodeAddress() error = %v", err)
|
||||
return
|
||||
}
|
||||
got1 := addr1.String()
|
||||
if got1 != "13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji" {
|
||||
t.Errorf("EncodeAddress() got1 = %v, want %v", got1, "13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji")
|
||||
t.Errorf("String() got1 = %v, want %v", got1, "13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji")
|
||||
}
|
||||
got2, err := addr.EncodeAddress(bchain.BCashAddress)
|
||||
addr2, err := newBCashAddress("13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji", CashAddr)
|
||||
if err != nil {
|
||||
t.Errorf("EncodeAddress() error = %v", err)
|
||||
t.Errorf("newBCashAddress() error = %v", err)
|
||||
return
|
||||
}
|
||||
got2 := addr2.String()
|
||||
if got2 != "bitcoincash:qqsvjuqqwgyzvz7zz9xcvxent0ul2xjs6y4d9qvsrf" {
|
||||
t.Errorf("EncodeAddress() got2 = %v, want %v", got2, "bitcoincash:qqsvjuqqwgyzvz7zz9xcvxent0ul2xjs6y4d9qvsrf")
|
||||
t.Errorf("String() got2 = %v, want %v", got2, "bitcoincash:qqsvjuqqwgyzvz7zz9xcvxent0ul2xjs6y4d9qvsrf")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcashAddressAreEqual(t *testing.T) {
|
||||
addr, err := newBCashAddress("13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji", GetChainParams("main"))
|
||||
addr1, err := newBCashAddress("13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji", Legacy)
|
||||
if err != nil {
|
||||
t.Errorf("newBCashAddress() error = %v", err)
|
||||
return
|
||||
}
|
||||
got1, err := addr.AreEqual("13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji")
|
||||
addr2, err := newBCashAddress("13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji", CashAddr)
|
||||
if err != nil {
|
||||
t.Errorf("AreEqual() error = %v", err)
|
||||
t.Errorf("newBCashAddress() error = %v", err)
|
||||
return
|
||||
}
|
||||
got1 := addr1.AreEqual("13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji")
|
||||
if got1 != true {
|
||||
t.Errorf("AreEqual() got1 = %v, want %v", got1, true)
|
||||
}
|
||||
got2, err := addr.AreEqual("bitcoincash:qqsvjuqqwgyzvz7zz9xcvxent0ul2xjs6y4d9qvsrf")
|
||||
if err != nil {
|
||||
t.Errorf("AreEqual() error = %v", err)
|
||||
return
|
||||
}
|
||||
got2 := addr2.AreEqual("bitcoincash:qqsvjuqqwgyzvz7zz9xcvxent0ul2xjs6y4d9qvsrf")
|
||||
if got2 != true {
|
||||
t.Errorf("AreEqual() got2 = %v, want %v", got2, true)
|
||||
}
|
||||
got3, err := addr.AreEqual("1HoKgKQh7ZNomWURmS9Tk3z8JM2MWm7S1w")
|
||||
if err != nil {
|
||||
t.Errorf("AreEqual() error = %v", err)
|
||||
return
|
||||
}
|
||||
got3 := addr1.AreEqual("1HoKgKQh7ZNomWURmS9Tk3z8JM2MWm7S1w")
|
||||
if got3 != false {
|
||||
t.Errorf("AreEqual() got3 = %v, want %v", got3, false)
|
||||
}
|
||||
got4, err := addr.AreEqual("bitcoincash:qzuyf0gpqj7q5wfck3nyghhklju7r0k3ksmq6d0vch")
|
||||
if err != nil {
|
||||
t.Errorf("AreEqual() error = %v", err)
|
||||
return
|
||||
}
|
||||
got4 := addr2.AreEqual("bitcoincash:qzuyf0gpqj7q5wfck3nyghhklju7r0k3ksmq6d0vch")
|
||||
if got4 != false {
|
||||
t.Errorf("AreEqual() got4 = %v, want %v", got4, false)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcashAddressInSlice(t *testing.T) {
|
||||
addr, err := newBCashAddress("13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji", GetChainParams("main"))
|
||||
addr1, err := newBCashAddress("13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji", Legacy)
|
||||
if err != nil {
|
||||
t.Errorf("newBCashAddress() error = %v", err)
|
||||
return
|
||||
}
|
||||
got1, err := addr.InSlice([]string{"13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji", "bitcoincash:qzuyf0gpqj7q5wfck3nyghhklju7r0k3ksmq6d0vch"})
|
||||
addr2, err := newBCashAddress("13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji", CashAddr)
|
||||
if err != nil {
|
||||
t.Errorf("InSlice() error = %v", err)
|
||||
t.Errorf("newBCashAddress() error = %v", err)
|
||||
return
|
||||
}
|
||||
got1 := addr1.InSlice([]string{"13zMwGC5bxRn9ckJ1mgxf7UR8qbbNe2iji", "1HoKgKQh7ZNomWURmS9Tk3z8JM2MWm7S1w"})
|
||||
if got1 != true {
|
||||
t.Errorf("InSlice() got1 = %v, want %v", got1, true)
|
||||
}
|
||||
got2, err := addr.InSlice([]string{"1HoKgKQh7ZNomWURmS9Tk3z8JM2MWm7S1w", "bitcoincash:qqsvjuqqwgyzvz7zz9xcvxent0ul2xjs6y4d9qvsrf"})
|
||||
if err != nil {
|
||||
t.Errorf("InSlice() error = %v", err)
|
||||
return
|
||||
}
|
||||
got2 := addr2.InSlice([]string{"bitcoincash:qzuyf0gpqj7q5wfck3nyghhklju7r0k3ksmq6d0vch", "bitcoincash:qqsvjuqqwgyzvz7zz9xcvxent0ul2xjs6y4d9qvsrf"})
|
||||
if got2 != true {
|
||||
t.Errorf("InSlice() got2 = %v, want %v", got2, true)
|
||||
}
|
||||
got3, err := addr.InSlice([]string{"1HoKgKQh7ZNomWURmS9Tk3z8JM2MWm7S1w", "1E6Np6dUPYpBSdLMLuwBF8sRQ3cngdaRRY"})
|
||||
if err != nil {
|
||||
t.Errorf("InSlice() error = %v", err)
|
||||
return
|
||||
}
|
||||
got3 := addr1.InSlice([]string{"1HoKgKQh7ZNomWURmS9Tk3z8JM2MWm7S1w", "1E6Np6dUPYpBSdLMLuwBF8sRQ3cngdaRRY"})
|
||||
if got3 != false {
|
||||
t.Errorf("InSlice() got3 = %v, want %v", got3, false)
|
||||
}
|
||||
got4, err := addr.InSlice([]string{"bitcoincash:qzuyf0gpqj7q5wfck3nyghhklju7r0k3ksmq6d0vch", "bitcoincash:qz8emmpenqgeg7et8xsz8prvhy6cqcalyyjcamt7e9"})
|
||||
if err != nil {
|
||||
t.Errorf("InSlice() error = %v", err)
|
||||
return
|
||||
}
|
||||
got4 := addr2.InSlice([]string{"bitcoincash:qzuyf0gpqj7q5wfck3nyghhklju7r0k3ksmq6d0vch", "bitcoincash:qz8emmpenqgeg7et8xsz8prvhy6cqcalyyjcamt7e9"})
|
||||
if got4 != false {
|
||||
t.Errorf("InSlice() got4 = %v, want %v", got4, false)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddressToOutputScript(t *testing.T) {
|
||||
parser := NewBCashParser(GetChainParams("test"), &btc.Configuration{})
|
||||
parser, err := NewBCashParser(GetChainParams("test"), &btc.Configuration{AddressFormat: "legacy"})
|
||||
if err != nil {
|
||||
t.Errorf("NewBCashParser() error = %v", err)
|
||||
return
|
||||
}
|
||||
want, err := hex.DecodeString("76a9144fa927fd3bcf57d4e3c582c3d2eb2bd3df8df47c88ac")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -149,12 +128,12 @@ func init() {
|
|||
addr1, addr2, addr3 bchain.Address
|
||||
err error
|
||||
)
|
||||
addr1, err = newBCashAddress("3AZKvpKhSh1o8t1QrX3UeXG9d2BhCRnbcK", GetChainParams("main"))
|
||||
addr1, err = newBCashAddress("3AZKvpKhSh1o8t1QrX3UeXG9d2BhCRnbcK", Legacy)
|
||||
if err == nil {
|
||||
addr2, err = newBCashAddress("2NByHN6A8QYkBATzxf4pRGbCSHD5CEN2TRu", GetChainParams("test"))
|
||||
addr2, err = newBCashAddress("2NByHN6A8QYkBATzxf4pRGbCSHD5CEN2TRu", Legacy)
|
||||
}
|
||||
if err == nil {
|
||||
addr3, err = newBCashAddress("2MvZguYaGjM7JihBgNqgLF2Ca2Enb76Hj9D", GetChainParams("test"))
|
||||
addr3, err = newBCashAddress("2MvZguYaGjM7JihBgNqgLF2Ca2Enb76Hj9D", Legacy)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -182,7 +161,7 @@ func init() {
|
|||
ScriptPubKey: bchain.ScriptPubKey{
|
||||
Hex: "a9146144d57c8aff48492c9dfb914e120b20bad72d6f87",
|
||||
Addresses: []string{
|
||||
"3AZKvpKhSh1o8t1QrX3UeXG9d2BhCRnbcK",
|
||||
"bitcoincash:pps5f4tu3tl5sjfvnhaeznsjpvst44eddugfcnqpy9",
|
||||
},
|
||||
},
|
||||
Address: addr1,
|
||||
|
@ -212,7 +191,7 @@ func init() {
|
|||
ScriptPubKey: bchain.ScriptPubKey{
|
||||
Hex: "a914cd668d781ece600efa4b2404dc91fd26b8b8aed887",
|
||||
Addresses: []string{
|
||||
"2NByHN6A8QYkBATzxf4pRGbCSHD5CEN2TRu",
|
||||
"bchtest:prxkdrtcrm8xqrh6fvjqfhy3l5nt3w9wmq9fmsvkmz",
|
||||
},
|
||||
},
|
||||
Address: addr2,
|
||||
|
@ -223,7 +202,7 @@ func init() {
|
|||
ScriptPubKey: bchain.ScriptPubKey{
|
||||
Hex: "a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a87",
|
||||
Addresses: []string{
|
||||
"2MvZguYaGjM7JihBgNqgLF2Ca2Enb76Hj9D",
|
||||
"bchtest:pqjxv4dah42v0erh6r4zxa0gdcxm9w8cpg0qw8tqf6",
|
||||
},
|
||||
},
|
||||
Address: addr3,
|
||||
|
@ -233,6 +212,17 @@ func init() {
|
|||
}
|
||||
|
||||
func Test_UnpackTx(t *testing.T) {
|
||||
parser1, err := NewBCashParser(GetChainParams("main"), &btc.Configuration{AddressFormat: "legacy"})
|
||||
if err != nil {
|
||||
t.Errorf("NewBCashParser() error = %v", err)
|
||||
return
|
||||
}
|
||||
parser2, err := NewBCashParser(GetChainParams("test"), &btc.Configuration{AddressFormat: "legacy"})
|
||||
if err != nil {
|
||||
t.Errorf("NewBCashParser() error = %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
type args struct {
|
||||
packedTx string
|
||||
parser *BCashParser
|
||||
|
@ -248,7 +238,7 @@ func Test_UnpackTx(t *testing.T) {
|
|||
name: "btc-1",
|
||||
args: args{
|
||||
packedTx: testTxPacked1,
|
||||
parser: NewBCashParser(GetChainParams("main"), &btc.Configuration{}),
|
||||
parser: parser1,
|
||||
},
|
||||
want: &testTx1,
|
||||
want1: 123456,
|
||||
|
@ -258,7 +248,7 @@ func Test_UnpackTx(t *testing.T) {
|
|||
name: "testnet-1",
|
||||
args: args{
|
||||
packedTx: testTxPacked2,
|
||||
parser: NewBCashParser(GetChainParams("test"), &btc.Configuration{}),
|
||||
parser: parser2,
|
||||
},
|
||||
want: &testTx2,
|
||||
want1: 510234,
|
||||
|
|
|
@ -32,7 +32,6 @@ func NewBCashRPC(config json.RawMessage, pushHandler func(bchain.NotificationTyp
|
|||
|
||||
// Initialize initializes BCashRPC instance.
|
||||
func (b *BCashRPC) Initialize() error {
|
||||
|
||||
chainName, err := b.GetChainInfoAndInitializeMempool(b)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -41,7 +40,11 @@ func (b *BCashRPC) Initialize() error {
|
|||
params := GetChainParams(chainName)
|
||||
|
||||
// always create parser
|
||||
b.Parser = NewBCashParser(params, b.ChainConfig)
|
||||
b.Parser, err = NewBCashParser(params, b.ChainConfig)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// parameters for getInfo request
|
||||
if params.Net == bchutil.MainnetMagic {
|
||||
|
|
|
@ -14,10 +14,14 @@ import (
|
|||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
// OutputScriptToAddressesFunc converts ScriptPubKey to bitcoin addresses
|
||||
type OutputScriptToAddressesFunc func(script []byte, params *chaincfg.Params) ([]string, error)
|
||||
|
||||
// BitcoinParser handle
|
||||
type BitcoinParser struct {
|
||||
*bchain.BaseParser
|
||||
Params *chaincfg.Params
|
||||
Params *chaincfg.Params
|
||||
OutputScriptToAddresses OutputScriptToAddressesFunc
|
||||
}
|
||||
|
||||
// NewBitcoinParser returns new BitcoinParser instance
|
||||
|
@ -28,6 +32,7 @@ func NewBitcoinParser(params *chaincfg.Params, c *Configuration) *BitcoinParser
|
|||
BlockAddressesToKeep: c.BlockAddressesToKeep,
|
||||
},
|
||||
params,
|
||||
outputScriptToAddresses,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,9 +72,9 @@ func (p *BitcoinParser) AddressToOutputScript(address string) ([]byte, error) {
|
|||
return script, nil
|
||||
}
|
||||
|
||||
// OutputScriptToAddresses converts ScriptPubKey to bitcoin addresses
|
||||
func (p *BitcoinParser) OutputScriptToAddresses(script []byte) ([]string, error) {
|
||||
_, addresses, _, err := txscript.ExtractPkScriptAddrs(script, p.Params)
|
||||
// outputScriptToAddresses converts ScriptPubKey to bitcoin addresses
|
||||
func outputScriptToAddresses(script []byte, params *chaincfg.Params) ([]string, error) {
|
||||
_, addresses, _, err := txscript.ExtractPkScriptAddrs(script, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -105,7 +110,7 @@ func (p *BitcoinParser) txFromMsgTx(t *wire.MsgTx, parseAddresses bool) bchain.T
|
|||
for i, out := range t.TxOut {
|
||||
addrs := []string{}
|
||||
if parseAddresses {
|
||||
addrs, _ = p.OutputScriptToAddresses(out.PkScript)
|
||||
addrs, _ = p.OutputScriptToAddresses(out.PkScript, p.Params)
|
||||
}
|
||||
s := bchain.ScriptPubKey{
|
||||
Hex: hex.EncodeToString(out.PkScript),
|
||||
|
|
|
@ -94,17 +94,16 @@ func TestOutputScriptToAddresses(t *testing.T) {
|
|||
wantErr: false,
|
||||
},
|
||||
}
|
||||
parser := NewBitcoinParser(GetChainParams("main"), &Configuration{})
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
b, _ := hex.DecodeString(tt.args.script)
|
||||
got, err := parser.OutputScriptToAddresses(b)
|
||||
got, err := outputScriptToAddresses(b, GetChainParams("main"))
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("OutputScriptToAddresses() error = %v, wantErr %v", err, tt.wantErr)
|
||||
t.Errorf("outputScriptToAddresses() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("OutputScriptToAddresses() = %v, want %v", got, tt.want)
|
||||
t.Errorf("outputScriptToAddresses() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ type Configuration struct {
|
|||
BlockAddressesToKeep int `json:"blockAddressesToKeep"`
|
||||
MempoolWorkers int `json:"mempoolWorkers"`
|
||||
MempoolSubWorkers int `json:"mempoolSubWorkers"`
|
||||
AddressFormat string `json:"addressFormat"`
|
||||
}
|
||||
|
||||
// NewBitcoinRPC returns new BitcoinRPC instance.
|
||||
|
|
|
@ -41,18 +41,10 @@ type ScriptPubKey struct {
|
|||
Addresses []string `json:"addresses,omitempty"`
|
||||
}
|
||||
|
||||
type AddressFormat = uint8
|
||||
|
||||
const (
|
||||
DefaultAddress AddressFormat = iota
|
||||
BCashAddress
|
||||
)
|
||||
|
||||
type Address interface {
|
||||
String() string
|
||||
EncodeAddress(format AddressFormat) (string, error)
|
||||
AreEqual(addr string) (bool, error)
|
||||
InSlice(addrs []string) (bool, error)
|
||||
AreEqual(addr string) bool
|
||||
InSlice(addrs []string) bool
|
||||
}
|
||||
|
||||
type Vout struct {
|
||||
|
@ -164,7 +156,6 @@ type BlockChainParser interface {
|
|||
GetAddrIDFromAddress(address string) ([]byte, error)
|
||||
// address to output script conversions
|
||||
AddressToOutputScript(address string) ([]byte, error)
|
||||
OutputScriptToAddresses(script []byte) ([]string, error)
|
||||
// transactions
|
||||
PackedTxidLen() int
|
||||
PackTxid(txid string) ([]byte, error)
|
||||
|
|
|
@ -8,5 +8,6 @@
|
|||
"subversion": "/Bitcoin ABC:0.17.1/",
|
||||
"mempoolWorkers": 8,
|
||||
"mempoolSubWorkers": 2,
|
||||
"blockAddressesToKeep": 300
|
||||
"blockAddressesToKeep": 300,
|
||||
"addressFormat": "legacy"
|
||||
}
|
||||
|
|
|
@ -8,5 +8,6 @@
|
|||
"subversion": "/Bitcoin ABC:0.17.1/",
|
||||
"mempoolWorkers": 8,
|
||||
"mempoolSubWorkers": 2,
|
||||
"blockAddressesToKeep": 300
|
||||
"blockAddressesToKeep": 300,
|
||||
"addressFormat": "legacy"
|
||||
}
|
||||
|
|
|
@ -208,16 +208,11 @@ func (s *SocketIoServer) apiBlockIndex(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
type addrOpts struct {
|
||||
Start int `json:"start"`
|
||||
End int `json:"end"`
|
||||
QueryMempoolOnly bool `json:"queryMempoolOnly"`
|
||||
From int `json:"from"`
|
||||
To int `json:"to"`
|
||||
AddressFormat uint8 `json:"addressFormat"`
|
||||
}
|
||||
|
||||
type txOpts struct {
|
||||
AddressFormat uint8 `json:"addressFormat"`
|
||||
Start int `json:"start"`
|
||||
End int `json:"end"`
|
||||
QueryMempoolOnly bool `json:"queryMempoolOnly"`
|
||||
From int `json:"from"`
|
||||
To int `json:"to"`
|
||||
}
|
||||
|
||||
var onMessageHandlers = map[string]func(*SocketIoServer, json.RawMessage) (interface{}, error){
|
||||
|
@ -260,9 +255,9 @@ var onMessageHandlers = map[string]func(*SocketIoServer, json.RawMessage) (inter
|
|||
return s.getInfo()
|
||||
},
|
||||
"getDetailedTransaction": func(s *SocketIoServer, params json.RawMessage) (rv interface{}, err error) {
|
||||
txid, opts, err := unmarshalGetDetailedTransaction(params)
|
||||
txid, err := unmarshalGetDetailedTransaction(params)
|
||||
if err == nil {
|
||||
rv, err = s.getDetailedTransaction(txid, opts)
|
||||
rv, err = s.getDetailedTransaction(txid)
|
||||
}
|
||||
return
|
||||
},
|
||||
|
@ -483,16 +478,9 @@ func (s *SocketIoServer) getAddressHistory(addr []string, opts *addrOpts) (res r
|
|||
Script: &aoh,
|
||||
}
|
||||
if vout.Address != nil {
|
||||
a, err := vout.Address.EncodeAddress(opts.AddressFormat)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
a := vout.Address.String()
|
||||
ao.Address = &a
|
||||
found, err := vout.Address.InSlice(addr)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
if found {
|
||||
if vout.Address.InSlice(addr) {
|
||||
hi, ok := ads[a]
|
||||
if ok {
|
||||
hi.OutputIndexes = append(hi.OutputIndexes, int(vout.N))
|
||||
|
@ -693,13 +681,13 @@ func unmarshalStringParameter(params []byte) (s string, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func unmarshalGetDetailedTransaction(params []byte) (txid string, opts txOpts, err error) {
|
||||
func unmarshalGetDetailedTransaction(params []byte) (txid string, err error) {
|
||||
var p []json.RawMessage
|
||||
err = json.Unmarshal(params, &p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(p) < 1 || len(p) > 2 {
|
||||
if len(p) != 1 {
|
||||
err = errors.New("incorrect number of parameters")
|
||||
return
|
||||
}
|
||||
|
@ -707,9 +695,6 @@ func unmarshalGetDetailedTransaction(params []byte) (txid string, opts txOpts, e
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(p) > 1 {
|
||||
err = json.Unmarshal(p[1], &opts)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -717,7 +702,7 @@ type resultGetDetailedTransaction struct {
|
|||
Result resTx `json:"result"`
|
||||
}
|
||||
|
||||
func (s *SocketIoServer) getDetailedTransaction(txid string, opts txOpts) (res resultGetDetailedTransaction, err error) {
|
||||
func (s *SocketIoServer) getDetailedTransaction(txid string) (res resultGetDetailedTransaction, err error) {
|
||||
bestheight, _, err := s.db.GetBestBlock()
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -743,10 +728,7 @@ func (s *SocketIoServer) getDetailedTransaction(txid string, opts txOpts) (res r
|
|||
if len(otx.Vout) > int(vin.Vout) {
|
||||
vout := otx.Vout[vin.Vout]
|
||||
if vout.Address != nil {
|
||||
a, err := vout.Address.EncodeAddress(opts.AddressFormat)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
a := vout.Address.String()
|
||||
ai.Address = &a
|
||||
}
|
||||
ai.Satoshis = int64(vout.Value*1E8 + 0.5)
|
||||
|
@ -761,10 +743,7 @@ func (s *SocketIoServer) getDetailedTransaction(txid string, opts txOpts) (res r
|
|||
Script: &aos,
|
||||
}
|
||||
if vout.Address != nil {
|
||||
a, err := vout.Address.EncodeAddress(opts.AddressFormat)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
a := vout.Address.String()
|
||||
ao.Address = &a
|
||||
}
|
||||
ho = append(ho, ao)
|
||||
|
|
|
@ -56,15 +56,14 @@
|
|||
var addresses = document.getElementById('getAddressHistoryAddresses').value.split(",");
|
||||
addresses = addresses.map(s => s.trim());
|
||||
var mempool = document.getElementById("getAddressHistoryMempool").checked;
|
||||
var format = document.getElementById("getAddressHistoryFormat").value;
|
||||
lookupAddressHistories(addresses, 0, 5, mempool, 20000000, 0, format, function (result) {
|
||||
lookupAddressHistories(addresses, 0, 5, mempool, 20000000, 0, function (result) {
|
||||
console.log('getAddressHistory sent successfully');
|
||||
console.log(result);
|
||||
document.getElementById('getAddressHistoryResult').innerText = JSON.stringify(result).replace(/,/g, ", ");
|
||||
});
|
||||
}
|
||||
|
||||
function lookupAddressHistories(addresses, from, to, mempool, start, end, format, f) {
|
||||
function lookupAddressHistories(addresses, from, to, mempool, start, end, f) {
|
||||
const method = 'getAddressHistory';
|
||||
const opts = mempool ? {
|
||||
start, // needed for older bitcores (so we don't load all history if bitcore-node < 3.1.3)
|
||||
|
@ -81,7 +80,6 @@
|
|||
...opts,
|
||||
from,
|
||||
to,
|
||||
addressFormat: parseInt(format),
|
||||
},
|
||||
];
|
||||
return socket.send({ method, params }, f);
|
||||
|
@ -172,28 +170,18 @@
|
|||
|
||||
function getDetailedTransaction() {
|
||||
var hash = document.getElementById('getDetailedTransactionHash').value.trim();
|
||||
var format = document.getElementById("getDetailedTransactionFormat").value;
|
||||
lookupDetailedTransaction(hash, format, function (result) {
|
||||
lookupDetailedTransaction(hash, function (result) {
|
||||
console.log('getDetailedTransaction sent successfully');
|
||||
console.log(result);
|
||||
document.getElementById('getDetailedTransactionResult').innerText = JSON.stringify(result).replace(/,/g, ", ");
|
||||
});
|
||||
}
|
||||
|
||||
function lookupDetailedTransaction(hash, format, f) {
|
||||
function lookupDetailedTransaction(hash, f) {
|
||||
const method = 'getDetailedTransaction';
|
||||
const af = parseInt(format)
|
||||
var params = [
|
||||
hash,
|
||||
];
|
||||
if (af !== 0) {
|
||||
params = [
|
||||
hash,
|
||||
{
|
||||
addressFormat: af,
|
||||
},
|
||||
];
|
||||
}
|
||||
return socket.send({ method, params }, f);
|
||||
}
|
||||
|
||||
|
@ -302,14 +290,6 @@
|
|||
<input type="checkbox" id="getAddressHistoryMempool">
|
||||
<label>only mempool</label>
|
||||
</div>
|
||||
<div class="col-9"></div>
|
||||
<div class="col form-inline">
|
||||
<label>address format</label>
|
||||
<select id="getAddressHistoryFormat" value="0">
|
||||
<option value="0">default</option>
|
||||
<option value="1">bitcoincash</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col" id="getAddressHistoryResult">
|
||||
|
@ -373,14 +353,6 @@
|
|||
<input type="text" class="form-control" id="getDetailedTransactionHash" value="474e6795760ebe81cb4023dc227e5a0efe340e1771c89a0035276361ed733de7">
|
||||
</div>
|
||||
<div class="col"></div>
|
||||
<div class="col-9"></div>
|
||||
<div class="col form-inline">
|
||||
<label>address format</label>
|
||||
<select id="getDetailedTransactionFormat" value="0">
|
||||
<option value="0">default</option>
|
||||
<option value="1">bitcoincash</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col" id="getDetailedTransactionResult">
|
||||
|
@ -443,4 +415,4 @@
|
|||
document.getElementById('serverAddress').value = window.location.protocol.replace("http", "ws") + "//" + window.location.host;
|
||||
</script>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
|
Loading…
Reference in New Issue