Fix: floData not showing in API and UI

- floData is now in v1 (default) api
- v1 (default) API response data to match flosight as much as possible
- v2 API response with tx details now has coin-specific data field always
- Fixed: locktime not showing in API
- Fixed: display floData in UI (tx, block, address page) if not empty

Note: Due to these floData fixes, this version will only work for FLO blockchain
This commit is contained in:
sairajzero 2023-06-30 03:33:50 +05:30
parent fc52acbde4
commit 9003df740d
3 changed files with 78 additions and 34 deletions

View File

@ -2,7 +2,8 @@ package api
import ( import (
"math/big" "math/big"
"encoding/json"
"strconv"
"github.com/trezor/blockbook/bchain" "github.com/trezor/blockbook/bchain"
) )
@ -21,9 +22,10 @@ type VinV1 struct {
ScriptSig ScriptSigV1 `json:"scriptSig"` ScriptSig ScriptSigV1 `json:"scriptSig"`
AddrDesc bchain.AddressDescriptor `json:"-"` AddrDesc bchain.AddressDescriptor `json:"-"`
Addresses []string `json:"addresses"` Addresses []string `json:"addresses"`
IsAddress bool `json:"-"` IsAddress bool `json:"isAddress"`
Value string `json:"value"` Value float64 `json:"value"`
ValueSat big.Int `json:"-"` ValueSat big.Int `json:"valueSat"`
Coinbase string `json:"coinbase,omitempty"`
} }
// ScriptPubKeyV1 is used for legacy api v1 // ScriptPubKeyV1 is used for legacy api v1
@ -32,14 +34,14 @@ type ScriptPubKeyV1 struct {
Asm string `json:"asm,omitempty"` Asm string `json:"asm,omitempty"`
AddrDesc bchain.AddressDescriptor `json:"-"` AddrDesc bchain.AddressDescriptor `json:"-"`
Addresses []string `json:"addresses"` Addresses []string `json:"addresses"`
IsAddress bool `json:"-"` IsAddress bool `json:"isAddress"`
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
} }
// VoutV1 is used for legacy api v1 // VoutV1 is used for legacy api v1
type VoutV1 struct { type VoutV1 struct {
Value string `json:"value"` Value float64 `json:"value"`
ValueSat big.Int `json:"-"` ValueSat big.Int `json:"valueSat"`
N int `json:"n"` N int `json:"n"`
ScriptPubKey ScriptPubKeyV1 `json:"scriptPubKey"` ScriptPubKey ScriptPubKeyV1 `json:"scriptPubKey"`
Spent bool `json:"spent"` Spent bool `json:"spent"`
@ -52,7 +54,7 @@ type VoutV1 struct {
type TxV1 struct { type TxV1 struct {
Txid string `json:"txid"` Txid string `json:"txid"`
Version int32 `json:"version,omitempty"` Version int32 `json:"version,omitempty"`
Locktime uint32 `json:"locktime,omitempty"` Locktime uint32 `json:"locktime"`
Vin []VinV1 `json:"vin"` Vin []VinV1 `json:"vin"`
Vout []VoutV1 `json:"vout"` Vout []VoutV1 `json:"vout"`
Blockhash string `json:"blockhash,omitempty"` Blockhash string `json:"blockhash,omitempty"`
@ -60,24 +62,29 @@ type TxV1 struct {
Confirmations uint32 `json:"confirmations"` Confirmations uint32 `json:"confirmations"`
Time int64 `json:"time,omitempty"` Time int64 `json:"time,omitempty"`
Blocktime int64 `json:"blocktime"` Blocktime int64 `json:"blocktime"`
ValueOut string `json:"valueOut"` ValueOut float64 `json:"valueOut"`
ValueOutSat big.Int `json:"-"` ValueOutSat big.Int `json:"valueOutSat"`
Size int `json:"size,omitempty"` Size int `json:"size,omitempty"`
ValueIn string `json:"valueIn"` ValueIn float64 `json:"valueIn"`
ValueInSat big.Int `json:"-"` ValueInSat big.Int `json:"valueInSat"`
Fees string `json:"fees"` Fees float64 `json:"fees"`
FeesSat big.Int `json:"-"` FeesSat big.Int `json:"feesSat"`
Hex string `json:"hex"` Hex string `json:"hex"`
FloData string `json:"floData,omitempty"`
} }
// AddressV1 is used for legacy api v1 // AddressV1 is used for legacy api v1
type AddressV1 struct { type AddressV1 struct {
Paging Paging
AddrStr string `json:"addrStr"` AddrStr string `json:"addrStr"`
Balance string `json:"balance"` Balance float64 `json:"balance"`
TotalReceived string `json:"totalReceived"` BalanceSat big.Int `json:"balanceSat"`
TotalSent string `json:"totalSent"` TotalReceived float64 `json:"totalReceived"`
UnconfirmedBalance string `json:"unconfirmedBalance"` TotalReceivedSat big.Int `json:"totalReceivedSat"`
TotalSent float64 `json:"totalSent"`
TotalSentSat big.Int `json:"totalSentSat"`
UnconfirmedBalance float64 `json:"unconfirmedBalance"`
UnconfirmedBalanceSat big.Int `json:"unconfirmedBalanceSat"`
UnconfirmedTxApperances int `json:"unconfirmedTxApperances"` UnconfirmedTxApperances int `json:"unconfirmedTxApperances"`
TxApperances int `json:"txApperances"` TxApperances int `json:"txApperances"`
Transactions []*TxV1 `json:"txs,omitempty"` Transactions []*TxV1 `json:"txs,omitempty"`
@ -88,7 +95,7 @@ type AddressV1 struct {
type AddressUtxoV1 struct { type AddressUtxoV1 struct {
Txid string `json:"txid"` Txid string `json:"txid"`
Vout uint32 `json:"vout"` Vout uint32 `json:"vout"`
Amount string `json:"amount"` Amount float64 `json:"amount"`
AmountSat big.Int `json:"satoshis"` AmountSat big.Int `json:"satoshis"`
Height int `json:"height,omitempty"` Height int `json:"height,omitempty"`
Confirmations int `json:"confirmations"` Confirmations int `json:"confirmations"`
@ -102,6 +109,15 @@ type BlockV1 struct {
Transactions []*TxV1 `json:"txs,omitempty"` Transactions []*TxV1 `json:"txs,omitempty"`
} }
type CoinSpecificDataV0 struct {
FloData string
}
func stringToFloat(f string) float64 {
s, _ := strconv.ParseFloat(f, 64)
return s
}
// TxToV1 converts Tx to TxV1 // TxToV1 converts Tx to TxV1
func (w *Worker) TxToV1(tx *Tx) *TxV1 { func (w *Worker) TxToV1(tx *Tx) *TxV1 {
d := w.chainParser.AmountDecimals() d := w.chainParser.AmountDecimals()
@ -119,9 +135,10 @@ func (w *Worker) TxToV1(tx *Tx) *TxV1 {
IsAddress: v.IsAddress, IsAddress: v.IsAddress,
Sequence: v.Sequence, Sequence: v.Sequence,
Txid: v.Txid, Txid: v.Txid,
Value: v.ValueSat.DecimalString(d), Value: stringToFloat(v.ValueSat.DecimalString(d)),
ValueSat: v.ValueSat.AsBigInt(), ValueSat: v.ValueSat.AsBigInt(),
Vout: v.Vout, Vout: v.Vout,
Coinbase: v.Coinbase,
} }
} }
voutV1 := make([]VoutV1, len(tx.Vout)) voutV1 := make([]VoutV1, len(tx.Vout))
@ -141,29 +158,39 @@ func (w *Worker) TxToV1(tx *Tx) *TxV1 {
SpentHeight: v.SpentHeight, SpentHeight: v.SpentHeight,
SpentIndex: v.SpentIndex, SpentIndex: v.SpentIndex,
SpentTxID: v.SpentTxID, SpentTxID: v.SpentTxID,
Value: v.ValueSat.DecimalString(d), Value: stringToFloat(v.ValueSat.DecimalString(d)),
ValueSat: v.ValueSat.AsBigInt(), ValueSat: v.ValueSat.AsBigInt(),
} }
} }
//floData
var coinData CoinSpecificDataV0
// Notice the dereferencing asterisk *
err := json.Unmarshal(tx.CoinSpecificData, &coinData)
if err != nil {
return nil
}
return &TxV1{ return &TxV1{
Blockhash: tx.Blockhash, Blockhash: tx.Blockhash,
Blockheight: tx.Blockheight, Blockheight: tx.Blockheight,
Blocktime: tx.Blocktime, Blocktime: tx.Blocktime,
Confirmations: tx.Confirmations, Confirmations: tx.Confirmations,
Fees: tx.FeesSat.DecimalString(d), Fees: stringToFloat(tx.FeesSat.DecimalString(d)),
FeesSat: tx.FeesSat.AsBigInt(), FeesSat: tx.FeesSat.AsBigInt(),
Hex: tx.Hex, Hex: tx.Hex,
Locktime: tx.Locktime, Locktime: tx.Locktime,
Size: tx.Size, Size: tx.Size,
Time: tx.Blocktime, Time: tx.Blocktime,
Txid: tx.Txid, Txid: tx.Txid,
ValueIn: tx.ValueInSat.DecimalString(d), ValueIn: stringToFloat(tx.ValueInSat.DecimalString(d)),
ValueInSat: tx.ValueInSat.AsBigInt(), ValueInSat: tx.ValueInSat.AsBigInt(),
ValueOut: tx.ValueOutSat.DecimalString(d), ValueOut: stringToFloat(tx.ValueOutSat.DecimalString(d)),
ValueOutSat: tx.ValueOutSat.AsBigInt(), ValueOutSat: tx.ValueOutSat.AsBigInt(),
Version: tx.Version, Version: tx.Version,
Vin: vinV1, Vin: vinV1,
Vout: voutV1, Vout: voutV1,
FloData: coinData.FloData, //(tx.CoinSpecificData).FloData
} }
} }
@ -180,14 +207,18 @@ func (w *Worker) AddressToV1(a *Address) *AddressV1 {
d := w.chainParser.AmountDecimals() d := w.chainParser.AmountDecimals()
return &AddressV1{ return &AddressV1{
AddrStr: a.AddrStr, AddrStr: a.AddrStr,
Balance: a.BalanceSat.DecimalString(d), Balance: stringToFloat(a.BalanceSat.DecimalString(d)),
BalanceSat: a.BalanceSat.AsBigInt(),
Paging: a.Paging, Paging: a.Paging,
TotalReceived: a.TotalReceivedSat.DecimalString(d), TotalReceived: stringToFloat(a.TotalReceivedSat.DecimalString(d)),
TotalSent: a.TotalSentSat.DecimalString(d), TotalReceivedSat: a.TotalReceivedSat.AsBigInt(),
TotalSent: stringToFloat(a.TotalSentSat.DecimalString(d)),
TotalSentSat: a.TotalSentSat.AsBigInt(),
Transactions: w.transactionsToV1(a.Transactions), Transactions: w.transactionsToV1(a.Transactions),
TxApperances: a.Txs, TxApperances: a.Txs,
Txids: a.Txids, Txids: a.Txids,
UnconfirmedBalance: a.UnconfirmedBalanceSat.DecimalString(d), UnconfirmedBalance: stringToFloat(a.UnconfirmedBalanceSat.DecimalString(d)),
UnconfirmedBalanceSat: a.UnconfirmedBalanceSat.AsBigInt(),
UnconfirmedTxApperances: a.UnconfirmedTxs, UnconfirmedTxApperances: a.UnconfirmedTxs,
} }
} }
@ -200,7 +231,7 @@ func (w *Worker) AddressUtxoToV1(au Utxos) []AddressUtxoV1 {
utxo := &au[i] utxo := &au[i]
v1[i] = AddressUtxoV1{ v1[i] = AddressUtxoV1{
AmountSat: utxo.AmountSat.AsBigInt(), AmountSat: utxo.AmountSat.AsBigInt(),
Amount: utxo.AmountSat.DecimalString(d), Amount: stringToFloat(utxo.AmountSat.DecimalString(d)),
Confirmations: utxo.Confirmations, Confirmations: utxo.Confirmations,
Height: utxo.Height, Height: utxo.Height,
Txid: utxo.Txid, Txid: utxo.Txid,

View File

@ -187,7 +187,7 @@ func (w *Worker) getAddressAliases(addresses map[string]struct{}) AddressAliases
// GetTransaction reads transaction data from txid // GetTransaction reads transaction data from txid
func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool) (*Tx, error) { func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool) (*Tx, error) {
addresses := w.newAddressesMapForAliases() addresses := w.newAddressesMapForAliases()
tx, err := w.getTransaction(txid, spendingTxs, specificJSON, addresses) tx, err := w.getTransaction(txid, spendingTxs, /*specificJSON*/ true, addresses)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1159,7 +1159,7 @@ func (w *Worker) txFromTxid(txid string, bestHeight uint32, option AccountDetail
if ta == nil { if ta == nil {
glog.Warning("DB inconsistency: tx ", txid, ": not found in txAddresses") glog.Warning("DB inconsistency: tx ", txid, ": not found in txAddresses")
// as fallback, get tx from backend // as fallback, get tx from backend
tx, err = w.getTransaction(txid, false, false, addresses) tx, err = w.getTransaction(txid, false, true, addresses)
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "getTransaction %v", txid) return nil, errors.Annotatef(err, "getTransaction %v", txid)
} }
@ -1178,7 +1178,7 @@ func (w *Worker) txFromTxid(txid string, bestHeight uint32, option AccountDetail
tx = w.txFromTxAddress(txid, ta, blockInfo, bestHeight, addresses) tx = w.txFromTxAddress(txid, ta, blockInfo, bestHeight, addresses)
} }
} else { } else {
tx, err = w.getTransaction(txid, false, false, addresses) tx, err = w.getTransaction(txid, false, true, addresses)
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "getTransaction %v", txid) return nil, errors.Annotatef(err, "getTransaction %v", txid)
} }
@ -1337,7 +1337,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
if option == AccountDetailsTxidHistory { if option == AccountDetailsTxidHistory {
txids = append(txids, txid) txids = append(txids, txid)
} else { } else {
tx, err := w.txFromTxid(txid, bestheight, option, nil, addresses) tx, err := w.txFromTxid(txid, bestheight, AccountDetailsTxHistory, nil, addresses)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -2146,7 +2146,7 @@ func (w *Worker) GetBlock(bid string, page int, txsOnPage int) (*Block, error) {
txi := 0 txi := 0
addresses := w.newAddressesMapForAliases() addresses := w.newAddressesMapForAliases()
for i := from; i < to; i++ { for i := from; i < to; i++ {
txs[txi], err = w.txFromTxid(bi.Txids[i], bestheight, AccountDetailsTxHistoryLight, dbi, addresses) txs[txi], err = w.txFromTxid(bi.Txids[i], bestheight, AccountDetailsTxHistory, dbi, addresses)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -73,6 +73,19 @@
</span> </span>
{{amountSpan $tx.ValueOutSat $data "txvalue copyable"}} {{amountSpan $tx.ValueOutSat $data "txvalue copyable"}}
</div> </div>
<div class="col-sm-12 col-md-4">
<span class="fw-bold">floData: </span><span class="fst-normal"></span>
<script type="text/javascript">
(function(){
let _script = document.currentScript;
let floData = ({{$tx.CoinSpecificData}}).floData;
if(floData) {
_script.previousElementSibling.textContent = floData;
_script.remove();
} else _script.parentElement.remove();
})()
</script>
</div>
</div> </div>
</div> </div>
{{end}} {{end}}