Remove outputs spent by mempool tx from GetAddressUtxo
This commit is contained in:
parent
f1affaf92e
commit
446ba5cd9d
@ -478,35 +478,54 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, onlyTxids b
|
||||
}
|
||||
|
||||
// GetAddressUtxo returns unspent outputs for given address
|
||||
func (w *Worker) GetAddressUtxo(address string) ([]AddressUtxo, error) {
|
||||
func (w *Worker) GetAddressUtxo(address string, onlyConfirmed bool) ([]AddressUtxo, error) {
|
||||
start := time.Now()
|
||||
addrDesc, err := w.chainParser.GetAddrDescFromAddress(address)
|
||||
if err != nil {
|
||||
return nil, NewAPIError(fmt.Sprintf("Invalid address, %v", err), true)
|
||||
}
|
||||
spentInMempool := make(map[string]struct{})
|
||||
r := make([]AddressUtxo, 0, 8)
|
||||
// get utxo from mempool
|
||||
txm, err := w.getAddressTxids(addrDesc, true)
|
||||
if err != nil {
|
||||
return nil, errors.Annotatef(err, "getAddressTxids %v true", address)
|
||||
}
|
||||
txm = UniqueTxidsInReverse(txm)
|
||||
for _, txid := range txm {
|
||||
bchainTx, _, err := w.txCache.GetTransaction(txid)
|
||||
// mempool transaction may fail
|
||||
if !onlyConfirmed {
|
||||
// get utxo from mempool
|
||||
txm, err := w.getAddressTxids(addrDesc, true)
|
||||
if err != nil {
|
||||
glog.Error("GetTransaction in mempool ", txid, ": ", err)
|
||||
} else {
|
||||
for i := range bchainTx.Vout {
|
||||
bchainVout := &bchainTx.Vout[i]
|
||||
vad, err := w.chainParser.GetAddrDescFromVout(bchainVout)
|
||||
if err == nil && bytes.Equal(addrDesc, vad) {
|
||||
r = append(r, AddressUtxo{
|
||||
Txid: bchainTx.Txid,
|
||||
Vout: uint32(i),
|
||||
AmountSat: bchainVout.ValueSat,
|
||||
Amount: w.chainParser.AmountToDecimalString(&bchainVout.ValueSat),
|
||||
})
|
||||
return nil, errors.Annotatef(err, "getAddressTxids %v true", address)
|
||||
}
|
||||
txm = UniqueTxidsInReverse(txm)
|
||||
mc := make([]*bchain.Tx, len(txm))
|
||||
for i, txid := range txm {
|
||||
// get mempool txs and process their inputs to detect spends between mempool txs
|
||||
bchainTx, _, err := w.txCache.GetTransaction(txid)
|
||||
// mempool transaction may fail
|
||||
if err != nil {
|
||||
glog.Error("GetTransaction in mempool ", txid, ": ", err)
|
||||
} else {
|
||||
mc[i] = bchainTx
|
||||
// get outputs spent by the mempool tx
|
||||
for i := range bchainTx.Vin {
|
||||
vin := &bchainTx.Vin[i]
|
||||
spentInMempool[vin.Txid+strconv.Itoa(int(vin.Vout))] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, bchainTx := range mc {
|
||||
if bchainTx != nil {
|
||||
for i := range bchainTx.Vout {
|
||||
vout := &bchainTx.Vout[i]
|
||||
vad, err := w.chainParser.GetAddrDescFromVout(vout)
|
||||
if err == nil && bytes.Equal(addrDesc, vad) {
|
||||
// report only outpoints that are not spent in mempool
|
||||
_, e := spentInMempool[bchainTx.Txid+strconv.Itoa(i)]
|
||||
if !e {
|
||||
r = append(r, AddressUtxo{
|
||||
Txid: bchainTx.Txid,
|
||||
Vout: uint32(i),
|
||||
AmountSat: vout.ValueSat,
|
||||
Amount: w.chainParser.AmountToDecimalString(&vout.ValueSat),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -558,14 +577,18 @@ func (w *Worker) GetAddressUtxo(address string) ([]AddressUtxo, error) {
|
||||
} else {
|
||||
if !ta.Outputs[o.vout].Spent {
|
||||
v := ta.Outputs[o.vout].ValueSat
|
||||
r = append(r, AddressUtxo{
|
||||
Txid: o.txid,
|
||||
Vout: o.vout,
|
||||
AmountSat: v,
|
||||
Amount: w.chainParser.AmountToDecimalString(&v),
|
||||
Height: int(ta.Height),
|
||||
Confirmations: bestheight - int(ta.Height) + 1,
|
||||
})
|
||||
// report only outpoints that are not spent in mempool
|
||||
_, e := spentInMempool[o.txid+strconv.Itoa(int(o.vout))]
|
||||
if !e {
|
||||
r = append(r, AddressUtxo{
|
||||
Txid: o.txid,
|
||||
Vout: o.vout,
|
||||
AmountSat: v,
|
||||
Amount: w.chainParser.AmountToDecimalString(&v),
|
||||
Height: int(ta.Height),
|
||||
Confirmations: bestheight - int(ta.Height) + 1,
|
||||
})
|
||||
}
|
||||
checksum.Sub(&checksum, &v)
|
||||
}
|
||||
}
|
||||
|
||||
@ -691,7 +691,15 @@ func (s *PublicServer) apiAddressUtxo(r *http.Request) (interface{}, error) {
|
||||
var err error
|
||||
s.metrics.ExplorerViews.With(common.Labels{"action": "api-address"}).Inc()
|
||||
if i := strings.LastIndexByte(r.URL.Path, '/'); i > 0 {
|
||||
utxo, err = s.api.GetAddressUtxo(r.URL.Path[i+1:])
|
||||
onlyConfirmed := false
|
||||
c := r.URL.Query().Get("confirmed")
|
||||
if len(c) > 0 {
|
||||
onlyConfirmed, err = strconv.ParseBool(c)
|
||||
if err != nil {
|
||||
return nil, api.NewAPIError("Parameter 'confirmed' cannot be converted to boolean", true)
|
||||
}
|
||||
}
|
||||
utxo, err = s.api.GetAddressUtxo(r.URL.Path[i+1:], onlyConfirmed)
|
||||
}
|
||||
return utxo, err
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user