Handle error tx not found #94
This commit is contained in:
parent
8c4fcf4441
commit
3ca593aff1
@ -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) {
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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 == "" {
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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"}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user