Show valid address with zero transaction in explorer #82
parent
7d28b710e3
commit
2dca694f95
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/gobuffalo/packr"
|
||||
)
|
||||
|
||||
// Text contains static overridable texts used in explorer
|
||||
var Text struct {
|
||||
BlockbookAbout, TOSLink string
|
||||
}
|
||||
|
|
22
api/types.go
22
api/types.go
|
@ -8,27 +8,31 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
type ApiError struct {
|
||||
// APIError extends error by information if the error details should be returned to the end user
|
||||
type APIError struct {
|
||||
Text string
|
||||
Public bool
|
||||
}
|
||||
|
||||
func (e *ApiError) Error() string {
|
||||
func (e *APIError) Error() string {
|
||||
return e.Text
|
||||
}
|
||||
|
||||
func NewApiError(s string, public bool) error {
|
||||
return &ApiError{
|
||||
// NewAPIError creates ApiError
|
||||
func NewAPIError(s string, public bool) error {
|
||||
return &APIError{
|
||||
Text: s,
|
||||
Public: public,
|
||||
}
|
||||
}
|
||||
|
||||
// ScriptSig contains input script
|
||||
type ScriptSig struct {
|
||||
Hex string `json:"hex"`
|
||||
Asm string `json:"asm,omitempty"`
|
||||
}
|
||||
|
||||
// Vin contains information about single transaction input
|
||||
type Vin struct {
|
||||
Txid string `json:"txid"`
|
||||
Vout uint32 `json:"vout"`
|
||||
|
@ -42,6 +46,7 @@ type Vin struct {
|
|||
ValueSat big.Int `json:"-"`
|
||||
}
|
||||
|
||||
// ScriptPubKey contains output script and addresses derived from it
|
||||
type ScriptPubKey struct {
|
||||
Hex string `json:"hex"`
|
||||
Asm string `json:"asm,omitempty"`
|
||||
|
@ -50,6 +55,8 @@ type ScriptPubKey struct {
|
|||
Searchable bool `json:"-"`
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// Vout contains information about single transaction output
|
||||
type Vout struct {
|
||||
Value string `json:"value"`
|
||||
ValueSat big.Int `json:"-"`
|
||||
|
@ -61,6 +68,7 @@ type Vout struct {
|
|||
SpentHeight int `json:"spentHeight,omitempty"`
|
||||
}
|
||||
|
||||
// Tx holds information about a transaction
|
||||
type Tx struct {
|
||||
Txid string `json:"txid"`
|
||||
Version int32 `json:"version,omitempty"`
|
||||
|
@ -82,12 +90,14 @@ type Tx struct {
|
|||
Hex string `json:"hex"`
|
||||
}
|
||||
|
||||
// Paging contains information about paging for address, blocks and block
|
||||
type Paging struct {
|
||||
Page int `json:"page"`
|
||||
TotalPages int `json:"totalPages"`
|
||||
ItemsOnPage int `json:"itemsOnPage"`
|
||||
}
|
||||
|
||||
// Address holds information about address and its transactions
|
||||
type Address struct {
|
||||
Paging
|
||||
AddrStr string `json:"addrStr"`
|
||||
|
@ -101,11 +111,13 @@ type Address struct {
|
|||
Txids []string `json:"transactions,omitempty"`
|
||||
}
|
||||
|
||||
// Blocks is list of blocks with paging information
|
||||
type Blocks struct {
|
||||
Paging
|
||||
Blocks []db.BlockInfo `json:"blocks"`
|
||||
}
|
||||
|
||||
// Block contains information about block
|
||||
type Block struct {
|
||||
Paging
|
||||
bchain.BlockInfo
|
||||
|
@ -113,6 +125,7 @@ type Block struct {
|
|||
Transactions []*Tx `json:"txs,omitempty"`
|
||||
}
|
||||
|
||||
// BlockbookInfo contains information about the running blockbook instance
|
||||
type BlockbookInfo struct {
|
||||
Coin string `json:"coin"`
|
||||
Host string `json:"host"`
|
||||
|
@ -133,6 +146,7 @@ type BlockbookInfo struct {
|
|||
About string `json:"about"`
|
||||
}
|
||||
|
||||
// SystemInfo contains information about the running blockbook and backend instance
|
||||
type SystemInfo struct {
|
||||
Blockbook *BlockbookInfo `json:"blockbook"`
|
||||
Backend *bchain.ChainInfo `json:"backend"`
|
||||
|
|
|
@ -85,7 +85,7 @@ func (w *Worker) GetSpendingTxid(txid string, n int) (string, error) {
|
|||
return "", err
|
||||
}
|
||||
if n >= len(tx.Vout) || n < 0 {
|
||||
return "", NewApiError(fmt.Sprintf("Passed incorrect vout index %v for tx %v, len vout %v", n, tx.Txid, len(tx.Vout)), false)
|
||||
return "", NewAPIError(fmt.Sprintf("Passed incorrect vout index %v for tx %v, len vout %v", n, tx.Txid, len(tx.Vout)), false)
|
||||
}
|
||||
err = w.setSpendingTxToVout(&tx.Vout[n], tx.Txid, uint32(tx.Blockheight))
|
||||
if err != nil {
|
||||
|
@ -100,7 +100,7 @@ func (w *Worker) GetTransaction(txid string, spendingTxs bool) (*Tx, error) {
|
|||
start := time.Now()
|
||||
bchainTx, height, err := w.txCache.GetTransaction(txid)
|
||||
if err != nil {
|
||||
return nil, NewApiError(fmt.Sprintf("Tx not found, %v", err), true)
|
||||
return nil, NewAPIError(fmt.Sprintf("Tx not found, %v", err), true)
|
||||
}
|
||||
ta, err := w.db.GetTxAddresses(txid)
|
||||
if err != nil {
|
||||
|
@ -357,12 +357,12 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, onlyTxids b
|
|||
}
|
||||
addrDesc, err := w.chainParser.GetAddrDescFromAddress(address)
|
||||
if err != nil {
|
||||
return nil, NewApiError(fmt.Sprintf("Address not found, %v", err), true)
|
||||
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)
|
||||
return nil, NewAPIError(fmt.Sprintf("Address not found, %v", err), true)
|
||||
}
|
||||
// convert the address to the format defined by the parser
|
||||
addresses, _, err := w.chainParser.GetAddressesFromAddrDesc(addrDesc)
|
||||
|
@ -390,7 +390,9 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, onlyTxids b
|
|||
txm = UniqueTxidsInReverse(txm)
|
||||
// check if the address exist
|
||||
if len(txc)+len(txm) == 0 {
|
||||
return nil, NewApiError("Address not found", true)
|
||||
return &Address{
|
||||
AddrStr: address,
|
||||
}, nil
|
||||
}
|
||||
bestheight, _, err := w.db.GetBestBlock()
|
||||
if err != nil {
|
||||
|
@ -526,9 +528,9 @@ func (w *Worker) GetBlock(bid string, page int, txsOnPage int) (*Block, error) {
|
|||
bi, err := w.chain.GetBlockInfo(hash)
|
||||
if err != nil {
|
||||
if err == bchain.ErrBlockNotFound {
|
||||
return nil, NewApiError("Block not found", true)
|
||||
return nil, NewAPIError("Block not found", true)
|
||||
}
|
||||
return nil, NewApiError(fmt.Sprintf("Block not found, %v", err), true)
|
||||
return nil, NewAPIError(fmt.Sprintf("Block not found, %v", err), true)
|
||||
}
|
||||
dbi := &db.BlockInfo{
|
||||
Hash: bi.Hash,
|
||||
|
|
|
@ -215,7 +215,7 @@ func (s *PublicServer) jsonHandler(handler func(r *http.Request) (interface{}, e
|
|||
}()
|
||||
data, err = handler(r)
|
||||
if err != nil || data == nil {
|
||||
if apiErr, ok := err.(*api.ApiError); ok {
|
||||
if apiErr, ok := err.(*api.APIError); ok {
|
||||
if apiErr.Public {
|
||||
data = jsonError{apiErr.Error(), http.StatusBadRequest}
|
||||
} else {
|
||||
|
@ -251,7 +251,7 @@ func (s *PublicServer) newTemplateData() *TemplateData {
|
|||
|
||||
func (s *PublicServer) newTemplateDataWithError(text string) *TemplateData {
|
||||
td := s.newTemplateData()
|
||||
td.Error = &api.ApiError{Text: text}
|
||||
td.Error = &api.APIError{Text: text}
|
||||
return td
|
||||
}
|
||||
|
||||
|
@ -290,7 +290,7 @@ func (s *PublicServer) htmlTemplateHandler(handler func(w http.ResponseWriter, r
|
|||
t, data, err = handler(w, r)
|
||||
if err != nil || (data == nil && t != noTpl) {
|
||||
t = errorInternalTpl
|
||||
if apiErr, ok := err.(*api.ApiError); ok {
|
||||
if apiErr, ok := err.(*api.APIError); ok {
|
||||
data = s.newTemplateData()
|
||||
data.Error = apiErr
|
||||
if apiErr.Public {
|
||||
|
@ -336,7 +336,7 @@ type TemplateData struct {
|
|||
AddrStr string
|
||||
Tx *api.Tx
|
||||
TxSpecific json.RawMessage
|
||||
Error *api.ApiError
|
||||
Error *api.APIError
|
||||
Blocks *api.Blocks
|
||||
Block *api.Block
|
||||
Info *api.SystemInfo
|
||||
|
@ -427,7 +427,7 @@ func (s *PublicServer) explorerSpendingTx(w http.ResponseWriter, r *http.Request
|
|||
}
|
||||
}
|
||||
if err == nil {
|
||||
err = api.NewApiError("Transaction not found", true)
|
||||
err = api.NewAPIError("Transaction not found", true)
|
||||
}
|
||||
return errorTpl, nil, err
|
||||
}
|
||||
|
@ -531,7 +531,7 @@ func (s *PublicServer) explorerSearch(w http.ResponseWriter, r *http.Request) (t
|
|||
return noTpl, nil, nil
|
||||
}
|
||||
}
|
||||
return errorTpl, nil, api.NewApiError(fmt.Sprintf("No matching records found for '%v'", q), true)
|
||||
return errorTpl, nil, api.NewAPIError(fmt.Sprintf("No matching records found for '%v'", q), true)
|
||||
}
|
||||
|
||||
func (s *PublicServer) explorerSendTx(w http.ResponseWriter, r *http.Request) (tpl, *TemplateData, error) {
|
||||
|
@ -547,7 +547,7 @@ func (s *PublicServer) explorerSendTx(w http.ResponseWriter, r *http.Request) (t
|
|||
res, err := s.chain.SendRawTransaction(hex)
|
||||
if err != nil {
|
||||
data.SendTxHex = hex
|
||||
data.Error = &api.ApiError{Text: err.Error(), Public: true}
|
||||
data.Error = &api.APIError{Text: err.Error(), Public: true}
|
||||
return sendTransactionTpl, data, nil
|
||||
}
|
||||
data.Status = "Transaction sent, result " + res
|
||||
|
@ -652,7 +652,7 @@ func (s *PublicServer) apiTx(r *http.Request) (interface{}, error) {
|
|||
if len(p) > 0 {
|
||||
spendingTxs, err = strconv.ParseBool(p)
|
||||
if err != nil {
|
||||
return nil, api.NewApiError("Parameter 'spending' cannot be converted to boolean", true)
|
||||
return nil, api.NewAPIError("Parameter 'spending' cannot be converted to boolean", true)
|
||||
}
|
||||
}
|
||||
tx, err = s.api.GetTransaction(txid, spendingTxs)
|
||||
|
@ -711,7 +711,7 @@ func (s *PublicServer) apiSendTx(r *http.Request) (interface{}, error) {
|
|||
if r.Method == http.MethodPost {
|
||||
data, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return nil, api.NewApiError("Missing tx blob", true)
|
||||
return nil, api.NewAPIError("Missing tx blob", true)
|
||||
}
|
||||
hex = string(data)
|
||||
} else {
|
||||
|
@ -722,11 +722,11 @@ func (s *PublicServer) apiSendTx(r *http.Request) (interface{}, error) {
|
|||
if len(hex) > 0 {
|
||||
res.Result, err = s.chain.SendRawTransaction(hex)
|
||||
if err != nil {
|
||||
return nil, api.NewApiError(err.Error(), true)
|
||||
return nil, api.NewAPIError(err.Error(), true)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
return nil, api.NewApiError("Missing tx blob", true)
|
||||
return nil, api.NewAPIError("Missing tx blob", true)
|
||||
}
|
||||
|
||||
type resultEstimateFeeAsString struct {
|
||||
|
@ -741,14 +741,14 @@ func (s *PublicServer) apiEstimateFee(r *http.Request) (interface{}, error) {
|
|||
if len(b) > 0 {
|
||||
blocks, err := strconv.Atoi(b)
|
||||
if err != nil {
|
||||
return nil, api.NewApiError("Parameter 'number of blocks' is not a number", true)
|
||||
return nil, api.NewAPIError("Parameter 'number of blocks' is not a number", true)
|
||||
}
|
||||
conservative := true
|
||||
c := r.URL.Query().Get("conservative")
|
||||
if len(c) > 0 {
|
||||
conservative, err = strconv.ParseBool(c)
|
||||
if err != nil {
|
||||
return nil, api.NewApiError("Parameter 'conservative' cannot be converted to boolean", true)
|
||||
return nil, api.NewAPIError("Parameter 'conservative' cannot be converted to boolean", true)
|
||||
}
|
||||
}
|
||||
var fee big.Int
|
||||
|
@ -763,5 +763,5 @@ func (s *PublicServer) apiEstimateFee(r *http.Request) (interface{}, error) {
|
|||
return res, nil
|
||||
}
|
||||
}
|
||||
return nil, api.NewApiError("Missing parameter 'number of blocks'", true)
|
||||
return nil, api.NewAPIError("Missing parameter 'number of blocks'", true)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue