Include mempool transactions in GetAddressForXpub

This commit is contained in:
Martin Boehm 2019-02-08 00:42:38 +01:00
parent 1ca4a0cfc7
commit 273436f109
2 changed files with 116 additions and 91 deletions

View File

@ -107,7 +107,7 @@ func (w *Worker) xpubGetAddressTxids(addrDesc bchain.AddressDescriptor, mempool
return txs, complete, nil return txs, complete, nil
} }
func (w *Worker) xpubCheckAndLoadTxids(ad *xpubAddress, filter *AddressFilter, maxHeight uint32, pageSize int) error { func (w *Worker) xpubCheckAndLoadTxids(ad *xpubAddress, filter *AddressFilter, maxHeight uint32, maxResults int) error {
// skip if not discovered // skip if not discovered
if ad.balance == nil { if ad.balance == nil {
return nil return nil
@ -130,9 +130,9 @@ func (w *Worker) xpubCheckAndLoadTxids(ad *xpubAddress, filter *AddressFilter, m
} }
// unless the filter is completely off, load all txids // unless the filter is completely off, load all txids
if filter.FromHeight != 0 || filter.ToHeight != 0 || filter.Vout != AddressFilterVoutOff { if filter.FromHeight != 0 || filter.ToHeight != 0 || filter.Vout != AddressFilterVoutOff {
pageSize = maxInt maxResults = maxInt
} }
newTxids, complete, err := w.xpubGetAddressTxids(ad.addrDesc, false, 0, maxHeight, pageSize) newTxids, complete, err := w.xpubGetAddressTxids(ad.addrDesc, false, 0, maxHeight, maxResults)
if err != nil { if err != nil {
return err return err
} }
@ -245,9 +245,13 @@ func (w *Worker) GetAddressForXpub(xpub string, page int, txsOnPage int, option
cachedXpubsMux.Lock() cachedXpubsMux.Lock()
data, found := cachedXpubs[xpub] data, found := cachedXpubs[xpub]
cachedXpubsMux.Unlock() cachedXpubsMux.Unlock()
type mempoolMap struct {
tx *Tx
inputOutput byte
}
var ( var (
txc xpubTxids txc xpubTxids
txm []string txmMap map[string]*Tx
txs []*Tx txs []*Tx
txids []string txids []string
pg Paging pg Paging
@ -255,6 +259,7 @@ func (w *Worker) GetAddressForXpub(xpub string, page int, txsOnPage int, option
err error err error
bestheight uint32 bestheight uint32
besthash string besthash string
uBalSat big.Int
) )
// to load all data for xpub may take some time, do it in a loop to process a possible new block // to load all data for xpub may take some time, do it in a loop to process a possible new block
for { for {
@ -302,14 +307,11 @@ func (w *Worker) GetAddressForXpub(xpub string, page int, txsOnPage int, option
glog.Info("Scanned ", len(data.addresses)+len(data.changeAddresses), " addresses in ", time.Since(start)) glog.Info("Scanned ", len(data.addresses)+len(data.changeAddresses), " addresses in ", time.Since(start))
} }
if option >= TxidHistory { if option >= TxidHistory {
for i := range data.addresses { for _, da := range [][]xpubAddress{data.addresses, data.changeAddresses} {
if err = w.xpubCheckAndLoadTxids(&data.addresses[i], filter, bestheight, (page+1)*txsOnPage); err != nil { for i := range da {
return nil, err if err = w.xpubCheckAndLoadTxids(&da[i], filter, bestheight, (page+1)*txsOnPage); err != nil {
} return nil, err
} }
for i := range data.changeAddresses {
if err = w.xpubCheckAndLoadTxids(&data.changeAddresses[i], filter, bestheight, (page+1)*txsOnPage); err != nil {
return nil, err
} }
} }
} }
@ -317,41 +319,85 @@ func (w *Worker) GetAddressForXpub(xpub string, page int, txsOnPage int, option
cachedXpubsMux.Lock() cachedXpubsMux.Lock()
cachedXpubs[xpub] = data cachedXpubs[xpub] = data
cachedXpubsMux.Unlock() cachedXpubsMux.Unlock()
// TODO mempool // setup filtering of txids
if option >= TxidHistory { var useTxids func(txid *xpubTxid, ad *xpubAddress) bool
txc = make(xpubTxids, 0, 32) var addTxids func(ad *xpubAddress)
var addTxids func(ad *xpubAddress) if filter.FromHeight == 0 && filter.ToHeight == 0 && filter.Vout == AddressFilterVoutOff {
if filter.FromHeight == 0 && filter.ToHeight == 0 && filter.Vout == AddressFilterVoutOff { addTxids = func(ad *xpubAddress) {
addTxids = func(ad *xpubAddress) { txc = append(txc, ad.txids...)
txc = append(txc, ad.txids...) }
totalResults = int(data.txs)
} else {
toHeight := maxUint32
if filter.ToHeight != 0 {
toHeight = filter.ToHeight
}
useTxids = func(txid *xpubTxid, ad *xpubAddress) bool {
if txid.height < filter.FromHeight || txid.height > toHeight {
return false
} }
totalResults = int(data.txs) if filter.Vout != AddressFilterVoutOff {
} else { if filter.Vout == AddressFilterVoutInputs && txid.inputOutput&txInput == 0 ||
toHeight := maxUint32 filter.Vout == AddressFilterVoutOutputs && txid.inputOutput&txOutput == 0 {
if filter.ToHeight != 0 { return false
toHeight = filter.ToHeight }
} }
addTxids = func(ad *xpubAddress) { return true
for _, txid := range ad.txids { }
if txid.height < filter.FromHeight || txid.height > toHeight { addTxids = func(ad *xpubAddress) {
continue for _, txid := range ad.txids {
} if useTxids(&txid, ad) {
if filter.Vout != AddressFilterVoutOff {
if filter.Vout == AddressFilterVoutInputs && txid.inputOutput&txInput == 0 ||
filter.Vout == AddressFilterVoutOutputs && txid.inputOutput&txOutput == 0 {
continue
}
}
txc = append(txc, txid) txc = append(txc, txid)
} }
} }
totalResults = -1
} }
for i := range data.addresses { totalResults = -1
addTxids(&data.addresses[i]) }
// process mempool, only if blockheight filter is off
if filter.FromHeight == 0 && filter.ToHeight == 0 {
txmMap = make(map[string]*Tx)
for _, da := range [][]xpubAddress{data.addresses, data.changeAddresses} {
for i := range da {
ad := &da[i]
newTxids, _, err := w.xpubGetAddressTxids(ad.addrDesc, true, 0, 0, maxInt)
if err != nil {
return nil, err
}
for _, txid := range newTxids {
// the same tx can have multiple addresses from the same xpub, get it from backend it only once
tx, foundTx := txmMap[txid.txid]
if !foundTx {
tx, err = w.GetTransaction(txid.txid, false, false)
// mempool transaction may fail
if err != nil || tx == nil {
glog.Warning("GetTransaction in mempool: ", err)
continue
}
txmMap[txid.txid] = tx
}
// skip already confirmed txs, mempool may be out of sync
if tx.Confirmations == 0 {
uBalSat.Add(&uBalSat, tx.getAddrVoutValue(ad.addrDesc))
uBalSat.Sub(&uBalSat, tx.getAddrVinValue(ad.addrDesc))
if page == 0 && !foundTx && (useTxids == nil || useTxids(&txid, ad)) {
if option == TxidHistory {
txids = append(txids, tx.Txid)
} else if option >= TxHistoryLight {
txs = append(txs, tx)
}
}
}
}
}
} }
for i := range data.changeAddresses { }
addTxids(&data.changeAddresses[i]) if option >= TxidHistory {
txc = make(xpubTxids, 0, 32)
for _, da := range [][]xpubAddress{data.addresses, data.changeAddresses} {
for i := range da {
addTxids(&da[i])
}
} }
sort.Stable(txc) sort.Stable(txc)
var from, to int var from, to int
@ -363,58 +409,37 @@ func (w *Worker) GetAddressForXpub(xpub string, page int, txsOnPage int, option
pg, _, _, _ = computePaging(totalResults, page, txsOnPage) pg, _, _, _ = computePaging(totalResults, page, txsOnPage)
} }
} }
if option == TxidHistory {
txids = make([]string, len(txm)+to-from)
} else {
txs = make([]*Tx, len(txm)+to-from)
}
txi := 0
// get confirmed transactions // get confirmed transactions
for i := from; i < to; i++ { for i := from; i < to; i++ {
xpubTxid := &txc[i] xpubTxid := &txc[i]
if option == TxidHistory { if option == TxidHistory {
txids[txi] = xpubTxid.txid txids = append(txids, xpubTxid.txid)
} else { } else {
if txs[txi], err = w.txFromTxid(xpubTxid.txid, bestheight, option); err != nil { tx, err := w.txFromTxid(xpubTxid.txid, bestheight, option)
if err != nil {
return nil, err return nil, err
} }
txs = append(txs, tx)
} }
txi++
}
if option == TxidHistory {
txids = txids[:txi]
} else if option >= TxHistoryLight {
txs = txs[:txi]
} }
} }
totalTokens := 0 totalTokens := 0
xpubAddresses := make(map[string]struct{}) xpubAddresses := make(map[string]struct{})
tokens := make([]Token, 0, 4) tokens := make([]Token, 0, 4)
for i := range data.addresses { for ci, da := range [][]xpubAddress{data.addresses, data.changeAddresses} {
ad := &data.addresses[i] for i := range da {
if ad.balance != nil { ad := &da[i]
totalTokens++ var t *Token
if filter.AllTokens || !IsZeroBigInt(&ad.balance.BalanceSat) { if ad.balance != nil {
t := w.tokenFromXpubAddress(data, ad, 0, i) totalTokens++
tokens = append(tokens, t) if filter.AllTokens || !IsZeroBigInt(&ad.balance.BalanceSat) {
xpubAddresses[t.Name] = struct{}{} token := w.tokenFromXpubAddress(data, ad, ci, i)
} else { tokens = append(tokens, token)
a, _, _ := w.chainParser.GetAddressesFromAddrDesc(ad.addrDesc) t = &token
if len(a) > 0 { xpubAddresses[t.Name] = struct{}{}
xpubAddresses[a[0]] = struct{}{}
} }
} }
} if t == nil {
}
for i := range data.changeAddresses {
ad := &data.changeAddresses[i]
if ad.balance != nil {
totalTokens++
if filter.AllTokens || !IsZeroBigInt(&ad.balance.BalanceSat) {
t := w.tokenFromXpubAddress(data, ad, 1, i)
tokens = append(tokens, t)
xpubAddresses[t.Name] = struct{}{}
} else {
a, _, _ := w.chainParser.GetAddressesFromAddrDesc(ad.addrDesc) a, _, _ := w.chainParser.GetAddressesFromAddrDesc(ad.addrDesc)
if len(a) > 0 { if len(a) > 0 {
xpubAddresses[a[0]] = struct{}{} xpubAddresses[a[0]] = struct{}{}
@ -425,19 +450,19 @@ func (w *Worker) GetAddressForXpub(xpub string, page int, txsOnPage int, option
var totalReceived big.Int var totalReceived big.Int
totalReceived.Add(&data.balanceSat, &data.sentSat) totalReceived.Add(&data.balanceSat, &data.sentSat)
addr := Address{ addr := Address{
Paging: pg, Paging: pg,
AddrStr: xpub, AddrStr: xpub,
BalanceSat: (*Amount)(&data.balanceSat), BalanceSat: (*Amount)(&data.balanceSat),
TotalReceivedSat: (*Amount)(&totalReceived), TotalReceivedSat: (*Amount)(&totalReceived),
TotalSentSat: (*Amount)(&data.sentSat), TotalSentSat: (*Amount)(&data.sentSat),
Txs: int(data.txs), Txs: int(data.txs),
// UnconfirmedBalanceSat: (*Amount)(&uBalSat), UnconfirmedBalanceSat: (*Amount)(&uBalSat),
// UnconfirmedTxs: len(txm), UnconfirmedTxs: len(txmMap),
Transactions: txs, Transactions: txs,
Txids: txids, Txids: txids,
TotalTokens: totalTokens, TotalTokens: totalTokens,
Tokens: tokens, Tokens: tokens,
XPubAddresses: xpubAddresses, XPubAddresses: xpubAddresses,
} }
glog.Info("GetAddressForXpub ", xpub[:16], ", ", len(data.addresses)+len(data.changeAddresses), " derived addresses, ", data.txs, " total txs, loaded ", len(txc), " txids, finished in ", time.Since(start)) glog.Info("GetAddressForXpub ", xpub[:16], ", ", len(data.addresses)+len(data.changeAddresses), " derived addresses, ", data.txs, " total txs, loaded ", len(txc), " txids, finished in ", time.Since(start))
return &addr, nil return &addr, nil

View File

@ -183,7 +183,7 @@ h3 {
} }
.tx-own { .tx-own {
background-color: #fff6e6; background-color: #fbf8f0;
} }
.tx-amt { .tx-amt {