Include mempool transactions in GetAddressForXpub
This commit is contained in:
parent
1ca4a0cfc7
commit
273436f109
205
api/xpub.go
205
api/xpub.go
@ -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
|
||||||
|
|||||||
@ -183,7 +183,7 @@ h3 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tx-own {
|
.tx-own {
|
||||||
background-color: #fff6e6;
|
background-color: #fbf8f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tx-amt {
|
.tx-amt {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user