diff --git a/api/worker.go b/api/worker.go index f7572058..e30f3bdd 100644 --- a/api/worker.go +++ b/api/worker.go @@ -20,6 +20,7 @@ type Worker struct { txCache *db.TxCache chain bchain.BlockChain chainParser bchain.BlockChainParser + chainType bchain.ChainType is *common.InternalState } @@ -30,6 +31,7 @@ func NewWorker(db *db.RocksDB, chain bchain.BlockChain, txCache *db.TxCache, is txCache: txCache, chain: chain, chainParser: chain.GetChainParser(), + chainType: chain.GetChainParser().GetChainType(), is: is, } return w, nil @@ -102,9 +104,12 @@ func (w *Worker) GetTransaction(txid string, spendingTxs bool) (*Tx, error) { if err != nil { return nil, NewAPIError(fmt.Sprintf("Tx not found, %v", err), true) } - ta, err := w.db.GetTxAddresses(txid) - if err != nil { - return nil, errors.Annotatef(err, "GetTxAddresses %v", txid) + var ta *db.TxAddresses + if w.chainType == bchain.ChainBitcoinType { + ta, err = w.db.GetTxAddresses(txid) + if err != nil { + return nil, errors.Annotatef(err, "GetTxAddresses %v", txid) + } } var blockhash string if bchainTx.Confirmations > 0 { @@ -123,45 +128,56 @@ func (w *Worker) GetTransaction(txid string, spendingTxs bool) (*Tx, error) { vin.Vout = bchainVin.Vout vin.Sequence = int64(bchainVin.Sequence) vin.ScriptSig.Hex = bchainVin.ScriptSig.Hex - // bchainVin.Txid=="" is coinbase transaction - if bchainVin.Txid != "" { - // load spending addresses from TxAddresses - tas, err := w.db.GetTxAddresses(bchainVin.Txid) - if err != nil { - return nil, errors.Annotatef(err, "GetTxAddresses %v", bchainVin.Txid) - } - if tas == nil { - // mempool transactions are not in TxAddresses but confirmed should be there, log a problem - if bchainTx.Confirmations > 0 { - glog.Warning("DB inconsistency: tx ", bchainVin.Txid, ": not found in txAddresses") - } - // try to load from backend - otx, _, err := w.txCache.GetTransaction(bchainVin.Txid) + if w.chainType == bchain.ChainBitcoinType { + // bchainVin.Txid=="" is coinbase transaction + if bchainVin.Txid != "" { + // load spending addresses from TxAddresses + tas, err := w.db.GetTxAddresses(bchainVin.Txid) if err != nil { - return nil, errors.Annotatef(err, "txCache.GetTransaction %v", bchainVin.Txid) + return nil, errors.Annotatef(err, "GetTxAddresses %v", bchainVin.Txid) } - if len(otx.Vout) > int(vin.Vout) { - vout := &otx.Vout[vin.Vout] - vin.ValueSat = vout.ValueSat - vin.AddrDesc, vin.Addresses, vin.Searchable, err = w.getAddressesFromVout(vout) + if tas == nil { + // mempool transactions are not in TxAddresses but confirmed should be there, log a problem + if bchainTx.Confirmations > 0 { + glog.Warning("DB inconsistency: tx ", bchainVin.Txid, ": not found in txAddresses") + } + // try to load from backend + otx, _, err := w.txCache.GetTransaction(bchainVin.Txid) if err != nil { - glog.Errorf("getAddressesFromVout error %v, vout %+v", err, vout) - } - } - } else { - if len(tas.Outputs) > int(vin.Vout) { - output := &tas.Outputs[vin.Vout] - vin.ValueSat = output.ValueSat - vin.Value = w.chainParser.AmountToDecimalString(&vin.ValueSat) - vin.AddrDesc = output.AddrDesc - vin.Addresses, vin.Searchable, err = output.Addresses(w.chainParser) - if err != nil { - glog.Errorf("output.Addresses error %v, tx %v, output %v", err, bchainVin.Txid, i) + return nil, errors.Annotatef(err, "txCache.GetTransaction %v", bchainVin.Txid) + } + if len(otx.Vout) > int(vin.Vout) { + vout := &otx.Vout[vin.Vout] + vin.ValueSat = vout.ValueSat + vin.AddrDesc, vin.Addresses, vin.Searchable, err = w.getAddressesFromVout(vout) + if err != nil { + glog.Errorf("getAddressesFromVout error %v, vout %+v", err, vout) + } + } + } else { + if len(tas.Outputs) > int(vin.Vout) { + output := &tas.Outputs[vin.Vout] + vin.ValueSat = output.ValueSat + vin.Value = w.chainParser.AmountToDecimalString(&vin.ValueSat) + vin.AddrDesc = output.AddrDesc + vin.Addresses, vin.Searchable, err = output.Addresses(w.chainParser) + if err != nil { + glog.Errorf("output.Addresses error %v, tx %v, output %v", err, bchainVin.Txid, i) + } } } + vin.Value = w.chainParser.AmountToDecimalString(&vin.ValueSat) + valInSat.Add(&valInSat, &vin.ValueSat) + } + } else if w.chainType == bchain.ChainEthereumType { + if len(bchainVin.Addresses) > 0 { + vin.AddrDesc, err = w.chainParser.GetAddrDescFromAddress(bchainVin.Addresses[0]) + if err != nil { + glog.Errorf("GetAddrDescFromAddress error %v, tx %v, bchainVin %v", err, bchainTx.Txid, bchainVin) + } + vin.Addresses = bchainVin.Addresses + vin.Searchable = true } - vin.Value = w.chainParser.AmountToDecimalString(&vin.ValueSat) - valInSat.Add(&valInSat, &vin.ValueSat) } } vouts := make([]Vout, len(bchainTx.Vout)) diff --git a/bchain/coins/eth/ethparser.go b/bchain/coins/eth/ethparser.go index 90341cd5..72f6c1fb 100644 --- a/bchain/coins/eth/ethparser.go +++ b/bchain/coins/eth/ethparser.go @@ -291,7 +291,26 @@ func (p *EthereumParser) UnpackBlockHash(buf []byte) (string, error) { return hexutil.Encode(buf), nil } -// GetChainType returns TypeEthereum +// GetChainType returns EthereumType func (p *EthereumParser) GetChainType() bchain.ChainType { return bchain.ChainEthereumType } + +// GetHeightFromTx returns ethereum specific data from bchain.Tx +func GetHeightFromTx(tx *bchain.Tx) (uint32, error) { + // TODO - temporary implementation - will use bchain.Tx.SpecificData field + b, err := hex.DecodeString(tx.Hex) + if err != nil { + return 0, err + } + var r rpcTransaction + var n uint64 + err = json.Unmarshal(b, &r) + if err != nil { + return 0, err + } + if n, err = hexutil.DecodeUint64(r.BlockNumber); err != nil { + return 0, errors.Annotatef(err, "BlockNumber %v", r.BlockNumber) + } + return uint32(n), nil +} diff --git a/bchain/types.go b/bchain/types.go index 59d05d39..327dfdcd 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -15,7 +15,7 @@ type ChainType int const ( // ChainBitcoinType is blockchain derived from bitcoin ChainBitcoinType = ChainType(iota) - // TypeEthereum is blockchain derived from ethereum + // ChainEthereumType is blockchain derived from ethereum ChainEthereumType ) diff --git a/db/rocksdb.go b/db/rocksdb.go index 94d2f456..b795aaee 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -301,9 +301,11 @@ func (d *RocksDB) writeBlock(block *bchain.Block, op int) error { return err } } else if chainType == bchain.ChainEthereumType { - if err := d.writeAddressesTypeEthereum(wb, block, op); err != nil { + if err := d.writeAddressesEthereumType(wb, block, op); err != nil { return err } + } else { + return errors.New("Unknown chain type") } return d.db.Write(d.wo, wb) @@ -861,7 +863,7 @@ func (d *RocksDB) addAddrDescToRecords(op int, wb *gorocksdb.WriteBatch, records return nil } -func (d *RocksDB) writeAddressesTypeEthereum(wb *gorocksdb.WriteBatch, block *bchain.Block, op int) error { +func (d *RocksDB) writeAddressesEthereumType(wb *gorocksdb.WriteBatch, block *bchain.Block, op int) error { addresses := make(map[string][]outpoint) for _, tx := range block.Txs { btxID, err := d.chainParser.PackTxid(tx.Txid) diff --git a/db/txcache.go b/db/txcache.go index 25f17bff..b20d21fe 100644 --- a/db/txcache.go +++ b/db/txcache.go @@ -2,18 +2,21 @@ package db import ( "blockbook/bchain" + "blockbook/bchain/coins/eth" "blockbook/common" "github.com/golang/glog" + "github.com/juju/errors" ) // TxCache is handle to TxCacheServer type TxCache struct { - db *RocksDB - chain bchain.BlockChain - metrics *common.Metrics - is *common.InternalState - enabled bool + db *RocksDB + chain bchain.BlockChain + metrics *common.Metrics + is *common.InternalState + enabled bool + chainType bchain.ChainType } // NewTxCache creates new TxCache interface and returns its handle @@ -22,11 +25,12 @@ func NewTxCache(db *RocksDB, chain bchain.BlockChain, metrics *common.Metrics, i glog.Info("txcache: disabled") } return &TxCache{ - db: db, - chain: chain, - metrics: metrics, - is: is, - enabled: enabled, + db: db, + chain: chain, + metrics: metrics, + is: is, + enabled: enabled, + chainType: chain.GetChainParser().GetChainType(), }, nil } @@ -56,18 +60,27 @@ func (c *TxCache) GetTransaction(txid string) (*bchain.Tx, uint32, error) { c.metrics.TxCacheEfficiency.With(common.Labels{"status": "miss"}).Inc() // cache only confirmed transactions if tx.Confirmations > 0 { - ta, err := c.db.GetTxAddresses(txid) - if err != nil { - return nil, 0, err - } - // the transaction may me not yet indexed, in that case get the height from the backend - if ta == nil { - h, err = c.chain.GetBestBlockHeight() + if c.chainType == bchain.ChainBitcoinType { + ta, err := c.db.GetTxAddresses(txid) + if err != nil { + return nil, 0, err + } + // the transaction may me not yet indexed, in that case get the height from the backend + if ta == nil { + h, err = c.chain.GetBestBlockHeight() + if err != nil { + return nil, 0, err + } + } else { + h = ta.Height + } + } else if c.chainType == bchain.ChainEthereumType { + h, err = eth.GetHeightFromTx(tx) if err != nil { return nil, 0, err } } else { - h = ta.Height + return nil, 0, errors.New("Unknown chain type") } if c.enabled { err = c.db.PutTx(tx, h, tx.Blocktime)