Get data for explorer using index v3 - WIP

This commit is contained in:
Martin Boehm 2018-08-27 15:36:33 +02:00
parent b1e749dab9
commit b56b6c7a9b
4 changed files with 82 additions and 38 deletions

View File

@ -30,6 +30,7 @@ type Vin struct {
N int `json:"n"` N int `json:"n"`
ScriptSig ScriptSig `json:"scriptSig"` ScriptSig ScriptSig `json:"scriptSig"`
Addr string `json:"addr"` Addr string `json:"addr"`
AddrLink bool `json:"-"`
Value string `json:"value"` Value string `json:"value"`
ValueSat big.Int `json:"-"` ValueSat big.Int `json:"-"`
} }
@ -38,6 +39,7 @@ type ScriptPubKey struct {
Hex string `json:"hex"` Hex string `json:"hex"`
Asm string `json:"asm,omitempty"` Asm string `json:"asm,omitempty"`
Addresses []string `json:"addresses"` Addresses []string `json:"addresses"`
AddrsLink []bool `json:"-"`
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
} }
type Vout struct { type Vout struct {
@ -45,6 +47,7 @@ type Vout struct {
ValueSat big.Int `json:"-"` ValueSat big.Int `json:"-"`
N int `json:"n"` N int `json:"n"`
ScriptPubKey ScriptPubKey `json:"scriptPubKey"` ScriptPubKey ScriptPubKey `json:"scriptPubKey"`
Spent bool `json:"-"`
SpentTxID string `json:"spentTxId,omitempty"` SpentTxID string `json:"spentTxId,omitempty"`
SpentIndex int `json:"spentIndex,omitempty"` SpentIndex int `json:"spentIndex,omitempty"`
SpentHeight int `json:"spentHeight,omitempty"` SpentHeight int `json:"spentHeight,omitempty"`
@ -69,15 +72,16 @@ type Tx struct {
} }
type Address struct { type Address struct {
AddrStr string `json:"addrStr"` AddrStr string `json:"addrStr"`
Balance string `json:"balance"` Balance string `json:"balance"`
TotalReceived string `json:"totalReceived"` TotalReceived string `json:"totalReceived"`
TotalSent string `json:"totalSent"` TotalSent string `json:"totalSent"`
UnconfirmedBalance string `json:"unconfirmedBalance"` UnconfirmedBalance string `json:"unconfirmedBalance"`
UnconfirmedTxApperances int `json:"unconfirmedTxApperances"` UnconfirmedTxApperances int `json:"unconfirmedTxApperances"`
TxApperances int `json:"txApperances"` TxApperances int `json:"txApperances"`
Transactions []*Tx `json:"transactions"` Transactions []*Tx `json:"transactions,omitempty"`
Page int `json:"page"` Txids []string `json:"transactions,omitempty"` // this is intentional, we return either Transactions or Txids
TotalPages int `json:"totalPages"` Page int `json:"page"`
TxsOnPage int `json:"txsOnPage"` TotalPages int `json:"totalPages"`
TxsOnPage int `json:"txsOnPage"`
} }

View File

@ -4,6 +4,7 @@ import (
"blockbook/bchain" "blockbook/bchain"
"blockbook/common" "blockbook/common"
"blockbook/db" "blockbook/db"
"fmt"
"math/big" "math/big"
"time" "time"
@ -33,10 +34,10 @@ func NewWorker(db *db.RocksDB, chain bchain.BlockChain, txCache *db.TxCache, is
} }
// GetTransaction reads transaction data from txid // GetTransaction reads transaction data from txid
func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTx bool) (*Tx, error) { func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTxs bool) (*Tx, error) {
bchainTx, height, err := w.txCache.GetTransaction(txid, bestheight) bchainTx, height, err := w.txCache.GetTransaction(txid, bestheight)
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "txCache.GetTransaction %v", txid) return nil, NewApiError(fmt.Sprintf("Tx not found, %v", err), true)
} }
var blockhash string var blockhash string
if bchainTx.Confirmations > 0 { if bchainTx.Confirmations > 0 {
@ -56,20 +57,42 @@ func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTx bool)
vin.ScriptSig.Hex = bchainVin.ScriptSig.Hex vin.ScriptSig.Hex = bchainVin.ScriptSig.Hex
// bchainVin.Txid=="" is coinbase transaction // bchainVin.Txid=="" is coinbase transaction
if bchainVin.Txid != "" { if bchainVin.Txid != "" {
otx, _, err := w.txCache.GetTransaction(bchainVin.Txid, bestheight) // load spending addresses from TxAddresses
ta, err := w.db.GetTxAddresses(bchainVin.Txid)
if err != nil { 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) { if ta == nil {
vout := &otx.Vout[vin.Vout] // mempool transactions are not in TxAddresses, all confirmed should be there, log a problem
vin.ValueSat = vout.ValueSat if bchainTx.Confirmations > 0 {
vin.Value = w.chainParser.AmountToDecimalString(&vout.ValueSat) glog.Warning("DB inconsistency: tx ", bchainVin.Txid, ": not found in txAddresses")
valInSat.Add(&valInSat, &vout.ValueSat) }
if vout.Address != nil { // try to load from backend
a := vout.Address.String() otx, _, err := w.txCache.GetTransaction(bchainVin.Txid, bestheight)
vin.Addr = a if err != nil {
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
if vout.Address != nil {
a := vout.Address.String()
vin.Addr = a
}
}
} else {
if len(ta.Outputs) > int(vin.Vout) {
output := &ta.Outputs[vin.Vout]
vin.ValueSat = output.ValueSat
vin.Value = w.chainParser.AmountToDecimalString(&vin.ValueSat)
a, _ := output.Addresses(w.chainParser)
if len(a) > 0 {
vin.Addr = a[0]
}
} }
} }
vin.Value = w.chainParser.AmountToDecimalString(&vin.ValueSat)
valInSat.Add(&valInSat, &vin.ValueSat)
} }
} }
vouts := make([]Vout, len(bchainTx.Vout)) vouts := make([]Vout, len(bchainTx.Vout))
@ -82,7 +105,7 @@ func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTx bool)
valOutSat.Add(&valOutSat, &bchainVout.ValueSat) valOutSat.Add(&valOutSat, &bchainVout.ValueSat)
vout.ScriptPubKey.Hex = bchainVout.ScriptPubKey.Hex vout.ScriptPubKey.Hex = bchainVout.ScriptPubKey.Hex
vout.ScriptPubKey.Addresses = bchainVout.ScriptPubKey.Addresses vout.ScriptPubKey.Addresses = bchainVout.ScriptPubKey.Addresses
if spendingTx { if spendingTxs {
// TODO // TODO
} }
} }
@ -100,7 +123,7 @@ func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTx bool)
Confirmations: bchainTx.Confirmations, Confirmations: bchainTx.Confirmations,
Fees: w.chainParser.AmountToDecimalString(&feesSat), Fees: w.chainParser.AmountToDecimalString(&feesSat),
Locktime: bchainTx.LockTime, Locktime: bchainTx.LockTime,
WithSpends: spendingTx, WithSpends: spendingTxs,
Time: bchainTx.Time, Time: bchainTx.Time,
Txid: bchainTx.Txid, Txid: bchainTx.Txid,
ValueIn: w.chainParser.AmountToDecimalString(&valInSat), ValueIn: w.chainParser.AmountToDecimalString(&valInSat),
@ -112,11 +135,11 @@ func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTx bool)
return r, nil return r, nil
} }
func (s *Worker) getAddressTxids(address string, mempool bool) ([]string, error) { func (w *Worker) getAddressTxids(address string, mempool bool) ([]string, error) {
var err error var err error
txids := make([]string, 0) txids := make([]string, 0)
if !mempool { if !mempool {
err = s.db.GetTransactions(address, 0, ^uint32(0), func(txid string, vout uint32, isOutput bool) error { err = w.db.GetTransactions(address, 0, ^uint32(0), func(txid string, vout uint32, isOutput bool) error {
txids = append(txids, txid) txids = append(txids, txid)
return nil return nil
}) })
@ -124,7 +147,7 @@ func (s *Worker) getAddressTxids(address string, mempool bool) ([]string, error)
return nil, err return nil, err
} }
} else { } else {
m, err := s.chain.GetMempoolTransactions(address) m, err := w.chain.GetMempoolTransactions(address)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -223,9 +246,12 @@ func (w *Worker) txFromTxAddress(txid string, ta *db.TxAddresses, bi *db.BlockIn
// GetAddress computes address value and gets transactions for given address // 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 bool) (*Address, error) {
start := time.Now() start := time.Now()
if page < 0 {
page = 0
}
ba, err := w.db.GetAddressBalance(address) ba, err := w.db.GetAddressBalance(address)
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "GetAddressBalance %v", address) return nil, NewApiError(fmt.Sprintf("Address not found, %v", err), true)
} }
if ba == nil { if ba == nil {
return nil, NewApiError("Address not found", true) return nil, NewApiError("Address not found", true)
@ -235,19 +261,20 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, onlyTxids b
return nil, errors.Annotatef(err, "getAddressTxids %v false", address) return nil, errors.Annotatef(err, "getAddressTxids %v false", address)
} }
txc = UniqueTxidsInReverse(txc) txc = UniqueTxidsInReverse(txc)
txm, err := w.getAddressTxids(address, true) var txm []string
if err != nil { // mempool only on the first page
return nil, errors.Annotatef(err, "getAddressTxids %v true", address) if page == 0 {
txm, err = w.getAddressTxids(address, true)
if err != nil {
return nil, errors.Annotatef(err, "getAddressTxids %v true", address)
}
txm = UniqueTxidsInReverse(txm)
} }
txm = UniqueTxidsInReverse(txm)
bestheight, _, err := w.db.GetBestBlock() bestheight, _, err := w.db.GetBestBlock()
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "GetBestBlock") return nil, errors.Annotatef(err, "GetBestBlock")
} }
// paging // paging
if page < 0 {
page = 0
}
from := page * txsOnPage from := page * txsOnPage
totalPages := len(txc) / txsOnPage totalPages := len(txc) / txsOnPage
if from >= len(txc) { if from >= len(txc) {

View File

@ -15,6 +15,14 @@ a {
text-decoration: none; text-decoration: none;
} }
h1 small {
font-size: 65%;
}
h3 {
margin-top: 20px;
}
.octicon { .octicon {
color: #777; color: #777;
display: inline-block; display: inline-block;
@ -175,4 +183,9 @@ a {
.alert .data-table { .alert .data-table {
margin: 0; margin: 0;
} }
.navbar-nav .nav-link {
padding-right: 0;
padding-left: .25rem;
}

View File

@ -1,4 +1,4 @@
{{define "specific"}} {{define "specific"}}
<h1>Error</h1> <h1>Error</h1>
<h3>{{.Error.Text}}</h3> <h4>{{.Error.Text}}</h4>
{{end}} {{end}}