Implement explorer for EthereumType coin - WIP
parent
8886256d0b
commit
7a990f9b5b
67
api/types.go
67
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
|
||||
|
|
|
@ -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++
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
|
|
Loading…
Reference in New Issue