Implement explorer for EthereumType coin - WIP

ethereum
Martin Boehm 2018-11-26 00:20:01 +01:00
parent 8886256d0b
commit 7a990f9b5b
5 changed files with 98 additions and 61 deletions

View File

@ -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

View File

@ -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++
}

View File

@ -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

View File

@ -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
}

View File

@ -47,7 +47,7 @@
<pre id="txSpecific"></pre>
</div>
<script type="text/javascript">
txSpecific = {{$tx.CoinSpecificData}};
txSpecific = {{$tx.CoinSpecificJSON}};
function syntaxHighlight(json) {
json = JSON.stringify(json, undefined, 2);
json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');