diff --git a/api/types.go b/api/types.go index 1b4c57d5..111aca89 100644 --- a/api/types.go +++ b/api/types.go @@ -4,6 +4,7 @@ import ( "blockbook/bchain" "blockbook/common" "blockbook/db" + "encoding/json" "math/big" "time" ) @@ -70,25 +71,26 @@ type Vout struct { // Tx holds information about a transaction type Tx struct { - Txid string `json:"txid"` - Version int32 `json:"version,omitempty"` - Locktime uint32 `json:"locktime,omitempty"` - Vin []Vin `json:"vin"` - Vout []Vout `json:"vout"` - Blockhash string `json:"blockhash,omitempty"` - Blockheight int `json:"blockheight"` - Confirmations uint32 `json:"confirmations"` - Time int64 `json:"time,omitempty"` - Blocktime int64 `json:"blocktime"` - ValueOut string `json:"valueOut"` - ValueOutSat big.Int `json:"-"` - Size int `json:"size,omitempty"` - ValueIn string `json:"valueIn"` - ValueInSat big.Int `json:"-"` - Fees string `json:"fees"` - FeesSat big.Int `json:"-"` - Hex string `json:"hex"` - CoinSpecificData interface{} `json:"-"` + Txid string `json:"txid"` + Version int32 `json:"version,omitempty"` + Locktime uint32 `json:"locktime,omitempty"` + Vin []Vin `json:"vin"` + Vout []Vout `json:"vout"` + Blockhash string `json:"blockhash,omitempty"` + Blockheight int `json:"blockheight"` + Confirmations uint32 `json:"confirmations"` + Time int64 `json:"time,omitempty"` + Blocktime int64 `json:"blocktime"` + ValueOut string `json:"valueOut"` + ValueOutSat big.Int `json:"-"` + Size int `json:"size,omitempty"` + ValueIn string `json:"valueIn"` + ValueInSat big.Int `json:"-"` + Fees string `json:"fees"` + FeesSat big.Int `json:"-"` + Hex string `json:"hex"` + CoinSpecificData interface{} `json:"-"` + CoinSpecificJSON json.RawMessage `json:"-"` } // Paging contains information about paging for address, blocks and block @@ -98,18 +100,27 @@ type Paging struct { ItemsOnPage int `json:"itemsOnPage"` } +type Erc20Token struct { + Contract string `json:"contract"` + Txs int `json:"txs"` + Name string `json:"name"` + Symbol string `json:"symbol"` + Value string `json:"value"` +} + // Address holds information about address and its transactions type Address struct { Paging - AddrStr string `json:"addrStr"` - Balance string `json:"balance"` - TotalReceived string `json:"totalReceived"` - TotalSent string `json:"totalSent"` - UnconfirmedBalance string `json:"unconfirmedBalance"` - UnconfirmedTxApperances int `json:"unconfirmedTxApperances"` - TxApperances int `json:"txApperances"` - Transactions []*Tx `json:"txs,omitempty"` - Txids []string `json:"transactions,omitempty"` + AddrStr string `json:"addrStr"` + Balance string `json:"balance"` + TotalReceived string `json:"totalReceived"` + TotalSent string `json:"totalSent"` + UnconfirmedBalance string `json:"unconfirmedBalance"` + UnconfirmedTxApperances int `json:"unconfirmedTxApperances"` + TxApperances int `json:"txApperances"` + Transactions []*Tx `json:"txs,omitempty"` + Txids []string `json:"transactions,omitempty"` + Erc20Tokens []*Erc20Token `json:"erc20tokens,omitempty"` } // AddressUtxo holds information about address and its transactions diff --git a/api/worker.go b/api/worker.go index 4d8ff4e5..3700b250 100644 --- a/api/worker.go +++ b/api/worker.go @@ -99,7 +99,7 @@ func (w *Worker) GetSpendingTxid(txid string, n int) (string, error) { } // GetTransaction reads transaction data from txid -func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificData bool) (*Tx, error) { +func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool) (*Tx, error) { start := time.Now() bchainTx, height, err := w.txCache.GetTransaction(txid) if err != nil { @@ -211,9 +211,9 @@ func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificData bool } // for now do not return size, we would have to compute vsize of segwit transactions // size:=len(bchainTx.Hex) / 2 - var sd json.RawMessage - if specificData { - sd, err = w.chain.GetTransactionSpecific(bchainTx) + var sj json.RawMessage + if specificJSON { + sj, err = w.chain.GetTransactionSpecific(bchainTx) if err != nil { return nil, err } @@ -236,7 +236,8 @@ func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificData bool Hex: bchainTx.Hex, Vin: vins, Vout: vouts, - CoinSpecificData: sd, + CoinSpecificData: bchainTx.CoinSpecificData, + CoinSpecificJSON: sj, } if spendingTxs { glog.Info("GetTransaction ", txid, " finished in ", time.Since(start)) @@ -250,6 +251,7 @@ func (w *Worker) getAddressTxids(addrDesc bchain.AddressDescriptor, mempool bool if !mempool { err = w.db.GetAddrDescTransactions(addrDesc, 0, ^uint32(0), func(txid string, vout uint32, isOutput bool) error { txids = append(txids, txid) + // glog.Info(txid, " ", vout, " ", isOutput) return nil }) if err != nil { @@ -374,7 +376,7 @@ func computePaging(count, page, itemsOnPage int) (Paging, int, int, int) { } // GetAddress computes address value and gets transactions for given address -func (w *Worker) GetAddress(address string, page int, txsOnPage int, onlyTxids bool) (*Address, error) { +func (w *Worker) GetAddress(address string, page int, txsOnPage int, onlyTxids, existOnly bool) (*Address, error) { start := time.Now() page-- if page < 0 { @@ -384,10 +386,26 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, onlyTxids b if err != nil { return nil, NewAPIError(fmt.Sprintf("Invalid address, %v", err), true) } - // ba can be nil if the address is only in mempool! - ba, err := w.db.GetAddrDescBalance(addrDesc) - if err != nil { - return nil, NewAPIError(fmt.Sprintf("Address not found, %v", err), true) + var ( + ba *db.AddrBalance + ca *db.AddrContracts + ) + if w.chainType == bchain.ChainEthereumType { + ca, err = w.db.GetAddrDescContracts(addrDesc) + if err != nil { + return nil, NewAPIError(fmt.Sprintf("Address not found, %v", err), true) + } + glog.Infof("%+v", ca) + } else { + // ba can be nil if the address is only in mempool! + ba, err = w.db.GetAddrDescBalance(addrDesc) + if err != nil { + return nil, NewAPIError(fmt.Sprintf("Address not found, %v", err), true) + } + } + // if only check that the address exist, return if we have the address + if existOnly && ba != nil { + return &Address{AddrStr: address}, nil } // convert the address to the format defined by the parser addresses, _, err := w.chainParser.GetAddressesFromAddrDesc(addrDesc) @@ -434,8 +452,8 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, onlyTxids b txi := 0 // load mempool transactions var uBalSat big.Int - for _, tx := range txm { - tx, err := w.GetTransaction(tx, false, false) + for _, txid := range txm { + tx, err := w.GetTransaction(txid, false, false) // mempool transaction may fail if err != nil { glog.Error("GetTransaction in mempool ", tx, ": ", err) @@ -460,23 +478,30 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, onlyTxids b if onlyTxids { txids[txi] = txid } else { - ta, err := w.db.GetTxAddresses(txid) - if err != nil { - return nil, errors.Annotatef(err, "GetTxAddresses %v", txid) + if w.chainType == bchain.ChainEthereumType { + txs[txi], err = w.GetTransaction(txid, false, true) + if err != nil { + return nil, errors.Annotatef(err, "GetTransaction %v", txid) + } + } else { + ta, err := w.db.GetTxAddresses(txid) + if err != nil { + return nil, errors.Annotatef(err, "GetTxAddresses %v", txid) + } + if ta == nil { + glog.Warning("DB inconsistency: tx ", txid, ": not found in txAddresses") + continue + } + bi, err := w.db.GetBlockInfo(ta.Height) + if err != nil { + return nil, errors.Annotatef(err, "GetBlockInfo %v", ta.Height) + } + if bi == nil { + glog.Warning("DB inconsistency: block height ", ta.Height, ": not found in db") + continue + } + txs[txi] = w.txFromTxAddress(txid, ta, bi, bestheight) } - if ta == nil { - glog.Warning("DB inconsistency: tx ", txid, ": not found in txAddresses") - continue - } - bi, err := w.db.GetBlockInfo(ta.Height) - if err != nil { - return nil, errors.Annotatef(err, "GetBlockInfo %v", ta.Height) - } - if bi == nil { - glog.Warning("DB inconsistency: block height ", ta.Height, ": not found in db") - continue - } - txs[txi] = w.txFromTxAddress(txid, ta, bi, bestheight) } txi++ } diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 72513262..1a0dd049 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -445,7 +445,8 @@ func (b *EthereumRPC) getERC20EventsForBlock(blockNumber string) (map[string][]* return nil, errors.Annotatef(err, "blockNumber %v", blockNumber) } r := make(map[string][]*rpcLog) - for _, l := range logs { + for i := range logs { + l := &logs[i] r[l.Hash] = append(r[l.Hash], &l.rpcLog) } return r, nil diff --git a/server/public.go b/server/public.go index 3ab57907..f02c4fe0 100644 --- a/server/public.go +++ b/server/public.go @@ -435,7 +435,7 @@ func (s *PublicServer) explorerAddress(w http.ResponseWriter, r *http.Request) ( if ec != nil { page = 0 } - address, err = s.api.GetAddress(r.URL.Path[i+1:], page, txsOnPage, false) + address, err = s.api.GetAddress(r.URL.Path[i+1:], page, txsOnPage, false, false) if err != nil { return errorTpl, nil, err } @@ -519,7 +519,7 @@ func (s *PublicServer) explorerSearch(w http.ResponseWriter, r *http.Request) (t http.Redirect(w, r, joinURL("/tx/", tx.Txid), 302) return noTpl, nil, nil } - address, err = s.api.GetAddress(q, 0, 1, true) + address, err = s.api.GetAddress(q, 0, 1, true, true) if err == nil { http.Redirect(w, r, joinURL("/address/", address.AddrStr), 302) return noTpl, nil, nil @@ -674,7 +674,7 @@ func (s *PublicServer) apiAddress(r *http.Request) (interface{}, error) { if ec != nil { page = 0 } - address, err = s.api.GetAddress(r.URL.Path[i+1:], page, txsInAPI, true) + address, err = s.api.GetAddress(r.URL.Path[i+1:], page, txsInAPI, true, false) } return address, err } diff --git a/static/templates/tx.html b/static/templates/tx.html index 22aca6a3..98a5dc10 100644 --- a/static/templates/tx.html +++ b/static/templates/tx.html @@ -47,7 +47,7 @@