Refactor of address handling, renamed addrID to addr descriptor addrDesc

pull/56/head
Martin Boehm 2018-08-29 00:25:26 +02:00
parent b56b6c7a9b
commit be19523065
26 changed files with 393 additions and 581 deletions

View File

@ -24,23 +24,23 @@ type ScriptSig struct {
}
type Vin struct {
Txid string `json:"txid"`
Vout uint32 `json:"vout"`
Sequence int64 `json:"sequence,omitempty"`
N int `json:"n"`
ScriptSig ScriptSig `json:"scriptSig"`
Addr string `json:"addr"`
AddrLink bool `json:"-"`
Value string `json:"value"`
ValueSat big.Int `json:"-"`
Txid string `json:"txid"`
Vout uint32 `json:"vout"`
Sequence int64 `json:"sequence,omitempty"`
N int `json:"n"`
ScriptSig ScriptSig `json:"scriptSig"`
Addresses []string `json:"addresses"`
Searchable bool `json:"-"`
Value string `json:"value"`
ValueSat big.Int `json:"-"`
}
type ScriptPubKey struct {
Hex string `json:"hex"`
Asm string `json:"asm,omitempty"`
Addresses []string `json:"addresses"`
AddrsLink []bool `json:"-"`
Type string `json:"type,omitempty"`
Hex string `json:"hex"`
Asm string `json:"asm,omitempty"`
Addresses []string `json:"addresses"`
Searchable bool `json:"-"`
Type string `json:"type,omitempty"`
}
type Vout struct {
Value string `json:"value"`

View File

@ -33,6 +33,14 @@ func NewWorker(db *db.RocksDB, chain bchain.BlockChain, txCache *db.TxCache, is
return w, nil
}
func (w *Worker) getAddressesFromVout(vout *bchain.Vout) ([]string, bool, error) {
addrDesc, err := w.chainParser.GetAddrDescFromVout(vout)
if err != nil {
return nil, false, err
}
return w.chainParser.GetAddressesFromAddrDesc(addrDesc)
}
// GetTransaction reads transaction data from txid
func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTxs bool) (*Tx, error) {
bchainTx, height, err := w.txCache.GetTransaction(txid, bestheight)
@ -75,9 +83,9 @@ func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTxs bool
if len(otx.Vout) > int(vin.Vout) {
vout := &otx.Vout[vin.Vout]
vin.ValueSat = vout.ValueSat
if vout.Address != nil {
a := vout.Address.String()
vin.Addr = a
vin.Addresses, vin.Searchable, err = w.getAddressesFromVout(vout)
if err != nil {
glog.Errorf("getAddressesFromVout error %v, vout %+v", err, vout)
}
}
} else {
@ -85,9 +93,9 @@ func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTxs bool
output := &ta.Outputs[vin.Vout]
vin.ValueSat = output.ValueSat
vin.Value = w.chainParser.AmountToDecimalString(&vin.ValueSat)
a, _ := output.Addresses(w.chainParser)
if len(a) > 0 {
vin.Addr = a[0]
vin.Addresses, vin.Searchable, err = output.Addresses(w.chainParser)
if err != nil {
glog.Errorf("output.Addresses error %v, tx %v, output %v", err, bchainVin.Txid, i)
}
}
}
@ -104,6 +112,7 @@ func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTxs bool
vout.Value = w.chainParser.AmountToDecimalString(&bchainVout.ValueSat)
valOutSat.Add(&valOutSat, &bchainVout.ValueSat)
vout.ScriptPubKey.Hex = bchainVout.ScriptPubKey.Hex
vout.ScriptPubKey.Addresses = bchainVout.ScriptPubKey.Addresses
if spendingTxs {
// TODO
@ -156,11 +165,11 @@ func (w *Worker) getAddressTxids(address string, mempool bool) ([]string, error)
return txids, nil
}
func (t *Tx) getAddrVoutValue(addrID string) *big.Int {
func (t *Tx) getAddrVoutValue(addr string) *big.Int {
var val big.Int
for _, vout := range t.Vout {
for _, a := range vout.ScriptPubKey.Addresses {
if a == addrID {
if a == addr {
val.Add(&val, &vout.ValueSat)
}
}
@ -168,11 +177,13 @@ func (t *Tx) getAddrVoutValue(addrID string) *big.Int {
return &val
}
func (t *Tx) getAddrVinValue(addrID string) *big.Int {
func (t *Tx) getAddrVinValue(addr string) *big.Int {
var val big.Int
for _, vin := range t.Vin {
if vin.Addr == addrID {
val.Add(&val, &vin.ValueSat)
for _, a := range vin.Addresses {
if a == addr {
val.Add(&val, &vin.ValueSat)
}
}
}
return &val
@ -195,6 +206,7 @@ func UniqueTxidsInReverse(txids []string) []string {
}
func (w *Worker) txFromTxAddress(txid string, ta *db.TxAddresses, bi *db.BlockInfo, bestheight uint32) *Tx {
var err error
var valInSat, valOutSat, feesSat big.Int
vins := make([]Vin, len(ta.Inputs))
for i := range ta.Inputs {
@ -204,9 +216,9 @@ func (w *Worker) txFromTxAddress(txid string, ta *db.TxAddresses, bi *db.BlockIn
vin.ValueSat = tai.ValueSat
vin.Value = w.chainParser.AmountToDecimalString(&vin.ValueSat)
valInSat.Add(&valInSat, &vin.ValueSat)
a, err := tai.Addresses(w.chainParser)
if err == nil && len(a) == 1 {
vin.Addr = a[0]
vin.Addresses, vin.Searchable, err = tai.Addresses(w.chainParser)
if err != nil {
glog.Errorf("tai.Addresses error %v, tx %v, input %v", err, txid, i)
}
}
vouts := make([]Vout, len(ta.Outputs))
@ -217,9 +229,9 @@ func (w *Worker) txFromTxAddress(txid string, ta *db.TxAddresses, bi *db.BlockIn
vout.ValueSat = tao.ValueSat
vout.Value = w.chainParser.AmountToDecimalString(&vout.ValueSat)
valOutSat.Add(&valOutSat, &vout.ValueSat)
a, err := tao.Addresses(w.chainParser)
if err == nil {
vout.ScriptPubKey.Addresses = a
vout.ScriptPubKey.Addresses, vout.ScriptPubKey.Searchable, err = tao.Addresses(w.chainParser)
if err != nil {
glog.Errorf("tai.Addresses error %v, tx %v, output %v", err, txid, i)
}
}
// for coinbase transactions valIn is 0
@ -343,74 +355,3 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, onlyTxids b
glog.Info(address, " finished in ", time.Since(start))
return r, nil
}
// GetAddress computes address value and gets transactions for given address
func (w *Worker) GetAddressOld(addrID string, page int, txsOnPage int) (*Address, error) {
glog.Info(addrID, " start")
txc, err := w.getAddressTxids(addrID, false)
txc = UniqueTxidsInReverse(txc)
if err != nil {
return nil, err
}
txm, err := w.getAddressTxids(addrID, true)
if err != nil {
return nil, err
}
txm = UniqueTxidsInReverse(txm)
bestheight, _, err := w.db.GetBestBlock()
if err != nil {
return nil, err
}
lc := len(txc)
if lc > txsOnPage {
lc = txsOnPage
}
txs := make([]*Tx, len(txm)+lc)
txi := 0
var uBalSat, balSat, totRecvSat, totSentSat big.Int
for _, tx := range txm {
tx, err := w.GetTransaction(tx, bestheight, false)
// mempool transaction may fail
if err != nil {
glog.Error("GetTransaction ", tx, ": ", err)
} else {
uBalSat.Sub(tx.getAddrVoutValue(addrID), tx.getAddrVinValue(addrID))
txs[txi] = tx
txi++
}
}
if page < 0 {
page = 0
}
from := page * txsOnPage
if from > len(txc) {
from = 0
}
to := from + txsOnPage
for i, tx := range txc {
tx, err := w.GetTransaction(tx, bestheight, false)
if err != nil {
return nil, err
} else {
totRecvSat.Add(&totRecvSat, tx.getAddrVoutValue(addrID))
totSentSat.Add(&totSentSat, tx.getAddrVinValue(addrID))
if i >= from && i < to {
txs[txi] = tx
txi++
}
}
}
balSat.Sub(&totRecvSat, &totSentSat)
r := &Address{
AddrStr: addrID,
Balance: w.chainParser.AmountToDecimalString(&balSat),
TotalReceived: w.chainParser.AmountToDecimalString(&totRecvSat),
TotalSent: w.chainParser.AmountToDecimalString(&totSentSat),
Transactions: txs[:txi],
TxApperances: len(txc),
UnconfirmedBalance: w.chainParser.AmountToDecimalString(&uBalSat),
UnconfirmedTxApperances: len(txm),
}
glog.Info(addrID, " finished")
return r, nil
}

View File

@ -10,25 +10,12 @@ import (
"github.com/juju/errors"
)
type AddressFactoryFunc func(string) (Address, error)
// BaseParser implements data parsing/handling functionality base for all other parsers
type BaseParser struct {
AddressFactory AddressFactoryFunc
BlockAddressesToKeep int
AmountDecimalPoint int
}
// AddressToOutputScript converts address to ScriptPubKey - currently not implemented
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")
@ -100,13 +87,6 @@ func (p *BaseParser) ParseTxFromJson(msg json.RawMessage) (*Tx, error) {
return nil, err
}
vout.JsonValue = ""
if len(vout.ScriptPubKey.Addresses) == 1 {
a, err := p.AddressFactory(vout.ScriptPubKey.Addresses[0])
if err != nil {
return nil, err
}
vout.Address = a
}
}
return &tx, nil
@ -241,13 +221,6 @@ func (p *BaseParser) UnpackTx(buf []byte) (*Tx, uint32, error) {
},
ValueSat: vs,
}
if len(pto.Addresses) == 1 {
a, err := p.AddressFactory(pto.Addresses[0])
if err != nil {
return nil, 0, err
}
vout[i].Address = a
}
}
tx := Tx{
Blocktime: int64(pt.Blocktime),
@ -260,29 +233,3 @@ func (p *BaseParser) UnpackTx(buf []byte) (*Tx, uint32, error) {
}
return &tx, pt.Height, nil
}
type baseAddress struct {
addr string
}
func NewBaseAddress(addr string) (Address, error) {
return &baseAddress{addr: addr}, nil
}
func (a baseAddress) String() string {
return a.addr
}
func (a baseAddress) AreEqual(addr string) bool {
return a.String() == addr
}
func (a baseAddress) InSlice(addrs []string) bool {
ea := a.String()
for _, addr := range addrs {
if ea == addr {
return true
}
}
return false
}

View File

@ -47,15 +47,14 @@ func NewBCashParser(params *chaincfg.Params, c *btc.Configuration) (*BCashParser
p := &BCashParser{
BitcoinParser: &btc.BitcoinParser{
BaseParser: &bchain.BaseParser{
AddressFactory: func(addr string) (bchain.Address, error) { return newBCashAddress(addr, format) },
BlockAddressesToKeep: c.BlockAddressesToKeep,
AmountDecimalPoint: 8,
},
Params: params,
OutputScriptToAddressesFunc: outputScriptToAddresses,
},
AddressFormat: format,
}
p.OutputScriptToAddressesFunc = p.outputScriptToAddresses
return p, nil
}
@ -79,13 +78,13 @@ func GetChainParams(chain string) *chaincfg.Params {
return params
}
// GetAddrIDFromAddress returns internal address representation of given address
func (p *BCashParser) GetAddrIDFromAddress(address string) ([]byte, error) {
return p.AddressToOutputScript(address)
// GetAddrDescFromAddress returns internal address representation of given address
func (p *BCashParser) GetAddrDescFromAddress(address string) ([]byte, error) {
return p.addressToOutputScript(address)
}
// AddressToOutputScript converts bitcoin address to ScriptPubKey
func (p *BCashParser) AddressToOutputScript(address string) ([]byte, error) {
// 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 {
@ -124,12 +123,13 @@ func isCashAddr(addr string) bool {
}
// outputScriptToAddresses converts ScriptPubKey to bitcoin addresses
func outputScriptToAddresses(script []byte, params *chaincfg.Params) ([]string, error) {
a, err := bchutil.ExtractPkScriptAddrs(script, params)
func (p *BCashParser) outputScriptToAddresses(script []byte) ([]string, bool, error) {
a, err := bchutil.ExtractPkScriptAddrs(script, p.Params)
if err != nil {
return nil, err
return nil, false, err
}
return []string{a.EncodeAddress()}, nil
addr := a.EncodeAddress()
return []string{addr}, len(addr) > 0, nil
}
type bcashAddress struct {

View File

@ -91,7 +91,7 @@ func TestBcashAddressInSlice(t *testing.T) {
}
}
func TestAddressToOutputScript(t *testing.T) {
func Test_GetAddrDescFromAddress(t *testing.T) {
parser, err := NewBCashParser(GetChainParams("test"), &btc.Configuration{AddressFormat: "legacy"})
if err != nil {
t.Errorf("NewBCashParser() error = %v", err)
@ -101,21 +101,21 @@ func TestAddressToOutputScript(t *testing.T) {
if err != nil {
panic(err)
}
got1, err := parser.AddressToOutputScript("mnnAKPTSrWjgoi3uEYaQkHA1QEC5btFeBr")
got1, err := parser.GetAddrDescFromAddress("mnnAKPTSrWjgoi3uEYaQkHA1QEC5btFeBr")
if err != nil {
t.Errorf("AddressToOutputScript() error = %v", err)
t.Errorf("GetAddrDescFromAddress() error = %v", err)
return
}
if !bytes.Equal(got1, want) {
t.Errorf("AddressToOutputScript() got1 = %v, want %v", got1, want)
t.Errorf("GetAddrDescFromAddress() got1 = %v, want %v", got1, want)
}
got2, err := parser.AddressToOutputScript("bchtest:qp86jfla8084048rckpv85ht90falr050s03ejaesm")
got2, err := parser.GetAddrDescFromAddress("bchtest:qp86jfla8084048rckpv85ht90falr050s03ejaesm")
if err != nil {
t.Errorf("AddressToOutputScript() error = %v", err)
t.Errorf("GetAddrDescFromAddress() error = %v", err)
return
}
if !bytes.Equal(got2, want) {
t.Errorf("AddressToOutputScript() got2 = %v, want %v", got2, want)
t.Errorf("GetAddrDescFromAddress() got2 = %v, want %v", got2, want)
}
}
@ -127,20 +127,6 @@ var (
)
func init() {
var (
addr1, addr2, addr3 bchain.Address
err error
)
addr1, err = newBCashAddress("3AZKvpKhSh1o8t1QrX3UeXG9d2BhCRnbcK", Legacy)
if err == nil {
addr2, err = newBCashAddress("2NByHN6A8QYkBATzxf4pRGbCSHD5CEN2TRu", Legacy)
}
if err == nil {
addr3, err = newBCashAddress("2MvZguYaGjM7JihBgNqgLF2Ca2Enb76Hj9D", Legacy)
}
if err != nil {
panic(err)
}
testTx1 = bchain.Tx{
Hex: "01000000017f9a22c9cbf54bd902400df746f138f37bcf5b4d93eb755820e974ba43ed5f42040000006a4730440220037f4ed5427cde81d55b9b6a2fd08c8a25090c2c2fff3a75c1a57625ca8a7118022076c702fe55969fa08137f71afd4851c48e31082dd3c40c919c92cdbc826758d30121029f6da5623c9f9b68a9baf9c1bc7511df88fa34c6c2f71f7c62f2f03ff48dca80feffffff019c9700000000000017a9146144d57c8aff48492c9dfb914e120b20bad72d6f8773d00700",
@ -168,7 +154,6 @@ func init() {
"bitcoincash:pps5f4tu3tl5sjfvnhaeznsjpvst44eddugfcnqpy9",
},
},
Address: addr1,
},
},
}
@ -199,7 +184,6 @@ func init() {
"bchtest:prxkdrtcrm8xqrh6fvjqfhy3l5nt3w9wmq9fmsvkmz",
},
},
Address: addr2,
},
{
ValueSat: *big.NewInt(920081157),
@ -210,7 +194,6 @@ func init() {
"bchtest:pqjxv4dah42v0erh6r4zxa0gdcxm9w8cpg0qw8tqf6",
},
},
Address: addr3,
},
},
}

View File

@ -16,7 +16,7 @@ import (
)
// OutputScriptToAddressesFunc converts ScriptPubKey to bitcoin addresses
type OutputScriptToAddressesFunc func(script []byte, params *chaincfg.Params) ([]string, error)
type OutputScriptToAddressesFunc func(script []byte) ([]string, bool, error)
// BitcoinParser handle
type BitcoinParser struct {
@ -27,15 +27,15 @@ type BitcoinParser struct {
// NewBitcoinParser returns new BitcoinParser instance
func NewBitcoinParser(params *chaincfg.Params, c *Configuration) *BitcoinParser {
return &BitcoinParser{
&bchain.BaseParser{
AddressFactory: bchain.NewBaseAddress,
p := &BitcoinParser{
BaseParser: &bchain.BaseParser{
BlockAddressesToKeep: c.BlockAddressesToKeep,
AmountDecimalPoint: 8,
},
params,
outputScriptToAddresses,
Params: params,
}
p.OutputScriptToAddressesFunc = p.outputScriptToAddresses
return p
}
// GetChainParams contains network parameters for the main Bitcoin network,
@ -51,18 +51,28 @@ func GetChainParams(chain string) *chaincfg.Params {
return &chaincfg.MainNetParams
}
// GetAddrIDFromVout returns internal address representation of given transaction output
func (p *BitcoinParser) GetAddrIDFromVout(output *bchain.Vout) ([]byte, error) {
// GetAddrDescFromVout returns internal address representation (descriptor) of given transaction output
func (p *BitcoinParser) GetAddrDescFromVout(output *bchain.Vout) ([]byte, error) {
return hex.DecodeString(output.ScriptPubKey.Hex)
}
// GetAddrIDFromAddress returns internal address representation of given address
func (p *BitcoinParser) GetAddrIDFromAddress(address string) ([]byte, error) {
return p.AddressToOutputScript(address)
// GetAddrDescFromAddress returns internal address representation (descriptor) of given address
func (p *BitcoinParser) GetAddrDescFromAddress(address string) ([]byte, error) {
return p.addressToOutputScript(address)
}
// AddressToOutputScript converts bitcoin address to ScriptPubKey
func (p *BitcoinParser) AddressToOutputScript(address string) ([]byte, error) {
// GetAddressesFromAddrDesc returns addresses for given address descriptor with flag if the addresses are searchable
func (p *BitcoinParser) GetAddressesFromAddrDesc(addrDesc []byte) ([]string, bool, error) {
return p.outputScriptToAddresses(addrDesc)
}
// GetScriptFromAddrDesc returns output script for given address descriptor
func (p *BitcoinParser) GetScriptFromAddrDesc(addrDesc []byte) ([]byte, error) {
return addrDesc, nil
}
// addressToOutputScript converts bitcoin address to ScriptPubKey
func (p *BitcoinParser) addressToOutputScript(address string) ([]byte, error) {
da, err := btcutil.DecodeAddress(address, p.Params)
if err != nil {
return nil, err
@ -74,22 +84,21 @@ 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) {
return p.OutputScriptToAddressesFunc(script, p.Params)
}
// outputScriptToAddresses converts ScriptPubKey to bitcoin addresses
func outputScriptToAddresses(script []byte, params *chaincfg.Params) ([]string, error) {
_, addresses, _, err := txscript.ExtractPkScriptAddrs(script, params)
func (p *BitcoinParser) outputScriptToAddresses(script []byte) ([]string, bool, error) {
sc, addresses, _, err := txscript.ExtractPkScriptAddrs(script, p.Params)
if err != nil {
return nil, err
return nil, false, err
}
rv := make([]string, len(addresses))
for i, a := range addresses {
rv[i] = a.EncodeAddress()
}
return rv, nil
var s bool
if sc != txscript.NonStandardTy && sc != txscript.NullDataTy {
s = true
}
return rv, s, nil
}
func (p *BitcoinParser) TxFromMsgTx(t *wire.MsgTx, parseAddresses bool) bchain.Tx {
@ -117,7 +126,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.OutputScriptToAddressesFunc(out.PkScript)
}
s := bchain.ScriptPubKey{
Hex: hex.EncodeToString(out.PkScript),
@ -156,17 +165,6 @@ func (p *BitcoinParser) ParseTx(b []byte) (*bchain.Tx, error) {
}
tx := p.TxFromMsgTx(&t, true)
tx.Hex = hex.EncodeToString(b)
for i, vout := range tx.Vout {
if len(vout.ScriptPubKey.Addresses) == 1 {
a, err := p.AddressFactory(vout.ScriptPubKey.Addresses[0])
if err != nil {
return nil, err
}
tx.Vout[i].Address = a
}
}
return &tx, nil
}

View File

@ -1,4 +1,4 @@
// +build unittest
// build unittest
package btc
@ -10,7 +10,7 @@ import (
"testing"
)
func TestAddressToOutputScript(t *testing.T) {
func Test_GetAddrDescFromAddress(t *testing.T) {
type args struct {
address string
}
@ -49,20 +49,20 @@ func TestAddressToOutputScript(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parser.AddressToOutputScript(tt.args.address)
got, err := parser.GetAddrDescFromAddress(tt.args.address)
if (err != nil) != tt.wantErr {
t.Errorf("AddressToOutputScript() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("AddressToOutputScript() = %v, want %v", h, tt.want)
t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want)
}
})
}
}
func TestOutputScriptToAddresses(t *testing.T) {
func Test_GetAddressesFromAddrDesc(t *testing.T) {
type args struct {
script string
}
@ -70,43 +70,62 @@ func TestOutputScriptToAddresses(t *testing.T) {
name string
args args
want []string
want2 bool
wantErr bool
}{
{
name: "P2PKH",
args: args{script: "76a914be027bf3eac907bd4ac8cb9c5293b6f37662722088ac"},
want: []string{"1JKgN43B9SyLuZH19H5ECvr4KcfrbVHzZ6"},
want2: true,
wantErr: false,
},
{
name: "P2SH",
args: args{script: "a9140394b3cf9a44782c10105b93962daa8dba304d7f87"},
want: []string{"321x69Cb9HZLWwAWGiUBT1U81r1zPLnEjL"},
want2: true,
wantErr: false,
},
{
name: "P2WPKH",
args: args{script: "00141c12afc6b2602607fdbc209f2a053c54ecd2c673"},
want: []string{"bc1qrsf2l34jvqnq0lduyz0j5pfu2nkd93nnq0qggn"},
want2: true,
wantErr: false,
},
{
name: "P2WSH",
args: args{script: "002003973a40ec94c0d10f6f6f0e7a62ba2044b7d19db6ff2bf60651e17fb29d8d29"},
want: []string{"bc1qqwtn5s8vjnqdzrm0du885c46ypzt05vakmljhasx28shlv5a355sw5exgr"},
want2: true,
wantErr: false,
},
{
// TODO handle OP_RETURN better
name: "OP_RETURN",
args: args{script: "6a0461686f6a"},
want: []string{},
want2: false,
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 := outputScriptToAddresses(b, GetChainParams("main"))
got, got2, err := parser.GetAddressesFromAddrDesc(b)
if (err != nil) != 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("GetAddressesFromAddrDesc() = %v, want %v", got, tt.want)
}
if !reflect.DeepEqual(got2, tt.want2) {
t.Errorf("GetAddressesFromAddrDesc() = %v, want %v", got2, tt.want2)
}
})
}
@ -120,21 +139,6 @@ var (
)
func init() {
var (
addr1, addr2, addr3 bchain.Address
err error
)
addr1, err = bchain.NewBaseAddress("3AZKvpKhSh1o8t1QrX3UeXG9d2BhCRnbcK")
if err == nil {
addr2, err = bchain.NewBaseAddress("2NByHN6A8QYkBATzxf4pRGbCSHD5CEN2TRu")
}
if err == nil {
addr3, err = bchain.NewBaseAddress("2MvZguYaGjM7JihBgNqgLF2Ca2Enb76Hj9D")
}
if err != nil {
panic(err)
}
testTx1 = bchain.Tx{
Hex: "01000000017f9a22c9cbf54bd902400df746f138f37bcf5b4d93eb755820e974ba43ed5f42040000006a4730440220037f4ed5427cde81d55b9b6a2fd08c8a25090c2c2fff3a75c1a57625ca8a7118022076c702fe55969fa08137f71afd4851c48e31082dd3c40c919c92cdbc826758d30121029f6da5623c9f9b68a9baf9c1bc7511df88fa34c6c2f71f7c62f2f03ff48dca80feffffff019c9700000000000017a9146144d57c8aff48492c9dfb914e120b20bad72d6f8773d00700",
Blocktime: 1519053802,
@ -161,7 +165,6 @@ func init() {
"3AZKvpKhSh1o8t1QrX3UeXG9d2BhCRnbcK",
},
},
Address: addr1,
},
},
}
@ -192,7 +195,6 @@ func init() {
"2NByHN6A8QYkBATzxf4pRGbCSHD5CEN2TRu",
},
},
Address: addr2,
},
{
ValueSat: *big.NewInt(920081157),
@ -203,7 +205,6 @@ func init() {
"2MvZguYaGjM7JihBgNqgLF2Ca2Enb76Hj9D",
},
},
Address: addr3,
},
},
}

View File

@ -15,7 +15,7 @@ import (
"testing"
)
func TestAddressToOutputScript_Mainnet(t *testing.T) {
func Test_GetAddrDescFromAddress_Mainnet(t *testing.T) {
type args struct {
address string
}
@ -54,14 +54,14 @@ func TestAddressToOutputScript_Mainnet(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parser.AddressToOutputScript(tt.args.address)
got, err := parser.GetAddrDescFromAddress(tt.args.address)
if (err != nil) != tt.wantErr {
t.Errorf("AddressToOutputScript() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("AddressToOutputScript() = %v, want %v", h, tt.want)
t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want)
}
})
}
@ -76,24 +76,6 @@ var (
)
func init() {
var (
addr1, addr2, addr3, addr4 bchain.Address
err error
)
addr1, err = bchain.NewBaseAddress("DSvXNiqvG42wdteLqh3i6inxgDTs8Y9w2i")
if err == nil {
addr2, err = bchain.NewBaseAddress("DRemF3ZcqJ1PFeM7e7sXzzwQJKR8GNUtwK")
}
if err == nil {
addr3, err = bchain.NewBaseAddress("DJa8bWDrZKu4HgsYRYWuJrvxt6iTYuvXJ6")
}
if err == nil {
addr4, err = bchain.NewBaseAddress("DDTtqnuZ5kfRT5qh2c7sNtqrJmV3iXYdGG")
}
if err != nil {
panic(err)
}
testTx1 = bchain.Tx{
Hex: "01000000016b3c0c53267964120acf7f7e72217e3f463e52ce622f89659f6a6bb8e69a4d91000000006c493046022100a96454237e3a020994534583e28c04757881374bceac89f933ea9ff00b4db259022100fbb757ff7ea4f02c4e42556b2834c61eba1f1af605db089d836a0614d90a3b46012103cebdde6d1046e285df4f48497bc50dc20a4a258ca5b7308cb0a929c9fdadcd9dffffffff0217e823ca7f0200001976a914eef21768a546590993e313c7f3dfadf6a6efa1e888acaddf4cba010000001976a914e0fee2ea29dd9c6c759d8341bd0da4c4f738cced88ac00000000",
Blocktime: 1519053456,
@ -120,7 +102,6 @@ func init() {
"DSvXNiqvG42wdteLqh3i6inxgDTs8Y9w2i",
},
},
Address: addr1,
},
{
ValueSat: *big.NewInt(7420567469),
@ -131,7 +112,6 @@ func init() {
"DRemF3ZcqJ1PFeM7e7sXzzwQJKR8GNUtwK",
},
},
Address: addr2,
},
},
}
@ -162,7 +142,6 @@ func init() {
"DJa8bWDrZKu4HgsYRYWuJrvxt6iTYuvXJ6",
},
},
Address: addr3,
},
{
ValueSat: *big.NewInt(999999890000000),
@ -173,7 +152,6 @@ func init() {
"DDTtqnuZ5kfRT5qh2c7sNtqrJmV3iXYdGG",
},
},
Address: addr4,
},
},
}

View File

@ -23,7 +23,6 @@ type EthereumParser struct {
// NewEthereumParser returns new EthereumParser instance
func NewEthereumParser() *EthereumParser {
return &EthereumParser{&bchain.BaseParser{
AddressFactory: bchain.NewBaseAddress,
BlockAddressesToKeep: 0,
AmountDecimalPoint: 18,
}}
@ -68,7 +67,6 @@ func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, blocktime int64, confirma
txid := ethHashToHash(tx.Hash)
var (
fa, ta []string
addr bchain.Address
err error
)
if len(tx.From) > 2 {
@ -76,10 +74,6 @@ func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, blocktime int64, confirma
}
if len(tx.To) > 2 {
ta = []string{tx.To}
addr, err = p.AddressFactory(tx.To)
if err != nil {
return nil, err
}
}
// temporarily, the complete rpcTransaction without BlockHash is marshalled and hex encoded to bchain.Tx.Hex
bh := tx.BlockHash
@ -119,26 +113,25 @@ func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, blocktime int64, confirma
// Hex
Addresses: ta,
},
Address: addr,
},
},
}, nil
}
// GetAddrIDFromVout returns internal address representation of given transaction output
func (p *EthereumParser) GetAddrIDFromVout(output *bchain.Vout) ([]byte, error) {
// GetAddrDescFromVout returns internal address representation of given transaction output
func (p *EthereumParser) GetAddrDescFromVout(output *bchain.Vout) ([]byte, error) {
if len(output.ScriptPubKey.Addresses) != 1 {
return nil, bchain.ErrAddressMissing
}
return p.GetAddrIDFromAddress(output.ScriptPubKey.Addresses[0])
return p.GetAddrDescFromAddress(output.ScriptPubKey.Addresses[0])
}
func has0xPrefix(s string) bool {
return len(s) >= 2 && s[0] == '0' && (s[1]|32) == 'x'
}
// GetAddrIDFromAddress returns internal address representation of given address
func (p *EthereumParser) GetAddrIDFromAddress(address string) ([]byte, error) {
// GetAddrDescFromAddress returns internal address representation of given address
func (p *EthereumParser) GetAddrDescFromAddress(address string) ([]byte, error) {
// github.com/ethereum/go-ethereum/common.HexToAddress does not handle address errors, using own decoding
if has0xPrefix(address) {
address = address[2:]
@ -152,6 +145,16 @@ func (p *EthereumParser) GetAddrIDFromAddress(address string) ([]byte, error) {
return hex.DecodeString(address)
}
// GetAddressesFromAddrDesc returns addresses for given address descriptor with flag if the addresses are searchable
func (p *EthereumParser) GetAddressesFromAddrDesc(addrDesc []byte) ([]string, bool, error) {
return []string{hexutil.Encode(addrDesc)}, true, nil
}
// GetScriptFromAddrDesc returns output script for given address descriptor
func (p *EthereumParser) GetScriptFromAddrDesc(addrDesc []byte) ([]byte, error) {
return addrDesc, nil
}
func hexDecode(s string) ([]byte, error) {
b, err := hexutil.Decode(s)
if err != nil && err != hexutil.ErrEmptyString {

View File

@ -10,7 +10,7 @@ import (
"testing"
)
func TestEthParser_GetAddrIDFromAddress(t *testing.T) {
func TestEthParser_GetAddrDescFromAddress(t *testing.T) {
type args struct {
address string
}
@ -51,14 +51,14 @@ func TestEthParser_GetAddrIDFromAddress(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := NewEthereumParser()
got, err := p.GetAddrIDFromAddress(tt.args.address)
got, err := p.GetAddrDescFromAddress(tt.args.address)
if (err != nil) != tt.wantErr {
t.Errorf("EthParser.GetAddrIDFromAddress() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("EthParser.GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("EthParser.GetAddrIDFromAddress() = %v, want %v", h, tt.want)
t.Errorf("EthParser.GetAddrDescFromAddress() = %v, want %v", h, tt.want)
}
})
}
@ -71,17 +71,6 @@ var (
)
func init() {
var (
addr1, addr2 bchain.Address
err error
)
addr1, err = bchain.NewBaseAddress("0x682b7903a11098cf770c7aef4aa02a85b3f3601a")
if err == nil {
addr2, err = bchain.NewBaseAddress("0x555ee11fbddc0e49a9bab358a8941ad95ffdb48f")
}
if err != nil {
panic(err)
}
testTx1 = bchain.Tx{
Blocktime: 1521515026,
@ -99,7 +88,6 @@ func init() {
ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"0x682b7903a11098cf770c7aef4aa02a85b3f3601a"},
},
Address: addr1,
},
},
}
@ -120,7 +108,6 @@ func init() {
ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"0x555ee11fbddc0e49a9bab358a8941ad95ffdb48f"},
},
Address: addr2,
},
},
}

View File

@ -11,7 +11,7 @@ import (
"testing"
)
func TestAddressToOutputScript_Testnet(t *testing.T) {
func Test_GetAddrDescFromAddress_Testnet(t *testing.T) {
type args struct {
address string
}
@ -44,20 +44,20 @@ func TestAddressToOutputScript_Testnet(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parser.AddressToOutputScript(tt.args.address)
got, err := parser.GetAddrDescFromAddress(tt.args.address)
if (err != nil) != tt.wantErr {
t.Errorf("AddressToOutputScript() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("AddressToOutputScript() = %v, want %v", h, tt.want)
t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want)
}
})
}
}
func TestAddressToOutputScript_Mainnet(t *testing.T) {
func Test_GetAddrDescFromAddress_Mainnet(t *testing.T) {
type args struct {
address string
}
@ -108,14 +108,14 @@ func TestAddressToOutputScript_Mainnet(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parser.AddressToOutputScript(tt.args.address)
got, err := parser.GetAddrDescFromAddress(tt.args.address)
if (err != nil) != tt.wantErr {
t.Errorf("AddressToOutputScript() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("AddressToOutputScript() = %v, want %v", h, tt.want)
t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want)
}
})
}
@ -128,18 +128,6 @@ var (
)
func init() {
var (
addr1, addr2 bchain.Address
err error
)
addr1, err = bchain.NewBaseAddress("LMgENNXzzuPxp7vfMjDrCU44bsmrEMgqvc")
if err == nil {
addr2, err = bchain.NewBaseAddress("LV1ByjbJNFTHyFQqwqwdJXKJznYDzXzg4B")
}
if err != nil {
panic(err)
}
testTx1 = bchain.Tx{
Hex: "02000000031e1977dc524bec5929e95d8d0946812944b7b5bda12f5b99fdf557773f2ee65e0100000000ffffffff8a398e44546dce0245452b90130e86832b21fd68f26662bc33aeb7c6c115d23c1900000000ffffffffb807ab93a7fcdff7af6d24581a4a18aa7c1db1ebecba2617a6805b009513940f0c00000000ffffffff020001a04a000000001976a9141ae882e788091732da6910595314447c9e38bd8d88ac27440f00000000001976a9146b474cbf0f6004329b630bdd4798f2c23d1751b688ac00000000",
Blocktime: 1519053456,
@ -182,7 +170,6 @@ func init() {
"LMgENNXzzuPxp7vfMjDrCU44bsmrEMgqvc",
},
},
Address: addr1,
},
{
ValueSat: *big.NewInt(1000487),
@ -193,7 +180,6 @@ func init() {
"LV1ByjbJNFTHyFQqwqwdJXKJznYDzXzg4B",
},
},
Address: addr2,
},
},
}

View File

@ -1,4 +1,4 @@
// build unittest
// +build unittest
package monacoin
@ -11,7 +11,7 @@ import (
"testing"
)
func TestAddressToOutputScript_Testnet(t *testing.T) {
func Test_GetAddrDescFromAddress_Testnet(t *testing.T) {
type args struct {
address string
}
@ -44,20 +44,20 @@ func TestAddressToOutputScript_Testnet(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parser.AddressToOutputScript(tt.args.address)
got, err := parser.GetAddrDescFromAddress(tt.args.address)
if (err != nil) != tt.wantErr {
t.Errorf("AddressToOutputScript() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("AddressToOutputScript() = %v, want %v", h, tt.want)
t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want)
}
})
}
}
func TestAddressToOutputScript_Mainnet(t *testing.T) {
func Test_GetAddrDescFromAddress_Mainnet(t *testing.T) {
type args struct {
address string
}
@ -108,14 +108,14 @@ func TestAddressToOutputScript_Mainnet(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parser.AddressToOutputScript(tt.args.address)
got, err := parser.GetAddrDescFromAddress(tt.args.address)
if (err != nil) != tt.wantErr {
t.Errorf("AddressToOutputScript() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("AddressToOutputScript() = %v, want %v", h, tt.want)
t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want)
}
})
}
@ -128,18 +128,6 @@ var (
)
func init() {
var (
addr1, addr2 bchain.Address
err error
)
addr1, err = bchain.NewBaseAddress("MWpWpANNQRskQHcuY5ZQpN4BVynQxmSxRb")
if err == nil {
addr2, err = bchain.NewBaseAddress("MGtFpCVyKEHNtpVNesxPMxYuQayoEBX5yZ")
}
if err != nil {
panic(err)
}
testTx1 = bchain.Tx{
Hex: "0200000003e44ef4e5fe2e4345f1e1340afe396c780773e3834a5bffb153a2faf510e2845e000000006a47304402205ebd735621eaaf512441998727a37e99be94e5ecded54601ea3eebac9282bc2502207d48da44e1c883579c6cd8c2b8ccfb5380e5ac71affe70b475d2b558e0f7bd4b01210391f72b34c04855ce16b97dd79b0ba78fc4b26f40abce853c33788e348cb79c3bfeffffff0ad690a74c43c0df9527c516d26e31fa47e15471a2ead65757b672522888e920010000006b48304502210091a473124bf506edbb095951aa1a32c76bea7eba4020ae2858314961b1a83de602205c3818e517cf830a95a1208fc84aa343faaeeaaa96eab76238379769598ab2d40121038c217e5de8e375ed6cf648e96ec6bfb9e0fbcf5ae3945a5ea60d16919d9c8b68feffffffb9aa4aed4ad4c4b95419e132a43db34aa03a7ec35ef0beecdd627f9ca07bda03010000006a47304402204906d973ac9b4786403f8f8fc2b2ad2e6745ea01a93336b4b67af1d7d1b625cc022016820be905ffd6e11949da79e7a1c7eb97939421a04e0645c8caef8fc585f7ca012102b5f647c4eb677e952913c0b6934c12b29dc50afba8b558b1677ffd2d78c84a88feffffff02f6da4601000000001976a914fb69fe6dcfe88557dc0ce0ea65bd7cf02f5e4f5b88ac8bfd8c57000000001976a914628d603ac50d656e3311ff0cd5490b4c5cdd92ea88ac25fd1400",
Blocktime: 1530902705,
@ -182,7 +170,6 @@ func init() {
"MWpWpANNQRskQHcuY5ZQpN4BVynQxmSxRb",
},
},
Address: addr1,
},
{
ValueSat: *big.NewInt(1468857739),
@ -193,7 +180,6 @@ func init() {
"MGtFpCVyKEHNtpVNesxPMxYuQayoEBX5yZ",
},
},
Address: addr2,
},
},
}

View File

@ -91,11 +91,11 @@ func GetMonaChainParams(chain string) *monacoinCfg.Params {
// GetAddrIDFromAddress returns internal address representation of given address
func (p *MonacoinParser) GetAddrIDFromAddress(address string) ([]byte, error) {
return p.AddressToOutputScript(address)
return p.addressToOutputScript(address)
}
// AddressToOutputScript converts monacoin address to ScriptPubKey
func (p *MonacoinParser) AddressToOutputScript(address string) ([]byte, error) {
// addressToOutputScript converts monacoin address to ScriptPubKey
func (p *MonacoinParser) addressToOutputScript(address string) ([]byte, error) {
switch p.Params.Net {
case MainnetMagic:
da, err := monautil.DecodeAddress(address, &MonaMainParams)

View File

@ -13,7 +13,7 @@ import (
"testing"
)
func TestAddressToOutputScript_Mainnet(t *testing.T) {
func Test_GetAddrDescFromAddress_Mainnet(t *testing.T) {
type args struct {
address string
}
@ -40,14 +40,14 @@ func TestAddressToOutputScript_Mainnet(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parser.AddressToOutputScript(tt.args.address)
got, err := parser.GetAddrDescFromAddress(tt.args.address)
if (err != nil) != tt.wantErr {
t.Errorf("AddressToOutputScript() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("AddressToOutputScript() = %v, want %v", h, tt.want)
t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want)
}
})
}

View File

@ -11,7 +11,7 @@ import (
"testing"
)
func TestAddressToOutputScript_Mainnet(t *testing.T) {
func Test_GetAddrDescFromAddress_Mainnet(t *testing.T) {
type args struct {
address string
}
@ -56,14 +56,14 @@ func TestAddressToOutputScript_Mainnet(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parser.AddressToOutputScript(tt.args.address)
got, err := parser.GetAddrDescFromAddress(tt.args.address)
if (err != nil) != tt.wantErr {
t.Errorf("AddressToOutputScript() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("AddressToOutputScript() = %v, want %v", h, tt.want)
t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want)
}
})
}
@ -76,18 +76,6 @@ var (
)
func init() {
var (
addr1, addr2 bchain.Address
err error
)
addr1, err = bchain.NewBaseAddress("Vp1UqzsmVecaexfbWFGSFFL5x1g2XQnrGR")
if err == nil {
addr2, err = bchain.NewBaseAddress("38A1RNvbA5c9wNRfyLVn1FCH5TPKJVG8YR")
}
if err != nil {
panic(err)
}
testTx1 = bchain.Tx{
Hex: "010000000146fd781834a34e0399ccda1edf9ec47d715e17d904ad0958d533a240b3605ad6000000006a473044022026b352a0c35c232342339e2b50ec9f04587b990d5213174e368cc76dc82686f002207d0787461ad846825872a50d3d6fc748d5a836575c1daf6ad0ca602f9c4a8826012103d36b6b829c571ed7caa565eca9bdc2aa36519b7ab8551ace5edb0356d477ad3cfdffffff020882a400000000001976a91499b16da88a7e29b913b6131df2644d6d06cb331b88ac80f0fa020000000017a91446eb90e002f137f05385896c882fe000cc2e967f8774870e00",
Blocktime: 1529925180,
@ -114,7 +102,6 @@ func init() {
"Vp1UqzsmVecaexfbWFGSFFL5x1g2XQnrGR",
},
},
Address: addr1,
},
{
ValueSat: *big.NewInt(50000000),
@ -125,7 +112,6 @@ func init() {
"38A1RNvbA5c9wNRfyLVn1FCH5TPKJVG8YR",
},
},
Address: addr2,
},
},
}

View File

@ -7,6 +7,7 @@ import (
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/wire"
"github.com/juju/errors"
)
const (
@ -24,7 +25,6 @@ type ZCashParser struct {
func NewZCashParser(c *btc.Configuration) *ZCashParser {
return &ZCashParser{
&bchain.BaseParser{
AddressFactory: bchain.NewBaseAddress,
BlockAddressesToKeep: c.BlockAddressesToKeep,
AmountDecimalPoint: 8,
},
@ -51,8 +51,8 @@ func GetChainParams(chain string) *chaincfg.Params {
return params
}
// GetAddrIDFromVout returns internal address representation of given transaction output
func (p *ZCashParser) GetAddrIDFromVout(output *bchain.Vout) ([]byte, error) {
// GetAddrDescFromVout returns internal address representation of given transaction output
func (p *ZCashParser) GetAddrDescFromVout(output *bchain.Vout) ([]byte, error) {
if len(output.ScriptPubKey.Addresses) != 1 {
return nil, nil
}
@ -60,8 +60,20 @@ func (p *ZCashParser) GetAddrIDFromVout(output *bchain.Vout) ([]byte, error) {
return hash, err
}
// GetAddrIDFromAddress returns internal address representation of given address
func (p *ZCashParser) GetAddrIDFromAddress(address string) ([]byte, error) {
// GetAddrDescFromAddress returns internal address representation of given address
func (p *ZCashParser) GetAddrDescFromAddress(address string) ([]byte, error) {
hash, _, err := utils.CheckDecode(address)
return hash, err
}
// GetAddressesFromAddrDesc returns addresses for given address descriptor with flag if the addresses are searchable
func (p *ZCashParser) GetAddressesFromAddrDesc(addrDesc []byte) ([]string, bool, error) {
// TODO implement
return nil, false, errors.New("GetAddressesFromAddrDesc: not implemented")
}
// GetScriptFromAddrDesc returns output script for given address descriptor
func (p *ZCashParser) GetScriptFromAddrDesc(addrDesc []byte) ([]byte, error) {
// TODO implement
return nil, errors.New("GetScriptFromAddrDesc: not implemented")
}

View File

@ -19,21 +19,6 @@ var (
)
func init() {
var (
addr1, addr2, addr3 bchain.Address
err error
)
addr1, err = bchain.NewBaseAddress("t1Y4yL14ACHaAbjemkdpW7nYNHWnv1yQbDA")
if err == nil {
addr2, err = bchain.NewBaseAddress("t1VmHTTwpEtwvojxodN2CSQqLYi1hzY3cAq")
}
if err == nil {
addr3, err = bchain.NewBaseAddress("t1ecxMXpphUTRQXGLXnVhJ6ucqD3DZipddg")
}
if err != nil {
panic(err)
}
testTx1 = bchain.Tx{
Hex: "02000000019c012650c99d0ef761e863dbb966babf2cb7a7a2b5d90b1461c09521c473d23d000000006b483045022100f220f48c5267ef92a1e7a4d3b44fe9d97cce76eeba2785d45a0e2620b70e8d7302205640bc39e197ce19d95a98a3239af0f208ca289c067f80c97d8e411e61da5dee0121021721e83315fb5282f1d9d2a11892322df589bccd9cef45517b5fb3cfd3055c83ffffffff018eec1a3c040000001976a9149bb8229741305d8316ba3ca6a8d20740ce33c24188ac000000000162b4fc6b0000000000000000000000006ffa88c89b74f0f82e24744296845a0d0113b132ff5dfc2af34e6418eb15206af53078c4dd475cf143cd9a427983f5993622464b53e3a37d2519a946492c3977e30f0866550b9097222993a439a39260ac5e7d36aef38c7fdd1df3035a2d5817a9c20526e38f52f822d4db9d2f0156c4119d786d6e3a060ca871df7fae9a5c3a9c921b38ddc6414b13d16aa807389c68016e54bd6a9eb3b23a6bc7bf152e6dba15e9ec36f95dab15ad8f4a92a9d0309bbd930ef24bb7247bf534065c1e2f5b42e2c80eb59f48b4da6ec522319e065f8c4e463f95cc7fcad8d7ee91608e3c0ffcaa44129ba2d2da45d9a413919eca41af29faaf806a3eeb823e5a6c51afb1ec709505d812c0306bd76061a0a62d207355ad44d1ffce2b9e1dfd0818f79bd0f8e4031116b71fee2488484f17818b80532865773166cd389929e8409bb94e3948bd2e0215ef96d4e29d094590fda0de50715c11ff47c03380bb1d31b14e5b4ad8a372ca0b03364ef85f086b8a8eb5c56c3b1aee33e2cfbf1b2be1a3fb41b14b2c432b5d04d54c058fa87a96ae1d65d61b79360d09acc1e25a883fd7ae9a2a734a03362903021401c243173e1050b5cdb459b9ffc07c95e920f026618952d3a800b2e47e03b902084aed7ee8466a65d34abdbbd292781564dcd9b7440029d48c2640ebc196d4b40217f2872c1d0c1c9c2abf1147d6a5a9501895bc92960bfa182ceeb76a658224f1022bc53c4c1cd6888d72a152dc1aec5ba8a1d750fb7e498bee844d3481e4b4cd210227f94f775744185c9f24571b7df0c1c694cb2d3e4e9b955ed0b1caad2b02b5702139c4fbba03f0e422b2f3e4fc822b4f58baf32e7cd217cdbdec8540cb13d6496f271959b72a05e130eeffbe5b9a7fcd2793347cd9c0ea695265669844c363190f690c52a600cf413c3f00bdc5e9d1539e0cc63f4ec2945e0d86e6304a6deb5651e73eac21add5a641dfc95ab56200ed40d81f76755aee4659334c17ed3841ca5a5ab22f923956be1d264be2b485a0de55404510ece5c73d6626798be688f9dc18b69846acfe897a357cc4afe31f57fea32896717f124290e68f36f849fa6ecf76e02087f8c19dbc566135d7fa2daca2d843b9cc5bc3897d35f1de7d174f6407658f4a3706c12cea53d880b4d8c4d45b3f0d210214f815be49a664021a4a44b4a63e06a41d76b46f9aa6bad248e8d1a974ae7bbae5ea8ac269447db91637a19346729083cad5aebd5ff43ea13d04783068e9136da321b1152c666d2995d0ca06b26541deac62f4ef91f0e4af445b18a5c2a17c96eada0b27f85bb26dfb8f16515114c6b9f88037e2b85b3b84b65822eb99c992d99d12dcf9c71e5b46a586016faf5758483a716566db95b42187c101df68ca0554824e1c23cf0302bea03ad0a146af57e91794a268b8c82d78211718c8b5fea286f5de72fc7dfffecddcc02413525c472cb26022641d4bec2b8b7e71a7beb9ee18b82632799498eeee9a351cb9431a8d1906d5164acdf351bd538c3e9d1da8a211fe1cd18c44e72d8cdf16ce3fc9551552c05d52846ea7ef619232102588395cc2bcce509a4e7f150262a76c15475496c923dfce6bfc05871467ee7c213b39ea365c010083e0b1ba8926d3a9e586d8b11c9bab2a47d888bc7cb1a226c0086a1530e295d0047547006f4c8f1c24cdd8e16bb3845749895dec95f03fcda97d3224f6875b1b7b1c819d2fd35dd30968a3c82bc480d10082caf9d9dda8f9ec649c136c7fa07978099d97eaf4abfdc9854c266979d3cfc868f60689b6e3098b6c52a21796fe7c259d9a0dadf1b6efa59297d4c8c902febe7acf826eed30d40d2ac5119be91b51f4839d94599872c9a93c3e2691294914034001d3a278cb4a84d4ae048c0201a97e4cf1341ee663a162f5b586355018b9e5e30624ccdbeacf7d0382afacaf45f08e84d30c50bcd4e55c3138377261deb4e8c2931cd3c51cee94a048ae4839517b6e6537a5c0148d3830a33fea719ef9b4fa437e4d5fecdb646397c19ee56a0973c362a81803895cdc67246352dc566689cb203f9ebda900a5537bbb75aa25ddf3d4ab87b88737a58d760e1d271f08265daae1fe056e71971a8b826e5b215a05b71f99315b167dd2ec78874189657acafac2b5eeb9a901913f55f7ab69e1f9b203504448d414e71098b932a2309db57257eb3fef9de2f2a5a69aa46747d7b827df838345d38b95772bdab8c178c45777b92e8773864964b8e12ae29dbc1b21bf6527589f6bec71ff1cbb9928477409811c2e8150c79c3f21027ee954863b716875d3e9adfc6fdb18cd57a49bb395ca5c42da56f3beb78aad3a7a487de34a870bca61f3cdec422061328c83c910ab32ea7403c354915b7ebee29e1fea5a75158197e4a68e103f017fd7de5a70148ee7ce59356b1a74f83492e14faaa6cd4870bcc004e6eb0114d3429b74ea98fe2851b4553467a7660074e69b040aa31220d0e405d9166dbaf15e3ae2d8ec3b049ed99d17e0743bb6a1a7c3890bbdb7117f7374ad7a59aa1ab47d10445b28f4bc033794a71f88a8bf024189e9d27f9dc5859a4296437585b215656f807aca9dad35747494a43b8a1cf38be2b18a13de32a262ab29f9ba271c4fbce1a470a8243ebf9e7fd37b09262314afbb9a7e1802",
Blocktime: 1521645728,
@ -60,7 +45,6 @@ func init() {
"t1Y4yL14ACHaAbjemkdpW7nYNHWnv1yQbDA",
},
},
Address: addr1,
},
},
}
@ -91,7 +75,6 @@ func init() {
"t1VmHTTwpEtwvojxodN2CSQqLYi1hzY3cAq",
},
},
Address: addr2,
},
{
ValueSat: *big.NewInt(10000000),
@ -102,7 +85,6 @@ func init() {
"t1ecxMXpphUTRQXGLXnVhJ6ucqD3DZipddg",
},
},
Address: addr3,
},
},
}

View File

@ -12,7 +12,7 @@ type NonUTXOMempool struct {
chain BlockChain
mux sync.Mutex
txToInputOutput map[string][]addrIndex
addrIDToTx map[string][]outpoint
addrDescToTx map[string][]outpoint
}
// NewNonUTXOMempool creates new mempool handler.
@ -23,13 +23,13 @@ func NewNonUTXOMempool(chain BlockChain) *NonUTXOMempool {
// GetTransactions returns slice of mempool transactions for given address
func (m *NonUTXOMempool) GetTransactions(address string) ([]string, error) {
parser := m.chain.GetChainParser()
addrID, err := parser.GetAddrIDFromAddress(address)
addrDesc, err := parser.GetAddrDescFromAddress(address)
if err != nil {
return nil, err
}
m.mux.Lock()
defer m.mux.Unlock()
outpoints := m.addrIDToTx[string(addrID)]
outpoints := m.addrDescToTx[string(addrDesc)]
txs := make([]string, 0, len(outpoints))
for _, o := range outpoints {
txs = append(txs, o.txid)
@ -37,11 +37,11 @@ func (m *NonUTXOMempool) GetTransactions(address string) ([]string, error) {
return txs, nil
}
func (m *NonUTXOMempool) updateMappings(newTxToInputOutput map[string][]addrIndex, newAddrIDToTx map[string][]outpoint) {
func (m *NonUTXOMempool) updateMappings(newTxToInputOutput map[string][]addrIndex, newAddrDescToTx map[string][]outpoint) {
m.mux.Lock()
defer m.mux.Unlock()
m.txToInputOutput = newTxToInputOutput
m.addrIDToTx = newAddrIDToTx
m.addrDescToTx = newAddrDescToTx
}
// Resync gets mempool transactions and maps outputs to transactions.
@ -57,7 +57,7 @@ func (m *NonUTXOMempool) Resync(onNewTxAddr func(txid string, addr string)) (int
parser := m.chain.GetChainParser()
// allocate slightly larger capacity of the maps
newTxToInputOutput := make(map[string][]addrIndex, len(m.txToInputOutput)+5)
newAddrIDToTx := make(map[string][]outpoint, len(m.addrIDToTx)+5)
newAddrDescToTx := make(map[string][]outpoint, len(m.addrDescToTx)+5)
for _, txid := range txs {
io, exists := m.txToInputOutput[txid]
if !exists {
@ -68,15 +68,15 @@ func (m *NonUTXOMempool) Resync(onNewTxAddr func(txid string, addr string)) (int
}
io = make([]addrIndex, 0, len(tx.Vout)+len(tx.Vin))
for _, output := range tx.Vout {
addrID, err := parser.GetAddrIDFromVout(&output)
addrDesc, err := parser.GetAddrDescFromVout(&output)
if err != nil {
if err != ErrAddressMissing {
glog.Error("error in output addrID in ", txid, " ", output.N, ": ", err)
glog.Error("error in output addrDesc in ", txid, " ", output.N, ": ", err)
}
continue
}
if len(addrID) > 0 {
io = append(io, addrIndex{string(addrID), int32(output.N)})
if len(addrDesc) > 0 {
io = append(io, addrIndex{string(addrDesc), int32(output.N)})
}
if onNewTxAddr != nil && len(output.ScriptPubKey.Addresses) == 1 {
onNewTxAddr(tx.Txid, output.ScriptPubKey.Addresses[0])
@ -85,22 +85,22 @@ func (m *NonUTXOMempool) Resync(onNewTxAddr func(txid string, addr string)) (int
for _, input := range tx.Vin {
for i, a := range input.Addresses {
if len(a) > 0 {
addrID, err := parser.GetAddrIDFromAddress(a)
addrDesc, err := parser.GetAddrDescFromAddress(a)
if err != nil {
glog.Error("error in input addrID in ", txid, " ", a, ": ", err)
glog.Error("error in input addrDesc in ", txid, " ", a, ": ", err)
continue
}
io = append(io, addrIndex{string(addrID), int32(^i)})
io = append(io, addrIndex{string(addrDesc), int32(^i)})
}
}
}
}
newTxToInputOutput[txid] = io
for _, si := range io {
newAddrIDToTx[si.addrID] = append(newAddrIDToTx[si.addrID], outpoint{txid, si.n})
newAddrDescToTx[si.addrDesc] = append(newAddrDescToTx[si.addrDesc], outpoint{txid, si.n})
}
}
m.updateMappings(newTxToInputOutput, newAddrIDToTx)
m.updateMappings(newTxToInputOutput, newAddrDescToTx)
glog.Info("Mempool: resync finished in ", time.Since(start), ", ", len(m.txToInputOutput), " transactions in mempool")
return len(m.txToInputOutput), nil
}

View File

@ -9,8 +9,8 @@ import (
// addrIndex and outpoint are used also in non utxo mempool
type addrIndex struct {
addrID string
n int32
addrDesc string
n int32
}
type outpoint struct {
@ -28,7 +28,7 @@ type UTXOMempool struct {
chain BlockChain
mux sync.Mutex
txToInputOutput map[string][]addrIndex
addrIDToTx map[string][]outpoint
addrDescToTx map[string][]outpoint
chanTxid chan string
chanAddrIndex chan txidio
onNewTxAddr func(txid string, addr string)
@ -70,13 +70,13 @@ func NewUTXOMempool(chain BlockChain, workers int, subworkers int) *UTXOMempool
// GetTransactions returns slice of mempool transactions for given address
func (m *UTXOMempool) GetTransactions(address string) ([]string, error) {
parser := m.chain.GetChainParser()
addrID, err := parser.GetAddrIDFromAddress(address)
addrDesc, err := parser.GetAddrDescFromAddress(address)
if err != nil {
return nil, err
}
m.mux.Lock()
defer m.mux.Unlock()
outpoints := m.addrIDToTx[string(addrID)]
outpoints := m.addrDescToTx[string(addrDesc)]
txs := make([]string, 0, len(outpoints))
for _, o := range outpoints {
txs = append(txs, o.txid)
@ -84,15 +84,14 @@ func (m *UTXOMempool) GetTransactions(address string) ([]string, error) {
return txs, nil
}
func (m *UTXOMempool) updateMappings(newTxToInputOutput map[string][]addrIndex, newAddrIDToTx map[string][]outpoint) {
func (m *UTXOMempool) updateMappings(newTxToInputOutput map[string][]addrIndex, newAddrDescToTx map[string][]outpoint) {
m.mux.Lock()
defer m.mux.Unlock()
m.txToInputOutput = newTxToInputOutput
m.addrIDToTx = newAddrIDToTx
m.addrDescToTx = newAddrDescToTx
}
func (m *UTXOMempool) getInputAddress(input outpoint) *addrIndex {
// TODO - possibly get from DB unspenttxs - however some output txs can be also in mempool
itx, err := m.chain.GetTransactionForMempool(input.txid)
if err != nil {
glog.Error("cannot get transaction ", input.txid, ": ", err)
@ -102,12 +101,12 @@ func (m *UTXOMempool) getInputAddress(input outpoint) *addrIndex {
glog.Error("Vout len in transaction ", input.txid, " ", len(itx.Vout), " input.Vout=", input.vout)
return nil
}
addrID, err := m.chain.GetChainParser().GetAddrIDFromVout(&itx.Vout[input.vout])
addrDesc, err := m.chain.GetChainParser().GetAddrDescFromVout(&itx.Vout[input.vout])
if err != nil {
glog.Error("error in addrID in ", input.txid, " ", input.vout, ": ", err)
glog.Error("error in addrDesc in ", input.txid, " ", input.vout, ": ", err)
return nil
}
return &addrIndex{string(addrID), ^input.vout}
return &addrIndex{string(addrDesc), ^input.vout}
}
@ -120,13 +119,13 @@ func (m *UTXOMempool) getTxAddrs(txid string, chanInput chan outpoint, chanResul
glog.V(2).Info("mempool: gettxaddrs ", txid, ", ", len(tx.Vin), " inputs")
io := make([]addrIndex, 0, len(tx.Vout)+len(tx.Vin))
for _, output := range tx.Vout {
addrID, err := m.chain.GetChainParser().GetAddrIDFromVout(&output)
addrDesc, err := m.chain.GetChainParser().GetAddrDescFromVout(&output)
if err != nil {
glog.Error("error in addrID in ", txid, " ", output.N, ": ", err)
glog.Error("error in addrDesc in ", txid, " ", output.N, ": ", err)
continue
}
if len(addrID) > 0 {
io = append(io, addrIndex{string(addrID), int32(output.N)})
if len(addrDesc) > 0 {
io = append(io, addrIndex{string(addrDesc), int32(output.N)})
}
if m.onNewTxAddr != nil && len(output.ScriptPubKey.Addresses) == 1 {
m.onNewTxAddr(tx.Txid, output.ScriptPubKey.Addresses[0])
@ -177,13 +176,13 @@ func (m *UTXOMempool) Resync(onNewTxAddr func(txid string, addr string)) (int, e
glog.V(2).Info("mempool: resync ", len(txs), " txs")
// allocate slightly larger capacity of the maps
newTxToInputOutput := make(map[string][]addrIndex, len(m.txToInputOutput)+5)
newAddrIDToTx := make(map[string][]outpoint, len(m.addrIDToTx)+5)
newAddrDescToTx := make(map[string][]outpoint, len(m.addrDescToTx)+5)
dispatched := 0
onNewData := func(txid string, io []addrIndex) {
if len(io) > 0 {
newTxToInputOutput[txid] = io
for _, si := range io {
newAddrIDToTx[si.addrID] = append(newAddrIDToTx[si.addrID], outpoint{txid, si.n})
newAddrDescToTx[si.addrDesc] = append(newAddrDescToTx[si.addrDesc], outpoint{txid, si.n})
}
}
}
@ -212,7 +211,7 @@ func (m *UTXOMempool) Resync(onNewTxAddr func(txid string, addr string)) (int, e
tio := <-m.chanAddrIndex
onNewData(tio.txid, tio.io)
}
m.updateMappings(newTxToInputOutput, newAddrIDToTx)
m.updateMappings(newTxToInputOutput, newAddrDescToTx)
m.onNewTxAddr = nil
glog.Info("mempool: resync finished in ", time.Since(start), ", ", len(m.txToInputOutput), " transactions in mempool")
return len(m.txToInputOutput), nil

View File

@ -96,7 +96,6 @@ func setTxAddresses(parser bchain.BlockChainParser, tx *bchain.Tx) error {
if err == nil {
for i := 0; i < len(tx.Vout); i++ {
tx.Vout[i].ScriptPubKey.Addresses = tmp.Vout[i].ScriptPubKey.Addresses
tx.Vout[i].Address = tmp.Vout[i].Address
}
}
}

View File

@ -43,18 +43,11 @@ type ScriptPubKey struct {
Addresses []string `json:"addresses"`
}
type Address interface {
String() string
AreEqual(addr string) bool
InSlice(addrs []string) bool
}
type Vout struct {
ValueSat big.Int
JsonValue json.Number `json:"value"`
N uint32 `json:"n"`
ScriptPubKey ScriptPubKey `json:"scriptPubKey"`
Address Address
}
// Tx is blockchain transaction
@ -164,12 +157,11 @@ type BlockChainParser interface {
// AmountToBigInt converts amount in json.Number (string) to big.Int
// it uses string operations to avoid problems with rounding
AmountToBigInt(n json.Number) (big.Int, error)
// address id conversions
GetAddrIDFromVout(output *Vout) ([]byte, error)
GetAddrIDFromAddress(address string) ([]byte, error)
// address to output script conversions
AddressToOutputScript(address string) ([]byte, error)
OutputScriptToAddresses(script []byte) ([]string, error)
// address descriptor conversions
GetAddrDescFromVout(output *Vout) ([]byte, error)
GetAddrDescFromAddress(address string) ([]byte, error)
GetAddressesFromAddrDesc(addrDesc []byte) ([]string, bool, error)
GetScriptFromAddrDesc(addrDesc []byte) ([]byte, error)
// transactions
PackedTxidLen() int
PackTxid(txid string) ([]byte, error)

View File

@ -24,7 +24,7 @@ import (
const refreshIterator = 5000000
const packedHeightBytes = 4
const dbVersion = 3
const maxAddrIDLen = 1024
const maxAddrDescLen = 1024
// RepairRocksDB calls RocksDb db repair function
func RepairRocksDB(name string) error {
@ -161,13 +161,13 @@ func (d *RocksDB) GetTransactions(address string, lower uint32, higher uint32, f
if glog.V(1) {
glog.Infof("rocksdb: address get %s %d-%d ", address, lower, higher)
}
addrID, err := d.chainParser.GetAddrIDFromAddress(address)
addrDesc, err := d.chainParser.GetAddrDescFromAddress(address)
if err != nil {
return err
}
kstart := packAddressKey(addrID, lower)
kstop := packAddressKey(addrID, higher)
kstart := packAddressKey(addrDesc, lower)
kstop := packAddressKey(addrDesc, higher)
it := d.db.NewIteratorCF(d.ro, d.cfh[cfAddresses])
defer it.Close()
@ -281,24 +281,22 @@ type outpoint struct {
}
type TxInput struct {
addrID []byte
addrDesc []byte
ValueSat big.Int
}
func (ti *TxInput) Addresses(p bchain.BlockChainParser) ([]string, error) {
// TODO - we will need AddressesFromAddrID parser method, this will not work for ZCash
return p.OutputScriptToAddresses(ti.addrID)
func (ti *TxInput) Addresses(p bchain.BlockChainParser) ([]string, bool, error) {
return p.GetAddressesFromAddrDesc(ti.addrDesc)
}
type TxOutput struct {
addrID []byte
addrDesc []byte
Spent bool
ValueSat big.Int
}
func (to *TxOutput) Addresses(p bchain.BlockChainParser) ([]string, error) {
// TODO - we will need AddressesFromAddrID parser method, this will not work for ZCash
return p.OutputScriptToAddresses(to.addrID)
func (to *TxOutput) Addresses(p bchain.BlockChainParser) ([]string, bool, error) {
return p.GetAddressesFromAddrDesc(to.addrDesc)
}
type TxAddresses struct {
@ -324,9 +322,9 @@ type blockTxs struct {
inputs []outpoint
}
func (d *RocksDB) resetValueSatToZero(valueSat *big.Int, addrID []byte, logText string) {
ad, err := d.chainParser.OutputScriptToAddresses(addrID)
had := hex.EncodeToString(addrID)
func (d *RocksDB) resetValueSatToZero(valueSat *big.Int, addrDesc []byte, logText string) {
ad, _, err := d.chainParser.GetAddressesFromAddrDesc(addrDesc)
had := hex.EncodeToString(addrDesc)
if err != nil {
glog.Warningf("rocksdb: unparsable address hex '%v' reached negative %s %v, resetting to 0. Parser error %v", had, logText, valueSat.String(), err)
} else {
@ -353,40 +351,40 @@ func (d *RocksDB) processAddressesUTXO(block *bchain.Block, addresses map[string
for i, output := range tx.Vout {
tao := &ta.Outputs[i]
tao.ValueSat = output.ValueSat
addrID, err := d.chainParser.GetAddrIDFromVout(&output)
if err != nil || len(addrID) == 0 || len(addrID) > maxAddrIDLen {
addrDesc, err := d.chainParser.GetAddrDescFromVout(&output)
if err != nil || len(addrDesc) == 0 || len(addrDesc) > maxAddrDescLen {
if err != nil {
// do not log ErrAddressMissing, transactions can be without to address (for example eth contracts)
if err != bchain.ErrAddressMissing {
glog.Warningf("rocksdb: addrID: %v - height %d, tx %v, output %v", err, block.Height, tx.Txid, output)
glog.Warningf("rocksdb: addrDesc: %v - height %d, tx %v, output %v", err, block.Height, tx.Txid, output)
}
} else {
glog.Infof("rocksdb: height %d, tx %v, vout %v, skipping addrID of length %d", block.Height, tx.Txid, i, len(addrID))
glog.Infof("rocksdb: height %d, tx %v, vout %v, skipping addrDesc of length %d", block.Height, tx.Txid, i, len(addrDesc))
}
continue
}
tao.addrID = addrID
strAddrID := string(addrID)
tao.addrDesc = addrDesc
strAddrDesc := string(addrDesc)
// check that the address was used already in this block
o, processed := addresses[strAddrID]
o, processed := addresses[strAddrDesc]
if processed {
// check that the address was already used in this tx
processed = processedInTx(o, btxID)
}
addresses[strAddrID] = append(o, outpoint{
addresses[strAddrDesc] = append(o, outpoint{
btxID: btxID,
index: int32(i),
})
ab, e := balances[strAddrID]
ab, e := balances[strAddrDesc]
if !e {
ab, err = d.getAddrIDBalance(addrID)
ab, err = d.getAddrDescBalance(addrDesc)
if err != nil {
return err
}
if ab == nil {
ab = &AddrBalance{}
}
balances[strAddrID] = ab
balances[strAddrDesc] = ab
}
// add number of trx in balance only once, address can be multiple times in tx
if !processed {
@ -433,38 +431,38 @@ func (d *RocksDB) processAddressesUTXO(block *bchain.Block, addresses map[string
if ot.Spent {
glog.Warningf("rocksdb: height %d, tx %v, input tx %v vout %v is double spend", block.Height, tx.Txid, input.Txid, input.Vout)
}
tai.addrID = ot.addrID
tai.addrDesc = ot.addrDesc
tai.ValueSat = ot.ValueSat
// mark the output as spent in tx
ot.Spent = true
if len(ot.addrID) == 0 {
if len(ot.addrDesc) == 0 {
if !logged {
glog.Warningf("rocksdb: height %d, tx %v, input tx %v vout %v skipping empty address", block.Height, tx.Txid, input.Txid, input.Vout)
logged = true
}
continue
}
strAddrID := string(ot.addrID)
strAddrDesc := string(ot.addrDesc)
// check that the address was used already in this block
o, processed := addresses[strAddrID]
o, processed := addresses[strAddrDesc]
if processed {
// check that the address was already used in this tx
processed = processedInTx(o, spendingTxid)
}
addresses[strAddrID] = append(o, outpoint{
addresses[strAddrDesc] = append(o, outpoint{
btxID: spendingTxid,
index: ^int32(i),
})
ab, e := balances[strAddrID]
ab, e := balances[strAddrDesc]
if !e {
ab, err = d.getAddrIDBalance(ot.addrID)
ab, err = d.getAddrDescBalance(ot.addrDesc)
if err != nil {
return err
}
if ab == nil {
ab = &AddrBalance{}
}
balances[strAddrID] = ab
balances[strAddrDesc] = ab
}
// add number of trx in balance only once, address can be multiple times in tx
if !processed {
@ -472,7 +470,7 @@ func (d *RocksDB) processAddressesUTXO(block *bchain.Block, addresses map[string
}
ab.BalanceSat.Sub(&ab.BalanceSat, &ot.ValueSat)
if ab.BalanceSat.Sign() < 0 {
d.resetValueSatToZero(&ab.BalanceSat, ot.addrID, "balance")
d.resetValueSatToZero(&ab.BalanceSat, ot.addrDesc, "balance")
}
ab.SentSat.Add(&ab.SentSat, &ot.ValueSat)
}
@ -490,8 +488,8 @@ func processedInTx(o []outpoint, btxID []byte) bool {
}
func (d *RocksDB) storeAddresses(wb *gorocksdb.WriteBatch, height uint32, addresses map[string][]outpoint) error {
for addrID, outpoints := range addresses {
ba := []byte(addrID)
for addrDesc, outpoints := range addresses {
ba := []byte(addrDesc)
key := packAddressKey(ba, height)
val := d.packOutpoints(outpoints)
wb.PutCF(d.cfh[cfAddresses], key, val)
@ -512,17 +510,17 @@ func (d *RocksDB) storeTxAddresses(wb *gorocksdb.WriteBatch, am map[string]*TxAd
func (d *RocksDB) storeBalances(wb *gorocksdb.WriteBatch, abm map[string]*AddrBalance) error {
// allocate buffer big enough for number of txs + 2 bigints
buf := make([]byte, vlq.MaxLen32+2*maxPackedBigintBytes)
for addrID, ab := range abm {
for addrDesc, ab := range abm {
// balance with 0 transactions is removed from db - happens in disconnect
if ab == nil || ab.Txs <= 0 {
wb.DeleteCF(d.cfh[cfAddressBalance], []byte(addrID))
wb.DeleteCF(d.cfh[cfAddressBalance], []byte(addrDesc))
} else {
l := packVaruint(uint(ab.Txs), buf)
ll := packBigint(&ab.SentSat, buf[l:])
l += ll
ll = packBigint(&ab.BalanceSat, buf[l:])
l += ll
wb.PutCF(d.cfh[cfAddressBalance], []byte(addrID), buf[:l])
wb.PutCF(d.cfh[cfAddressBalance], []byte(addrDesc), buf[:l])
}
}
return nil
@ -611,8 +609,8 @@ func (d *RocksDB) getBlockTxs(height uint32) ([]blockTxs, error) {
return bt, nil
}
func (d *RocksDB) getAddrIDBalance(addrID []byte) (*AddrBalance, error) {
val, err := d.db.GetCF(d.ro, d.cfh[cfAddressBalance], addrID)
func (d *RocksDB) getAddrDescBalance(addrDesc []byte) (*AddrBalance, error) {
val, err := d.db.GetCF(d.ro, d.cfh[cfAddressBalance], addrDesc)
if err != nil {
return nil, err
}
@ -634,11 +632,11 @@ func (d *RocksDB) getAddrIDBalance(addrID []byte) (*AddrBalance, error) {
// GetAddressBalance returns address balance for an address or nil if address not found
func (d *RocksDB) GetAddressBalance(address string) (*AddrBalance, error) {
addrID, err := d.chainParser.GetAddrIDFromAddress(address)
addrDesc, err := d.chainParser.GetAddrDescFromAddress(address)
if err != nil {
return nil, err
}
return d.getAddrIDBalance(addrID)
return d.getAddrDescBalance(addrDesc)
}
func (d *RocksDB) getTxAddresses(btxID []byte) (*TxAddresses, error) {
@ -682,23 +680,23 @@ func packTxAddresses(ta *TxAddresses, buf []byte, varBuf []byte) []byte {
}
func appendTxInput(txi *TxInput, buf []byte, varBuf []byte) []byte {
la := len(txi.addrID)
la := len(txi.addrDesc)
l := packVaruint(uint(la), varBuf)
buf = append(buf, varBuf[:l]...)
buf = append(buf, txi.addrID...)
buf = append(buf, txi.addrDesc...)
l = packBigint(&txi.ValueSat, varBuf)
buf = append(buf, varBuf[:l]...)
return buf
}
func appendTxOutput(txo *TxOutput, buf []byte, varBuf []byte) []byte {
la := len(txo.addrID)
la := len(txo.addrDesc)
if txo.Spent {
la = ^la
}
l := packVarint(la, varBuf)
buf = append(buf, varBuf[:l]...)
buf = append(buf, txo.addrID...)
buf = append(buf, txo.addrDesc...)
l = packBigint(&txo.ValueSat, varBuf)
buf = append(buf, varBuf[:l]...)
return buf
@ -725,8 +723,8 @@ func unpackTxAddresses(buf []byte) (*TxAddresses, error) {
func unpackTxInput(ti *TxInput, buf []byte) int {
al, l := unpackVaruint(buf)
ti.addrID = make([]byte, al)
copy(ti.addrID, buf[l:l+int(al)])
ti.addrDesc = make([]byte, al)
copy(ti.addrDesc, buf[l:l+int(al)])
al += uint(l)
ti.ValueSat, l = unpackBigint(buf[al:])
return l + int(al)
@ -738,8 +736,8 @@ func unpackTxOutput(to *TxOutput, buf []byte) int {
to.Spent = true
al = ^al
}
to.addrID = make([]byte, al)
copy(to.addrID, buf[l:l+al])
to.addrDesc = make([]byte, al)
copy(to.addrDesc, buf[l:l+al])
al += l
to.ValueSat, l = unpackBigint(buf[al:])
return l + al
@ -792,13 +790,13 @@ func (d *RocksDB) unpackNOutpoints(buf []byte) ([]outpoint, int, error) {
return outpoints, p, nil
}
func (d *RocksDB) addAddrIDToRecords(op int, wb *gorocksdb.WriteBatch, records map[string][]outpoint, addrID []byte, btxid []byte, vout int32, bh uint32) error {
if len(addrID) > 0 {
if len(addrID) > maxAddrIDLen {
glog.Infof("rocksdb: block %d, skipping addrID of length %d", bh, len(addrID))
func (d *RocksDB) addAddrDescToRecords(op int, wb *gorocksdb.WriteBatch, records map[string][]outpoint, addrDesc []byte, btxid []byte, vout int32, bh uint32) error {
if len(addrDesc) > 0 {
if len(addrDesc) > maxAddrDescLen {
glog.Infof("rocksdb: block %d, skipping addrDesc of length %d", bh, len(addrDesc))
} else {
strAddrID := string(addrID)
records[strAddrID] = append(records[strAddrID], outpoint{
strAddrDesc := string(addrDesc)
records[strAddrDesc] = append(records[strAddrDesc], outpoint{
btxID: btxid,
index: vout,
})
@ -819,15 +817,15 @@ func (d *RocksDB) writeAddressesNonUTXO(wb *gorocksdb.WriteBatch, block *bchain.
return err
}
for _, output := range tx.Vout {
addrID, err := d.chainParser.GetAddrIDFromVout(&output)
addrDesc, err := d.chainParser.GetAddrDescFromVout(&output)
if err != nil {
// do not log ErrAddressMissing, transactions can be without to address (for example eth contracts)
if err != bchain.ErrAddressMissing {
glog.Warningf("rocksdb: addrID: %v - height %d, tx %v, output %v", err, block.Height, tx.Txid, output)
glog.Warningf("rocksdb: addrDesc: %v - height %d, tx %v, output %v", err, block.Height, tx.Txid, output)
}
continue
}
err = d.addAddrIDToRecords(op, wb, addresses, addrID, btxID, int32(output.N), block.Height)
err = d.addAddrDescToRecords(op, wb, addresses, addrDesc, btxID, int32(output.N), block.Height)
if err != nil {
return err
}
@ -835,20 +833,20 @@ func (d *RocksDB) writeAddressesNonUTXO(wb *gorocksdb.WriteBatch, block *bchain.
// store inputs in format txid ^index
for _, input := range tx.Vin {
for i, a := range input.Addresses {
addrID, err := d.chainParser.GetAddrIDFromAddress(a)
addrDesc, err := d.chainParser.GetAddrDescFromAddress(a)
if err != nil {
glog.Warningf("rocksdb: addrID: %v - %d %s", err, block.Height, addrID)
glog.Warningf("rocksdb: addrDesc: %v - %d %s", err, block.Height, addrDesc)
continue
}
err = d.addAddrIDToRecords(op, wb, addresses, addrID, btxID, int32(^i), block.Height)
err = d.addAddrDescToRecords(op, wb, addresses, addrDesc, btxID, int32(^i), block.Height)
if err != nil {
return err
}
}
}
}
for addrID, outpoints := range addresses {
key := packAddressKey([]byte(addrID), block.Height)
for addrDesc, outpoints := range addresses {
key := packAddressKey([]byte(addrDesc), block.Height)
switch op {
case opInsert:
val := d.packOutpoints(outpoints)
@ -1024,12 +1022,12 @@ func (d *RocksDB) allAddressesScan(lower uint32, higher uint32) ([][]byte, [][]b
func (d *RocksDB) disconnectTxAddresses(wb *gorocksdb.WriteBatch, height uint32, txid string, inputs []outpoint, txa *TxAddresses,
txAddressesToUpdate map[string]*TxAddresses, balances map[string]*AddrBalance) error {
addresses := make(map[string]struct{})
getAddressBalance := func(addrID []byte) (*AddrBalance, error) {
getAddressBalance := func(addrDesc []byte) (*AddrBalance, error) {
var err error
s := string(addrID)
s := string(addrDesc)
b, fb := balances[s]
if !fb {
b, err = d.getAddrIDBalance(addrID)
b, err = d.getAddrDescBalance(addrDesc)
if err != nil {
return nil, err
}
@ -1038,13 +1036,13 @@ func (d *RocksDB) disconnectTxAddresses(wb *gorocksdb.WriteBatch, height uint32,
return b, nil
}
for i, t := range txa.Inputs {
if len(t.addrID) > 0 {
s := string(t.addrID)
if len(t.addrDesc) > 0 {
s := string(t.addrDesc)
_, exist := addresses[s]
if !exist {
addresses[s] = struct{}{}
}
b, err := getAddressBalance(t.addrID)
b, err := getAddressBalance(t.addrDesc)
if err != nil {
return err
}
@ -1055,12 +1053,12 @@ func (d *RocksDB) disconnectTxAddresses(wb *gorocksdb.WriteBatch, height uint32,
}
b.SentSat.Sub(&b.SentSat, &t.ValueSat)
if b.SentSat.Sign() < 0 {
d.resetValueSatToZero(&b.SentSat, t.addrID, "sent amount")
d.resetValueSatToZero(&b.SentSat, t.addrDesc, "sent amount")
}
b.BalanceSat.Add(&b.BalanceSat, &t.ValueSat)
} else {
ad, _ := d.chainParser.OutputScriptToAddresses(t.addrID)
had := hex.EncodeToString(t.addrID)
ad, _, _ := d.chainParser.GetAddressesFromAddrDesc(t.addrDesc)
had := hex.EncodeToString(t.addrDesc)
glog.Warningf("Balance for address %s (%s) not found", ad, had)
}
s = string(inputs[i].btxID)
@ -1076,13 +1074,13 @@ func (d *RocksDB) disconnectTxAddresses(wb *gorocksdb.WriteBatch, height uint32,
}
}
for _, t := range txa.Outputs {
if len(t.addrID) > 0 {
s := string(t.addrID)
if len(t.addrDesc) > 0 {
s := string(t.addrDesc)
_, exist := addresses[s]
if !exist {
addresses[s] = struct{}{}
}
b, err := getAddressBalance(t.addrID)
b, err := getAddressBalance(t.addrDesc)
if err != nil {
return err
}
@ -1093,11 +1091,11 @@ func (d *RocksDB) disconnectTxAddresses(wb *gorocksdb.WriteBatch, height uint32,
}
b.BalanceSat.Sub(&b.BalanceSat, &t.ValueSat)
if b.BalanceSat.Sign() < 0 {
d.resetValueSatToZero(&b.BalanceSat, t.addrID, "balance")
d.resetValueSatToZero(&b.BalanceSat, t.addrDesc, "balance")
}
} else {
ad, _ := d.chainParser.OutputScriptToAddresses(t.addrID)
had := hex.EncodeToString(t.addrID)
ad, _, _ := d.chainParser.GetAddressesFromAddrDesc(t.addrDesc)
had := hex.EncodeToString(t.addrDesc)
glog.Warningf("Balance for address %s (%s) not found", ad, had)
}
}
@ -1429,10 +1427,10 @@ func (d *RocksDB) ComputeInternalStateColumnStats(stopCompute chan os.Signal) er
// Helpers
func packAddressKey(addrID []byte, height uint32) []byte {
func packAddressKey(addrDesc []byte, height uint32) []byte {
bheight := packUint(height)
buf := make([]byte, 0, len(addrID)+len(bheight))
buf = append(buf, addrID...)
buf := make([]byte, 0, len(addrDesc)+len(bheight))
buf = append(buf, addrDesc...)
buf = append(buf, bheight...)
return buf
}

View File

@ -1,4 +1,4 @@
// build unittest
// +build unittest
package db
@ -59,7 +59,7 @@ func addressToPubKeyHex(addr string, t *testing.T, d *RocksDB) string {
if addr == "" {
return ""
}
b, err := d.chainParser.AddressToOutputScript(addr)
b, err := d.chainParser.GetAddrDescFromAddress(addr)
if err != nil {
t.Fatal(err)
}
@ -816,22 +816,22 @@ func TestRocksDB_Index_UTXO(t *testing.T) {
Height: 225494,
Inputs: []TxInput{
{
addrID: addressToOutput(addr3, d.chainParser),
addrDesc: addressToAddrDesc(addr3, d.chainParser),
ValueSat: *satB1T2A3,
},
{
addrID: addressToOutput(addr2, d.chainParser),
addrDesc: addressToAddrDesc(addr2, d.chainParser),
ValueSat: *satB1T1A2,
},
},
Outputs: []TxOutput{
{
addrID: addressToOutput(addr6, d.chainParser),
addrDesc: addressToAddrDesc(addr6, d.chainParser),
Spent: true,
ValueSat: *satB2T1A6,
},
{
addrID: addressToOutput(addr7, d.chainParser),
addrDesc: addressToAddrDesc(addr7, d.chainParser),
Spent: false,
ValueSat: *satB2T1A7,
},
@ -840,7 +840,7 @@ func TestRocksDB_Index_UTXO(t *testing.T) {
if !reflect.DeepEqual(ta, taw) {
t.Errorf("GetTxAddresses() = %+v, want %+v", ta, taw)
}
ia, err := ta.Inputs[0].Addresses(d.chainParser)
ia, _, err := ta.Inputs[0].Addresses(d.chainParser)
if err != nil {
t.Fatal(err)
}
@ -979,8 +979,8 @@ func Test_packBigint_unpackBigint(t *testing.T) {
}
}
func addressToOutput(addr string, parser bchain.BlockChainParser) []byte {
b, err := parser.AddressToOutputScript(addr)
func addressToAddrDesc(addr string, parser bchain.BlockChainParser) []byte {
b, err := parser.GetAddrDescFromAddress(addr)
if err != nil {
panic(err)
}
@ -1001,17 +1001,17 @@ func Test_packTxAddresses_unpackTxAddresses(t *testing.T) {
Height: 123,
Inputs: []TxInput{
{
addrID: addressToOutput("tb1qgw4vyzs3dcy75nmezjlpc40yc9a2vq9hghdyt2", parser),
addrDesc: addressToAddrDesc("tb1qgw4vyzs3dcy75nmezjlpc40yc9a2vq9hghdyt2", parser),
ValueSat: *big.NewInt(0),
},
{
addrID: addressToOutput("tb1q233n429a9e2jh48gnsq7w0qm0yz7kkzx0qczw8", parser),
addrDesc: addressToAddrDesc("tb1q233n429a9e2jh48gnsq7w0qm0yz7kkzx0qczw8", parser),
ValueSat: *big.NewInt(1234123421342341234),
},
},
Outputs: []TxOutput{
{
addrID: addressToOutput("tb1qgw4vyzs3dcy75nmezjlpc40yc9a2vq9hghdyt2", parser),
addrDesc: addressToAddrDesc("tb1qgw4vyzs3dcy75nmezjlpc40yc9a2vq9hghdyt2", parser),
ValueSat: *big.NewInt(1),
Spent: true,
},
@ -1025,39 +1025,39 @@ func Test_packTxAddresses_unpackTxAddresses(t *testing.T) {
Height: 12345,
Inputs: []TxInput{
{
addrID: addressToOutput("2N7iL7AvS4LViugwsdjTB13uN4T7XhV1bCP", parser),
addrDesc: addressToAddrDesc("2N7iL7AvS4LViugwsdjTB13uN4T7XhV1bCP", parser),
ValueSat: *big.NewInt(9011000000),
},
{
addrID: addressToOutput("2Mt9v216YiNBAzobeNEzd4FQweHrGyuRHze", parser),
addrDesc: addressToAddrDesc("2Mt9v216YiNBAzobeNEzd4FQweHrGyuRHze", parser),
ValueSat: *big.NewInt(8011000000),
},
{
addrID: addressToOutput("2NDyqJpHvHnqNtL1F9xAeCWMAW8WLJmEMyD", parser),
addrDesc: addressToAddrDesc("2NDyqJpHvHnqNtL1F9xAeCWMAW8WLJmEMyD", parser),
ValueSat: *big.NewInt(7011000000),
},
},
Outputs: []TxOutput{
{
addrID: addressToOutput("2MuwoFGwABMakU7DCpdGDAKzyj2nTyRagDP", parser),
addrDesc: addressToAddrDesc("2MuwoFGwABMakU7DCpdGDAKzyj2nTyRagDP", parser),
ValueSat: *big.NewInt(5011000000),
Spent: true,
},
{
addrID: addressToOutput("2Mvcmw7qkGXNWzkfH1EjvxDcNRGL1Kf2tEM", parser),
addrDesc: addressToAddrDesc("2Mvcmw7qkGXNWzkfH1EjvxDcNRGL1Kf2tEM", parser),
ValueSat: *big.NewInt(6011000000),
},
{
addrID: addressToOutput("2N9GVuX3XJGHS5MCdgn97gVezc6EgvzikTB", parser),
addrDesc: addressToAddrDesc("2N9GVuX3XJGHS5MCdgn97gVezc6EgvzikTB", parser),
ValueSat: *big.NewInt(7011000000),
Spent: true,
},
{
addrID: addressToOutput("mzii3fuRSpExMLJEHdHveW8NmiX8MPgavk", parser),
addrDesc: addressToAddrDesc("mzii3fuRSpExMLJEHdHveW8NmiX8MPgavk", parser),
ValueSat: *big.NewInt(999900000),
},
{
addrID: addressToOutput("mqHPFTRk23JZm9W1ANuEFtwTYwxjESSgKs", parser),
addrDesc: addressToAddrDesc("mqHPFTRk23JZm9W1ANuEFtwTYwxjESSgKs", parser),
ValueSat: *big.NewInt(5000000000),
Spent: true,
},
@ -1071,17 +1071,17 @@ func Test_packTxAddresses_unpackTxAddresses(t *testing.T) {
Height: 123456789,
Inputs: []TxInput{
{
addrID: []byte{},
addrDesc: []byte{},
ValueSat: *big.NewInt(1234),
},
},
Outputs: []TxOutput{
{
addrID: []byte{},
addrDesc: []byte{},
ValueSat: *big.NewInt(5678),
},
{
addrID: []byte{},
addrDesc: []byte{},
ValueSat: *big.NewInt(98),
Spent: true,
},

View File

@ -338,8 +338,7 @@ func (s *PublicServer) explorerAddress(r *http.Request) (tpl, *TemplateData, err
if ec != nil {
page = 0
}
addrID := r.URL.Path[i+1:]
address, err = s.api.GetAddress(addrID, page, txsOnPage, false)
address, err = s.api.GetAddress(r.URL.Path[i+1:], page, txsOnPage, false)
if err != nil {
return errorTpl, nil, err
}
@ -439,8 +438,7 @@ func (s *PublicServer) apiAddress(r *http.Request) (interface{}, error) {
if ec != nil {
page = 0
}
addrID := r.URL.Path[i+1:]
address, err = s.api.GetAddress(addrID, page, txsInAPI, true)
address, err = s.api.GetAddress(r.URL.Path[i+1:], page, txsInAPI, true)
}
return address, err
}

View File

@ -306,6 +306,29 @@ func txToResTx(tx *bchain.Tx, height int, hi []txInputs, ho []txOutputs) resTx {
}
}
func addressInSlice(s, t []string) string {
for _, sa := range s {
for _, ta := range t {
if ta == sa {
return sa
}
}
}
return ""
}
func (s *SocketIoServer) getAddressesFromVout(vout *bchain.Vout) ([]string, error) {
addrDesc, err := s.chainParser.GetAddrDescFromVout(vout)
if err != nil {
return nil, err
}
voutAddr, _, err := s.chainParser.GetAddressesFromAddrDesc(addrDesc)
if err != nil {
return nil, err
}
return voutAddr, nil
}
func (s *SocketIoServer) getAddressHistory(addr []string, opts *addrOpts) (res resultGetAddressHistory, err error) {
txr, err := s.getAddressTxids(addr, opts)
if err != nil {
@ -333,19 +356,23 @@ func (s *SocketIoServer) getAddressHistory(addr []string, opts *addrOpts) (res r
Satoshis: vout.ValueSat.String(),
Script: &aoh,
}
if vout.Address != nil {
a := vout.Address.String()
ao.Address = &a
if vout.Address.InSlice(addr) {
hi, ok := ads[a]
if ok {
hi.OutputIndexes = append(hi.OutputIndexes, int(vout.N))
} else {
hi := addressHistoryIndexes{}
hi.InputIndexes = make([]int, 0)
hi.OutputIndexes = append(hi.OutputIndexes, int(vout.N))
ads[a] = hi
}
voutAddr, err := s.getAddressesFromVout(&vout)
if err != nil {
return res, err
}
if len(voutAddr) > 0 {
ao.Address = &voutAddr[0]
}
a := addressInSlice(voutAddr, addr)
if a != "" {
hi, ok := ads[a]
if ok {
hi.OutputIndexes = append(hi.OutputIndexes, int(vout.N))
} else {
hi := addressHistoryIndexes{}
hi.InputIndexes = make([]int, 0)
hi.OutputIndexes = append(hi.OutputIndexes, int(vout.N))
ads[a] = hi
}
}
ho = append(ho, ao)
@ -587,9 +614,12 @@ func (s *SocketIoServer) getDetailedTransaction(txid string) (res resultGetDetai
}
if len(otx.Vout) > int(vin.Vout) {
vout := &otx.Vout[vin.Vout]
if vout.Address != nil {
a := vout.Address.String()
ai.Address = &a
voutAddr, err := s.getAddressesFromVout(vout)
if err != nil {
return res, err
}
if len(voutAddr) > 0 {
ai.Address = &voutAddr[0]
}
ai.Satoshis = vout.ValueSat.String()
}
@ -602,9 +632,12 @@ func (s *SocketIoServer) getDetailedTransaction(txid string) (res resultGetDetai
Satoshis: vout.ValueSat.String(),
Script: &aos,
}
if vout.Address != nil {
a := vout.Address.String()
ao.Address = &a
voutAddr, err := s.getAddressesFromVout(&vout)
if err != nil {
return res, err
}
if len(voutAddr) > 0 {
ao.Address = &voutAddr[0]
}
ho = append(ho, ao)
}

View File

@ -16,13 +16,16 @@
{{range $vin := $tx.Vin}}
<tr>
<td>
{{if $vin.Txid}}
<span class="float-left ellipsis">
{{if $vin.Addr}}{{if eq $vin.Addr $addr}}{{$vin.Addr}}{{else}}
<a href="/explorer/address/{{$vin.Addr}}">{{$vin.Addr}}</a>{{end}}{{else}}Unparsed address{{end}}
{{range $a := $vin.Addresses}}
<span class="ellipsis float-left">
{{if eq $a $addr}}{{$a}}{{else}}
<a href="/explorer/address/{{$a}}">{{$a}}</a>{{end}}
</span>
<span class="float-right{{if eq $vin.Addr $addr}} text-danger{{end}}">{{formatAmount $vin.Value}} {{$cs}}</span>
{{else}}No Inputs (Newly Generated Coins){{end}}
{{else}}
<span class="float-left">No Inputs (Newly Generated Coins)</span>
{{end}} {{if $vin.Addresses}}
<span class="float-right{{if stringInSlice $addr $vin.Addresses}} text-danger{{end}}">{{formatAmount $vin.Value}} {{$cs}}</span>
{{end}}
</td>
</tr>
{{end}}