Get data for explorer using index v3 - WIP
This commit is contained in:
parent
b1e749dab9
commit
b56b6c7a9b
26
api/types.go
26
api/types.go
@ -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"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{{define "specific"}}
|
{{define "specific"}}
|
||||||
<h1>Error</h1>
|
<h1>Error</h1>
|
||||||
<h3>{{.Error.Text}}</h3>
|
<h4>{{.Error.Text}}</h4>
|
||||||
{{end}}
|
{{end}}
|
||||||
Loading…
Reference in New Issue
Block a user