diff --git a/api/types.go b/api/types.go index e5c734e4..5e465ecf 100644 --- a/api/types.go +++ b/api/types.go @@ -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"` diff --git a/api/worker.go b/api/worker.go index ec5dc49f..397b1247 100644 --- a/api/worker.go +++ b/api/worker.go @@ -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 -} diff --git a/bchain/baseparser.go b/bchain/baseparser.go index 172a8180..48bb752e 100644 --- a/bchain/baseparser.go +++ b/bchain/baseparser.go @@ -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 -} diff --git a/bchain/coins/bch/bcashparser.go b/bchain/coins/bch/bcashparser.go index 02286ca6..ccfebc29 100644 --- a/bchain/coins/bch/bcashparser.go +++ b/bchain/coins/bch/bcashparser.go @@ -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 { diff --git a/bchain/coins/bch/bcashparser_test.go b/bchain/coins/bch/bcashparser_test.go index d6040b82..ef2867fe 100644 --- a/bchain/coins/bch/bcashparser_test.go +++ b/bchain/coins/bch/bcashparser_test.go @@ -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, }, }, } diff --git a/bchain/coins/btc/bitcoinparser.go b/bchain/coins/btc/bitcoinparser.go index 6e2f4c69..f741adf9 100644 --- a/bchain/coins/btc/bitcoinparser.go +++ b/bchain/coins/btc/bitcoinparser.go @@ -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 } diff --git a/bchain/coins/btc/bitcoinparser_test.go b/bchain/coins/btc/bitcoinparser_test.go index 79d02816..4b4308a1 100644 --- a/bchain/coins/btc/bitcoinparser_test.go +++ b/bchain/coins/btc/bitcoinparser_test.go @@ -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, }, }, } diff --git a/bchain/coins/dogecoin/dogecoinparser_test.go b/bchain/coins/dogecoin/dogecoinparser_test.go index 0b736c51..f9c1037e 100644 --- a/bchain/coins/dogecoin/dogecoinparser_test.go +++ b/bchain/coins/dogecoin/dogecoinparser_test.go @@ -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, }, }, } diff --git a/bchain/coins/eth/ethparser.go b/bchain/coins/eth/ethparser.go index 82d08153..f933b761 100644 --- a/bchain/coins/eth/ethparser.go +++ b/bchain/coins/eth/ethparser.go @@ -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 { diff --git a/bchain/coins/eth/ethparser_test.go b/bchain/coins/eth/ethparser_test.go index 40069a55..2a7ee8bf 100644 --- a/bchain/coins/eth/ethparser_test.go +++ b/bchain/coins/eth/ethparser_test.go @@ -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, }, }, } diff --git a/bchain/coins/litecoin/litecoinparser_test.go b/bchain/coins/litecoin/litecoinparser_test.go index a76faf63..1e046bcd 100644 --- a/bchain/coins/litecoin/litecoinparser_test.go +++ b/bchain/coins/litecoin/litecoinparser_test.go @@ -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, }, }, } diff --git a/bchain/coins/monacoin/monacoin_test.go b/bchain/coins/monacoin/monacoin_test.go index 6e41db33..9f559749 100644 --- a/bchain/coins/monacoin/monacoin_test.go +++ b/bchain/coins/monacoin/monacoin_test.go @@ -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, }, }, } diff --git a/bchain/coins/monacoin/monacoinparser.go b/bchain/coins/monacoin/monacoinparser.go index 1268e843..88d0870c 100644 --- a/bchain/coins/monacoin/monacoinparser.go +++ b/bchain/coins/monacoin/monacoinparser.go @@ -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) diff --git a/bchain/coins/namecoin/namecoinparser_test.go b/bchain/coins/namecoin/namecoinparser_test.go index 0f22015d..dcf34b5f 100644 --- a/bchain/coins/namecoin/namecoinparser_test.go +++ b/bchain/coins/namecoin/namecoinparser_test.go @@ -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) } }) } diff --git a/bchain/coins/vertcoin/vertcoinparser_test.go b/bchain/coins/vertcoin/vertcoinparser_test.go index 0cbf01d5..5cdcb5c6 100644 --- a/bchain/coins/vertcoin/vertcoinparser_test.go +++ b/bchain/coins/vertcoin/vertcoinparser_test.go @@ -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, }, }, } diff --git a/bchain/coins/zec/zcashparser.go b/bchain/coins/zec/zcashparser.go index dc2d84d9..84f422db 100644 --- a/bchain/coins/zec/zcashparser.go +++ b/bchain/coins/zec/zcashparser.go @@ -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") +} diff --git a/bchain/coins/zec/zcashparser_test.go b/bchain/coins/zec/zcashparser_test.go index b1bfd773..c5e02b5c 100644 --- a/bchain/coins/zec/zcashparser_test.go +++ b/bchain/coins/zec/zcashparser_test.go @@ -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, }, }, } diff --git a/bchain/mempool_nonutxo.go b/bchain/mempool_nonutxo.go index 8ccf018c..3d1aa66f 100644 --- a/bchain/mempool_nonutxo.go +++ b/bchain/mempool_nonutxo.go @@ -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 } diff --git a/bchain/mempool_utxo.go b/bchain/mempool_utxo.go index 1963bc19..f89daa29 100644 --- a/bchain/mempool_utxo.go +++ b/bchain/mempool_utxo.go @@ -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 diff --git a/bchain/tests/rpc/rpc.go b/bchain/tests/rpc/rpc.go index 20483573..bc7960a8 100644 --- a/bchain/tests/rpc/rpc.go +++ b/bchain/tests/rpc/rpc.go @@ -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 } } } diff --git a/bchain/types.go b/bchain/types.go index ba631310..50d69f9a 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -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) diff --git a/db/rocksdb.go b/db/rocksdb.go index de498388..82c7d46d 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -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 } diff --git a/db/rocksdb_test.go b/db/rocksdb_test.go index 3b21b93d..e1785fda 100644 --- a/db/rocksdb_test.go +++ b/db/rocksdb_test.go @@ -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, }, diff --git a/server/public.go b/server/public.go index 36556850..9b9800b7 100644 --- a/server/public.go +++ b/server/public.go @@ -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 } diff --git a/server/socketio.go b/server/socketio.go index 74fbd67b..9d36f2e4 100644 --- a/server/socketio.go +++ b/server/socketio.go @@ -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) } diff --git a/static/templates/txdetail.html b/static/templates/txdetail.html index 87ebd673..2c70c912 100644 --- a/static/templates/txdetail.html +++ b/static/templates/txdetail.html @@ -16,13 +16,16 @@ {{range $vin := $tx.Vin}} - {{if $vin.Txid}} - - {{if $vin.Addr}}{{if eq $vin.Addr $addr}}{{$vin.Addr}}{{else}} - {{$vin.Addr}}{{end}}{{else}}Unparsed address{{end}} + {{range $a := $vin.Addresses}} + + {{if eq $a $addr}}{{$a}}{{else}} + {{$a}}{{end}} - {{formatAmount $vin.Value}} {{$cs}} - {{else}}No Inputs (Newly Generated Coins){{end}} + {{else}} + No Inputs (Newly Generated Coins) + {{end}} {{if $vin.Addresses}} + {{formatAmount $vin.Value}} {{$cs}} + {{end}} {{end}}