Handle error tx not found #94

This commit is contained in:
Martin Boehm 2019-01-10 16:39:36 +01:00
parent 8c4fcf4441
commit 3ca593aff1
11 changed files with 39 additions and 29 deletions

View File

@ -107,7 +107,10 @@ func (w *Worker) GetSpendingTxid(txid string, n int) (string, error) {
func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool) (*Tx, error) { func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool) (*Tx, error) {
bchainTx, height, err := w.txCache.GetTransaction(txid) bchainTx, height, err := w.txCache.GetTransaction(txid)
if err != nil { if err != nil {
return nil, NewAPIError(fmt.Sprintf("Tx not found, %v", err), true) if err == bchain.ErrTxNotFound {
return nil, NewAPIError(fmt.Sprintf("Transaction '%v' not found", txid), true)
}
return nil, NewAPIError(fmt.Sprintf("Transaction '%v' not found (%v)", txid, err), true)
} }
return w.GetTransactionFromBchainTx(bchainTx, height, spendingTxs, specificJSON) return w.GetTransactionFromBchainTx(bchainTx, height, spendingTxs, specificJSON)
} }
@ -158,6 +161,11 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height uint32,
// try to load from backend // try to load from backend
otx, _, err := w.txCache.GetTransaction(bchainVin.Txid) otx, _, err := w.txCache.GetTransaction(bchainVin.Txid)
if err != nil { if err != nil {
if err == bchain.ErrTxNotFound {
// try to get AddrDesc using coin specific handling and continue processing the tx
vin.AddrDesc = w.chainParser.GetAddrDescForUnknownInput(bchainTx, i)
continue
}
return nil, errors.Annotatef(err, "txCache.GetTransaction %v", bchainVin.Txid) return nil, errors.Annotatef(err, "txCache.GetTransaction %v", bchainVin.Txid)
} }
if len(otx.Vout) > int(vin.Vout) { if len(otx.Vout) > int(vin.Vout) {

View File

@ -28,12 +28,12 @@ func (p *BaseParser) ParseTx(b []byte) (*Tx, error) {
} }
// GetAddrDescForUnknownInput returns nil AddressDescriptor // GetAddrDescForUnknownInput returns nil AddressDescriptor
func (p *BaseParser) GetAddrDescForUnknownInput(block *Block, tx *Tx, input int) AddressDescriptor { func (p *BaseParser) GetAddrDescForUnknownInput(tx *Tx, input int) AddressDescriptor {
var iTxid string var iTxid string
if len(tx.Vin) > input { if len(tx.Vin) > input {
iTxid = tx.Vin[input].Txid iTxid = tx.Vin[input].Txid
} }
glog.Warningf("height %d, tx %v, input tx %v not found in txAddresses", block.Height, tx.Txid, iTxid) glog.Warningf("tx %v, input tx %v not found in txAddresses", tx.Txid, iTxid)
return nil return nil
} }

View File

@ -154,6 +154,7 @@ func (p *BitcoinParser) outputScriptToAddresses(script []byte) ([]string, bool,
return rv, s, nil return rv, s, nil
} }
// TxFromMsgTx converts bitcoin wire Tx to bchain.Tx
func (p *BitcoinParser) TxFromMsgTx(t *wire.MsgTx, parseAddresses bool) bchain.Tx { func (p *BitcoinParser) TxFromMsgTx(t *wire.MsgTx, parseAddresses bool) bchain.Tx {
vin := make([]bchain.Vin, len(t.TxIn)) vin := make([]bchain.Vin, len(t.TxIn))
for i, in := range t.TxIn { for i, in := range t.TxIn {

View File

@ -633,6 +633,13 @@ func (b *BitcoinRPC) GetMempool() ([]string, error) {
return res.Result, nil return res.Result, nil
} }
func isMissingTx(err *bchain.RPCError) bool {
if err.Code == -5 { // "No such mempool or blockchain transaction"
return true
}
return false
}
// GetTransactionForMempool returns a transaction by the transaction ID // GetTransactionForMempool returns a transaction by the transaction ID
// It could be optimized for mempool, i.e. without block time and confirmations // It could be optimized for mempool, i.e. without block time and confirmations
func (b *BitcoinRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) { func (b *BitcoinRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) {
@ -647,6 +654,9 @@ func (b *BitcoinRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) {
return nil, errors.Annotatef(err, "txid %v", txid) return nil, errors.Annotatef(err, "txid %v", txid)
} }
if res.Error != nil { if res.Error != nil {
if isMissingTx(res.Error) {
return nil, bchain.ErrTxNotFound
}
return nil, errors.Annotatef(res.Error, "txid %v", txid) return nil, errors.Annotatef(res.Error, "txid %v", txid)
} }
data, err := hex.DecodeString(res.Result) data, err := hex.DecodeString(res.Result)
@ -696,6 +706,9 @@ func (b *BitcoinRPC) getRawTransaction(txid string) (json.RawMessage, error) {
return nil, errors.Annotatef(err, "txid %v", txid) return nil, errors.Annotatef(err, "txid %v", txid)
} }
if res.Error != nil { if res.Error != nil {
if isMissingTx(res.Error) {
return nil, bchain.ErrTxNotFound
}
return nil, errors.Annotatef(res.Error, "txid %v", txid) return nil, errors.Annotatef(res.Error, "txid %v", txid)
} }
return res.Result, nil return res.Result, nil

View File

@ -520,7 +520,7 @@ func (b *EthereumRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) {
func (b *EthereumRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) { func (b *EthereumRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) {
tx, err := b.GetTransaction(txid) tx, err := b.GetTransaction(txid)
// it there is an error getting the tx or the tx is confirmed, remove it from pending transactions // it there is an error getting the tx or the tx is confirmed, remove it from pending transactions
if err != nil || (tx != nil && tx.Confirmations > 0) { if err == bchain.ErrTxNotFound || (tx != nil && tx.Confirmations > 0) {
b.pendingTransactionsLock.Lock() b.pendingTransactionsLock.Lock()
delete(b.pendingTransactions, txid) delete(b.pendingTransactions, txid)
b.pendingTransactionsLock.Unlock() b.pendingTransactionsLock.Unlock()
@ -538,7 +538,7 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} else if tx == nil { } else if tx == nil {
return nil, ethereum.NotFound return nil, bchain.ErrTxNotFound
} }
var btx *bchain.Tx var btx *bchain.Tx
if tx.BlockNumber == "" { if tx.BlockNumber == "" {

View File

@ -74,7 +74,7 @@ func (p *LiquidParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
// GetAddrDescForUnknownInput processes inputs that were not found in txAddresses - they are bitcoin transactions // GetAddrDescForUnknownInput processes inputs that were not found in txAddresses - they are bitcoin transactions
// create a special script for the input in the form OP_INVALIDOPCODE <txid> <vout varint> // create a special script for the input in the form OP_INVALIDOPCODE <txid> <vout varint>
func (p *LiquidParser) GetAddrDescForUnknownInput(block *bchain.Block, tx *bchain.Tx, input int) bchain.AddressDescriptor { func (p *LiquidParser) GetAddrDescForUnknownInput(tx *bchain.Tx, input int) bchain.AddressDescriptor {
var iTxid string var iTxid string
s := make([]byte, 0, 40) s := make([]byte, 0, 40)
if len(tx.Vin) > input { if len(tx.Vin) > input {
@ -88,7 +88,7 @@ func (p *LiquidParser) GetAddrDescForUnknownInput(block *bchain.Block, tx *bchai
s = append(s, buf[:l]...) s = append(s, buf[:l]...)
} }
} }
glog.Info("height ", block.Height, ", tx ", tx.Txid, ", encountered Bitcoin tx ", iTxid) glog.Info("tx ", tx.Txid, ", encountered Bitcoin tx ", iTxid)
return s return s
} }

View File

@ -31,6 +31,8 @@ var (
// ErrTxidMissing is returned if txid is not specified // ErrTxidMissing is returned if txid is not specified
// for example coinbase transactions in Bitcoin // for example coinbase transactions in Bitcoin
ErrTxidMissing = errors.New("Txid missing") ErrTxidMissing = errors.New("Txid missing")
// ErrTxNotFound is returned if transaction was not found
ErrTxNotFound = errors.New("Tx not found")
) )
// Outpoint is txid together with output (or input) index // Outpoint is txid together with output (or input) index
@ -256,7 +258,7 @@ type BlockChainParser interface {
ParseTxFromJson(json.RawMessage) (*Tx, error) ParseTxFromJson(json.RawMessage) (*Tx, error)
PackTx(tx *Tx, height uint32, blockTime int64) ([]byte, error) PackTx(tx *Tx, height uint32, blockTime int64) ([]byte, error)
UnpackTx(buf []byte) (*Tx, uint32, error) UnpackTx(buf []byte) (*Tx, uint32, error)
GetAddrDescForUnknownInput(block *Block, tx *Tx, input int) AddressDescriptor GetAddrDescForUnknownInput(tx *Tx, input int) AddressDescriptor
// blocks // blocks
PackBlockHash(hash string) ([]byte, error) PackBlockHash(hash string) ([]byte, error)
UnpackBlockHash(buf []byte) (string, error) UnpackBlockHash(buf []byte) (string, error)

View File

@ -491,7 +491,7 @@ func (d *RocksDB) processAddressesBitcoinType(block *bchain.Block, addresses add
} }
if ita == nil { if ita == nil {
// allow parser to process unknown input, some coins may implement special handling, default is to log warning // allow parser to process unknown input, some coins may implement special handling, default is to log warning
tai.AddrDesc = d.chainParser.GetAddrDescForUnknownInput(block, tx, i) tai.AddrDesc = d.chainParser.GetAddrDescForUnknownInput(tx, i)
continue continue
} }
txAddressesMap[stxID] = ita txAddressesMap[stxID] = ita

View File

@ -19,7 +19,7 @@ import (
"github.com/golang/glog" "github.com/golang/glog"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"github.com/martinboehm/golang-socketio" gosocketio "github.com/martinboehm/golang-socketio"
"github.com/martinboehm/golang-socketio/transport" "github.com/martinboehm/golang-socketio/transport"
) )
@ -388,7 +388,7 @@ func httpTests_BitcoinType(t *testing.T, ts *httptest.Server) {
status: http.StatusBadRequest, status: http.StatusBadRequest,
contentType: "application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
body: []string{ body: []string{
`{"error":"Tx not found, Not found"}`, `{"error":"Transaction '1232e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07' not found"}`,
}, },
}, },
{ {
@ -406,7 +406,7 @@ func httpTests_BitcoinType(t *testing.T, ts *httptest.Server) {
status: http.StatusBadRequest, status: http.StatusBadRequest,
contentType: "application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
body: []string{ body: []string{
`{"error":"Tx not found, Not found"}`, `{"error":"Transaction '1232e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07' not found"}`,
}, },
}, },
{ {

View File

@ -138,7 +138,7 @@ func (c *fakeBlockChain) GetTransaction(txid string) (v *bchain.Tx, err error) {
if v != nil { if v != nil {
return v, nil return v, nil
} }
return nil, errors.New("Not found") return nil, bchain.ErrTxNotFound
} }
func (c *fakeBlockChain) GetTransactionSpecific(tx *bchain.Tx) (v json.RawMessage, err error) { func (c *fakeBlockChain) GetTransactionSpecific(tx *bchain.Tx) (v json.RawMessage, err error) {

View File

@ -11,7 +11,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/deckarep/golang-set" mapset "github.com/deckarep/golang-set"
"github.com/juju/errors" "github.com/juju/errors"
) )
@ -351,7 +351,7 @@ func getTxid2addrs(t *testing.T, h *TestHandler, txs []string) map[string][]stri
for i := range txs { for i := range txs {
tx, err := h.Chain.GetTransactionForMempool(txs[i]) tx, err := h.Chain.GetTransactionForMempool(txs[i])
if err != nil { if err != nil {
if isMissingTx(err) { if err == bchain.ErrTxNotFound {
continue continue
} }
t.Fatal(err) t.Fatal(err)
@ -370,20 +370,6 @@ func getTxid2addrs(t *testing.T, h *TestHandler, txs []string) map[string][]stri
return txid2addrs return txid2addrs
} }
func isMissingTx(err error) bool {
switch e1 := err.(type) {
case *errors.Err:
switch e2 := e1.Cause().(type) {
case *bchain.RPCError:
if e2.Code == -5 { // "No such mempool or blockchain transaction"
return true
}
}
}
return false
}
func intersect(a, b []string) []string { func intersect(a, b []string) []string {
setA := mapset.NewSet() setA := mapset.NewSet()
for _, v := range a { for _, v := range a {