Return inputs for socket.io getAddressHistory and getDetailedTransaction
This commit is contained in:
parent
eb5781f218
commit
f9809d7507
33
api/types.go
33
api/types.go
@ -64,21 +64,24 @@ type Vout struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Tx struct {
|
type Tx 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,omitempty"`
|
||||||
Vin []Vin `json:"vin"`
|
Vin []Vin `json:"vin"`
|
||||||
Vout []Vout `json:"vout"`
|
Vout []Vout `json:"vout"`
|
||||||
Blockhash string `json:"blockhash,omitempty"`
|
Blockhash string `json:"blockhash,omitempty"`
|
||||||
Blockheight int `json:"blockheight"`
|
Blockheight int `json:"blockheight"`
|
||||||
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 string `json:"valueOut"`
|
||||||
Size int `json:"size,omitempty"`
|
ValueOutSat big.Int `json:"-"`
|
||||||
ValueIn string `json:"valueIn"`
|
Size int `json:"size,omitempty"`
|
||||||
Fees string `json:"fees"`
|
ValueIn string `json:"valueIn"`
|
||||||
Hex string `json:"hex"`
|
ValueInSat big.Int `json:"-"`
|
||||||
|
Fees string `json:"fees"`
|
||||||
|
FeesSat big.Int `json:"-"`
|
||||||
|
Hex string `json:"hex"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Paging struct {
|
type Paging struct {
|
||||||
|
|||||||
@ -125,6 +125,7 @@ func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTxs bool
|
|||||||
vin.Txid = bchainVin.Txid
|
vin.Txid = bchainVin.Txid
|
||||||
vin.N = i
|
vin.N = i
|
||||||
vin.Vout = bchainVin.Vout
|
vin.Vout = bchainVin.Vout
|
||||||
|
vin.Sequence = int64(bchainVin.Sequence)
|
||||||
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 != "" {
|
||||||
@ -200,11 +201,14 @@ func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTxs bool
|
|||||||
Blocktime: bchainTx.Blocktime,
|
Blocktime: bchainTx.Blocktime,
|
||||||
Confirmations: bchainTx.Confirmations,
|
Confirmations: bchainTx.Confirmations,
|
||||||
Fees: w.chainParser.AmountToDecimalString(&feesSat),
|
Fees: w.chainParser.AmountToDecimalString(&feesSat),
|
||||||
|
FeesSat: feesSat,
|
||||||
Locktime: bchainTx.LockTime,
|
Locktime: bchainTx.LockTime,
|
||||||
Time: bchainTx.Time,
|
Time: bchainTx.Time,
|
||||||
Txid: bchainTx.Txid,
|
Txid: bchainTx.Txid,
|
||||||
ValueIn: w.chainParser.AmountToDecimalString(&valInSat),
|
ValueIn: w.chainParser.AmountToDecimalString(&valInSat),
|
||||||
|
ValueInSat: valInSat,
|
||||||
ValueOut: w.chainParser.AmountToDecimalString(&valOutSat),
|
ValueOut: w.chainParser.AmountToDecimalString(&valOutSat),
|
||||||
|
ValueOutSat: valOutSat,
|
||||||
Version: bchainTx.Version,
|
Version: bchainTx.Version,
|
||||||
Hex: bchainTx.Hex,
|
Hex: bchainTx.Hex,
|
||||||
Vin: vins,
|
Vin: vins,
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import (
|
|||||||
"blockbook/common"
|
"blockbook/common"
|
||||||
"blockbook/db"
|
"blockbook/db"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"math/big"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -27,10 +28,16 @@ type SocketIoServer struct {
|
|||||||
chainParser bchain.BlockChainParser
|
chainParser bchain.BlockChainParser
|
||||||
metrics *common.Metrics
|
metrics *common.Metrics
|
||||||
is *common.InternalState
|
is *common.InternalState
|
||||||
|
api *api.Worker
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSocketIoServer creates new SocketIo interface to blockbook and returns its handle
|
// NewSocketIoServer creates new SocketIo interface to blockbook and returns its handle
|
||||||
func NewSocketIoServer(db *db.RocksDB, chain bchain.BlockChain, txCache *db.TxCache, metrics *common.Metrics, is *common.InternalState) (*SocketIoServer, error) {
|
func NewSocketIoServer(db *db.RocksDB, chain bchain.BlockChain, txCache *db.TxCache, metrics *common.Metrics, is *common.InternalState) (*SocketIoServer, error) {
|
||||||
|
api, err := api.NewWorker(db, chain, txCache, is)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
server := gosocketio.NewServer(transport.GetDefaultWebsocketTransport())
|
server := gosocketio.NewServer(transport.GetDefaultWebsocketTransport())
|
||||||
|
|
||||||
server.On(gosocketio.OnConnection, func(c *gosocketio.Channel) {
|
server.On(gosocketio.OnConnection, func(c *gosocketio.Channel) {
|
||||||
@ -59,6 +66,7 @@ func NewSocketIoServer(db *db.RocksDB, chain bchain.BlockChain, txCache *db.TxCa
|
|||||||
chainParser: chain.GetChainParser(),
|
chainParser: chain.GetChainParser(),
|
||||||
metrics: metrics,
|
metrics: metrics,
|
||||||
is: is,
|
is: is,
|
||||||
|
api: api,
|
||||||
}
|
}
|
||||||
|
|
||||||
server.On("message", s.onMessage)
|
server.On("message", s.onMessage)
|
||||||
@ -244,33 +252,33 @@ type txOutputs struct {
|
|||||||
Satoshis int64 `json:"satoshis"`
|
Satoshis int64 `json:"satoshis"`
|
||||||
Script *string `json:"script"`
|
Script *string `json:"script"`
|
||||||
// ScriptAsm *string `json:"scriptAsm"`
|
// ScriptAsm *string `json:"scriptAsm"`
|
||||||
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"`
|
||||||
Address *string `json:"address"`
|
Address *string `json:"address"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type resTx struct {
|
type resTx struct {
|
||||||
Hex string `json:"hex"`
|
Hex string `json:"hex"`
|
||||||
// BlockHash string `json:"blockHash,omitempty"`
|
// BlockHash string `json:"blockHash,omitempty"`
|
||||||
Height int `json:"height"`
|
Height int `json:"height"`
|
||||||
BlockTimestamp int64 `json:"blockTimestamp,omitempty"`
|
BlockTimestamp int64 `json:"blockTimestamp,omitempty"`
|
||||||
// Version int `json:"version"`
|
Version int `json:"version"`
|
||||||
Hash string `json:"hash"`
|
Hash string `json:"hash"`
|
||||||
Locktime int `json:"locktime,omitempty"`
|
Locktime int `json:"locktime,omitempty"`
|
||||||
// Size int `json:"size,omitempty"`
|
// Size int `json:"size,omitempty"`
|
||||||
Inputs []txInputs `json:"inputs"`
|
Inputs []txInputs `json:"inputs"`
|
||||||
// InputSatoshis int64 `json:"inputSatoshis,omitempty"`
|
InputSatoshis int64 `json:"inputSatoshis,omitempty"`
|
||||||
Outputs []txOutputs `json:"outputs"`
|
Outputs []txOutputs `json:"outputs"`
|
||||||
// OutputSatoshis int64 `json:"outputSatoshis,omitempty"`
|
OutputSatoshis int64 `json:"outputSatoshis,omitempty"`
|
||||||
// FeeSatoshis int64 `json:"feeSatoshis,omitempty"`
|
FeeSatoshis int64 `json:"feeSatoshis,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type addressHistoryItem struct {
|
type addressHistoryItem struct {
|
||||||
Addresses map[string]addressHistoryIndexes `json:"addresses"`
|
Addresses map[string]*addressHistoryIndexes `json:"addresses"`
|
||||||
Satoshis int64 `json:"satoshis"`
|
Satoshis int64 `json:"satoshis"`
|
||||||
Confirmations int `json:"confirmations"`
|
Confirmations int `json:"confirmations"`
|
||||||
Tx resTx `json:"tx"`
|
Tx resTx `json:"tx"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type resultGetAddressHistory struct {
|
type resultGetAddressHistory struct {
|
||||||
@ -289,20 +297,55 @@ func stringInSlice(a string, list []string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func txToResTx(tx *bchain.Tx, height int, hi []txInputs, ho []txOutputs) resTx {
|
func txToResTx(tx *api.Tx) resTx {
|
||||||
|
inputs := make([]txInputs, len(tx.Vin))
|
||||||
|
for i := range tx.Vin {
|
||||||
|
vin := &tx.Vin[i]
|
||||||
|
script := vin.ScriptSig.Hex
|
||||||
|
input := txInputs{
|
||||||
|
Script: &script,
|
||||||
|
Sequence: int64(vin.Sequence),
|
||||||
|
OutputIndex: int(vin.Vout),
|
||||||
|
Satoshis: vin.ValueSat.Int64(),
|
||||||
|
}
|
||||||
|
if len(vin.Addresses) > 0 {
|
||||||
|
a := vin.Addresses[0]
|
||||||
|
input.Address = &a
|
||||||
|
}
|
||||||
|
inputs[i] = input
|
||||||
|
}
|
||||||
|
outputs := make([]txOutputs, len(tx.Vout))
|
||||||
|
for i := range tx.Vout {
|
||||||
|
vout := &tx.Vout[i]
|
||||||
|
script := vout.ScriptPubKey.Hex
|
||||||
|
output := txOutputs{
|
||||||
|
Satoshis: vout.ValueSat.Int64(),
|
||||||
|
Script: &script,
|
||||||
|
}
|
||||||
|
if len(vout.ScriptPubKey.Addresses) > 0 {
|
||||||
|
a := vout.ScriptPubKey.Addresses[0]
|
||||||
|
output.Address = &a
|
||||||
|
}
|
||||||
|
outputs[i] = output
|
||||||
|
}
|
||||||
|
var h int
|
||||||
|
if tx.Confirmations == 0 {
|
||||||
|
h = -1
|
||||||
|
} else {
|
||||||
|
h = int(tx.Blockheight)
|
||||||
|
}
|
||||||
return resTx{
|
return resTx{
|
||||||
// BlockHash: tx.BlockHash,
|
|
||||||
BlockTimestamp: tx.Blocktime,
|
BlockTimestamp: tx.Blocktime,
|
||||||
// FeeSatoshis,
|
FeeSatoshis: tx.FeesSat.Int64(),
|
||||||
Hash: tx.Txid,
|
Hash: tx.Txid,
|
||||||
Height: height,
|
Height: h,
|
||||||
Hex: tx.Hex,
|
Hex: tx.Hex,
|
||||||
Inputs: hi,
|
Inputs: inputs,
|
||||||
// InputSatoshis,
|
InputSatoshis: tx.ValueInSat.Int64(),
|
||||||
Locktime: int(tx.LockTime),
|
Locktime: int(tx.Locktime),
|
||||||
Outputs: ho,
|
Outputs: outputs,
|
||||||
// OutputSatoshis,
|
OutputSatoshis: tx.ValueOutSat.Int64(),
|
||||||
// Version: int(tx.Version),
|
Version: int(tx.Version),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -341,54 +384,53 @@ func (s *SocketIoServer) getAddressHistory(addr []string, opts *addrOpts) (res r
|
|||||||
txids := txr.Result
|
txids := txr.Result
|
||||||
res.Result.TotalCount = len(txids)
|
res.Result.TotalCount = len(txids)
|
||||||
res.Result.Items = make([]addressHistoryItem, 0)
|
res.Result.Items = make([]addressHistoryItem, 0)
|
||||||
for i, txid := range txids {
|
to := len(txids)
|
||||||
if i >= opts.From && i < opts.To {
|
if to > opts.To {
|
||||||
tx, height, err := s.txCache.GetTransaction(txid, bestheight)
|
to = opts.To
|
||||||
if err != nil {
|
}
|
||||||
return res, err
|
for txi := opts.From; txi < to; txi++ {
|
||||||
}
|
tx, err := s.api.GetTransaction(txids[txi], bestheight, false)
|
||||||
ads := make(map[string]addressHistoryIndexes)
|
// for i, txid := range txids {
|
||||||
hi := make([]txInputs, 0)
|
// if i >= opts.From && i < opts.To {
|
||||||
ho := make([]txOutputs, 0)
|
// tx, err := s.api.GetTransaction(txid, bestheight, false)
|
||||||
for _, vout := range tx.Vout {
|
if err != nil {
|
||||||
aoh := vout.ScriptPubKey.Hex
|
return res, err
|
||||||
ao := txOutputs{
|
|
||||||
Satoshis: vout.ValueSat.Int64(),
|
|
||||||
Script: &aoh,
|
|
||||||
}
|
|
||||||
voutAddr, err := s.getAddressesFromVout(&vout)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
if len(voutAddr) > 0 {
|
|
||||||
ao.Address = &voutAddr[0]
|
|
||||||
}
|
|
||||||
a := addressInSlice(voutAddr, addr)
|
|
||||||
if a != "" {
|
|
||||||
hi, ok := ads[a]
|
|
||||||
if ok {
|
|
||||||
hi.OutputIndexes = append(hi.OutputIndexes, int(vout.N))
|
|
||||||
} else {
|
|
||||||
hi := addressHistoryIndexes{}
|
|
||||||
hi.InputIndexes = make([]int, 0)
|
|
||||||
hi.OutputIndexes = append(hi.OutputIndexes, int(vout.N))
|
|
||||||
ads[a] = hi
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ho = append(ho, ao)
|
|
||||||
}
|
|
||||||
ahi := addressHistoryItem{}
|
|
||||||
ahi.Addresses = ads
|
|
||||||
ahi.Confirmations = int(tx.Confirmations)
|
|
||||||
var h int
|
|
||||||
if tx.Confirmations == 0 {
|
|
||||||
h = -1
|
|
||||||
} else {
|
|
||||||
h = int(height)
|
|
||||||
}
|
|
||||||
ahi.Tx = txToResTx(tx, h, hi, ho)
|
|
||||||
res.Result.Items = append(res.Result.Items, ahi)
|
|
||||||
}
|
}
|
||||||
|
ads := make(map[string]*addressHistoryIndexes)
|
||||||
|
var totalSat big.Int
|
||||||
|
for i := range tx.Vin {
|
||||||
|
vin := &tx.Vin[i]
|
||||||
|
a := addressInSlice(vin.Addresses, addr)
|
||||||
|
if a != "" {
|
||||||
|
hi := ads[a]
|
||||||
|
if hi == nil {
|
||||||
|
hi = &addressHistoryIndexes{OutputIndexes: []int{}}
|
||||||
|
ads[a] = hi
|
||||||
|
}
|
||||||
|
hi.InputIndexes = append(hi.InputIndexes, int(vin.N))
|
||||||
|
totalSat.Sub(&totalSat, &vin.ValueSat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := range tx.Vout {
|
||||||
|
vout := &tx.Vout[i]
|
||||||
|
a := addressInSlice(vout.ScriptPubKey.Addresses, addr)
|
||||||
|
if a != "" {
|
||||||
|
hi := ads[a]
|
||||||
|
if hi == nil {
|
||||||
|
hi = &addressHistoryIndexes{InputIndexes: []int{}}
|
||||||
|
ads[a] = hi
|
||||||
|
}
|
||||||
|
hi.OutputIndexes = append(hi.OutputIndexes, int(vout.N))
|
||||||
|
totalSat.Add(&totalSat, &vout.ValueSat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ahi := addressHistoryItem{}
|
||||||
|
ahi.Addresses = ads
|
||||||
|
ahi.Confirmations = int(tx.Confirmations)
|
||||||
|
ahi.Satoshis = totalSat.Int64()
|
||||||
|
ahi.Tx = txToResTx(tx)
|
||||||
|
res.Result.Items = append(res.Result.Items, ahi)
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -594,78 +636,11 @@ func (s *SocketIoServer) getDetailedTransaction(txid string) (res resultGetDetai
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tx, height, err := s.txCache.GetTransaction(txid, bestheight)
|
tx, err := s.api.GetTransaction(txid, bestheight, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
hi := make([]txInputs, 0)
|
res.Result = txToResTx(tx)
|
||||||
ho := make([]txOutputs, 0)
|
|
||||||
for _, vin := range tx.Vin {
|
|
||||||
ais := vin.ScriptSig.Hex
|
|
||||||
ai := txInputs{
|
|
||||||
Script: &ais,
|
|
||||||
Sequence: int64(vin.Sequence),
|
|
||||||
OutputIndex: int(vin.Vout),
|
|
||||||
}
|
|
||||||
if vin.Txid != "" {
|
|
||||||
var voutAddr []string
|
|
||||||
// load spending addresses from TxAddresses
|
|
||||||
ta, err := s.db.GetTxAddresses(vin.Txid)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
if ta == nil {
|
|
||||||
// the tx may be in mempool, try to load it from backend
|
|
||||||
otx, _, err := s.txCache.GetTransaction(vin.Txid, bestheight)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
if len(otx.Vout) > int(vin.Vout) {
|
|
||||||
vout := &otx.Vout[vin.Vout]
|
|
||||||
voutAddr, err = s.getAddressesFromVout(vout)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
ai.Satoshis = vout.ValueSat.Int64()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if len(ta.Outputs) > int(vin.Vout) {
|
|
||||||
output := &ta.Outputs[vin.Vout]
|
|
||||||
ai.Satoshis = output.ValueSat.Int64()
|
|
||||||
voutAddr, _, err = output.Addresses(s.chainParser)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(voutAddr) > 0 {
|
|
||||||
ai.Address = &voutAddr[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hi = append(hi, ai)
|
|
||||||
}
|
|
||||||
for _, vout := range tx.Vout {
|
|
||||||
aos := vout.ScriptPubKey.Hex
|
|
||||||
ao := txOutputs{
|
|
||||||
Satoshis: vout.ValueSat.Int64(),
|
|
||||||
Script: &aos,
|
|
||||||
}
|
|
||||||
voutAddr, err := s.getAddressesFromVout(&vout)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
if len(voutAddr) > 0 {
|
|
||||||
ao.Address = &voutAddr[0]
|
|
||||||
}
|
|
||||||
ho = append(ho, ao)
|
|
||||||
}
|
|
||||||
var h int
|
|
||||||
if tx.Confirmations == 0 {
|
|
||||||
h = -1
|
|
||||||
} else {
|
|
||||||
h = int(height)
|
|
||||||
}
|
|
||||||
res.Result = txToResTx(tx, h, hi, ho)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
// +build integration
|
// build integration
|
||||||
|
|
||||||
package server
|
package server
|
||||||
|
|
||||||
@ -8,6 +8,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"os"
|
"os"
|
||||||
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@ -94,16 +95,74 @@ func getFullAddressHistory(addr []string, rr addrOpts, ws *gosocketio.Client) (*
|
|||||||
return &bbResponse, nil
|
return &bbResponse, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func equalAddressHistoryItem(logItem addressHistoryItem, bbItem addressHistoryItem) error {
|
func equalTx(logTx resTx, bbTx resTx) error {
|
||||||
if logItem.Tx.Hash != bbItem.Tx.Hash {
|
if logTx.Hash != bbTx.Hash {
|
||||||
return errors.Errorf("Different hash bb: %v log: %v", bbItem.Tx.Hash, logItem.Tx.Hash)
|
return errors.Errorf("Different Hash bb: %v log: %v", bbTx.Hash, logTx.Hash)
|
||||||
}
|
}
|
||||||
if logItem.Tx.Hex != bbItem.Tx.Hex {
|
if logTx.Hex != bbTx.Hex {
|
||||||
return errors.Errorf("Different hex bb: %v log: %v", bbItem.Tx.Hex, logItem.Tx.Hex)
|
return errors.Errorf("Different Hex bb: %v log: %v", bbTx.Hex, logTx.Hex)
|
||||||
|
}
|
||||||
|
if logTx.BlockTimestamp != bbTx.BlockTimestamp {
|
||||||
|
return errors.Errorf("Different BlockTimestamp bb: %v log: %v", bbTx.BlockTimestamp, logTx.BlockTimestamp)
|
||||||
|
}
|
||||||
|
if logTx.FeeSatoshis != bbTx.FeeSatoshis {
|
||||||
|
return errors.Errorf("Different FeeSatoshis bb: %v log: %v", bbTx.FeeSatoshis, logTx.FeeSatoshis)
|
||||||
|
}
|
||||||
|
if logTx.Height != bbTx.Height {
|
||||||
|
return errors.Errorf("Different Height bb: %v log: %v", bbTx.Height, logTx.Height)
|
||||||
|
}
|
||||||
|
if logTx.InputSatoshis != bbTx.InputSatoshis {
|
||||||
|
return errors.Errorf("Different InputSatoshis bb: %v log: %v", bbTx.InputSatoshis, logTx.InputSatoshis)
|
||||||
|
}
|
||||||
|
if logTx.Locktime != bbTx.Locktime {
|
||||||
|
return errors.Errorf("Different Locktime bb: %v log: %v", bbTx.Locktime, logTx.Locktime)
|
||||||
|
}
|
||||||
|
if logTx.OutputSatoshis != bbTx.OutputSatoshis {
|
||||||
|
return errors.Errorf("Different OutputSatoshis bb: %v log: %v", bbTx.OutputSatoshis, logTx.OutputSatoshis)
|
||||||
|
}
|
||||||
|
if logTx.Version != bbTx.Version {
|
||||||
|
return errors.Errorf("Different Version bb: %v log: %v", bbTx.Version, logTx.Version)
|
||||||
|
}
|
||||||
|
if len(logTx.Inputs) != len(bbTx.Inputs) {
|
||||||
|
return errors.Errorf("Different number of Inputs bb: %v log: %v", len(bbTx.Inputs), len(logTx.Inputs))
|
||||||
|
}
|
||||||
|
// blockbook parses bech addresses, it is ok for bitcore to return nil address and blockbook parsed address
|
||||||
|
for i := range logTx.Inputs {
|
||||||
|
if logTx.Inputs[i].Satoshis != bbTx.Inputs[i].Satoshis ||
|
||||||
|
(bbTx.Inputs[i].Address == nil && logTx.Inputs[i].Address != bbTx.Inputs[i].Address) ||
|
||||||
|
(logTx.Inputs[i].Address != nil && *logTx.Inputs[i].Address != *bbTx.Inputs[i].Address) ||
|
||||||
|
logTx.Inputs[i].OutputIndex != bbTx.Inputs[i].OutputIndex ||
|
||||||
|
logTx.Inputs[i].Sequence != bbTx.Inputs[i].Sequence {
|
||||||
|
return errors.Errorf("Different Inputs bb: %+v log: %+v", bbTx.Inputs, logTx.Inputs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(logTx.Outputs) != len(bbTx.Outputs) {
|
||||||
|
return errors.Errorf("Different number of Outputs bb: %v log: %v", len(bbTx.Outputs), len(logTx.Outputs))
|
||||||
|
}
|
||||||
|
// blockbook parses bech addresses, it is ok for bitcore to return nil address and blockbook parsed address
|
||||||
|
for i := range logTx.Outputs {
|
||||||
|
if logTx.Outputs[i].Satoshis != bbTx.Outputs[i].Satoshis ||
|
||||||
|
(bbTx.Outputs[i].Address == nil && logTx.Outputs[i].Address != bbTx.Outputs[i].Address) ||
|
||||||
|
(logTx.Outputs[i].Address != nil && *logTx.Outputs[i].Address != *bbTx.Outputs[i].Address) {
|
||||||
|
return errors.Errorf("Different Outputs bb: %+v log: %+v", bbTx.Outputs, logTx.Outputs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Addresses do not match, bb getAddressHistory does not return input addresses
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func equalAddressHistoryItem(logItem addressHistoryItem, bbItem addressHistoryItem) error {
|
||||||
|
if err := equalTx(logItem.Tx, bbItem.Tx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(logItem.Addresses, bbItem.Addresses) {
|
||||||
|
return errors.Errorf("Different Addresses bb: %v log: %v", bbItem.Addresses, logItem.Addresses)
|
||||||
|
}
|
||||||
|
if logItem.Satoshis != bbItem.Satoshis {
|
||||||
|
return errors.Errorf("Different Satoshis bb: %v log: %v", bbItem.Satoshis, logItem.Satoshis)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func verifyGetAddressHistory(t *testing.T, id int, lrs *logRequestResponse, bbResStr string, stat *verifyStats, ws *gosocketio.Client, bbRequest map[string]json.RawMessage) {
|
func verifyGetAddressHistory(t *testing.T, id int, lrs *logRequestResponse, bbResStr string, stat *verifyStats, ws *gosocketio.Client, bbRequest map[string]json.RawMessage) {
|
||||||
bbResponse := resultGetAddressHistory{}
|
bbResponse := resultGetAddressHistory{}
|
||||||
logResponse := resultGetAddressHistory{}
|
logResponse := resultGetAddressHistory{}
|
||||||
@ -146,16 +205,17 @@ func verifyGetAddressHistory(t *testing.T, id int, lrs *logRequestResponse, bbRe
|
|||||||
}
|
}
|
||||||
found := false
|
found := false
|
||||||
for _, bbFullItem := range bbFullResponse.Result.Items {
|
for _, bbFullItem := range bbFullResponse.Result.Items {
|
||||||
if err1 = equalAddressHistoryItem(logItem, bbFullItem); err1 == nil {
|
err1 = equalAddressHistoryItem(logItem, bbFullItem)
|
||||||
|
if err1 == nil {
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
if err1.Error()[:14] != "Different Hash" {
|
||||||
|
t.Log(err1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
t.Log("getAddressHistory", id, "addresses", addr, "mismatch ", err)
|
t.Log("getAddressHistory", id, "addresses", addr, "mismatch ", err)
|
||||||
// bf, _ := json.Marshal(bbFullResponse.Result)
|
|
||||||
// bl, _ := json.Marshal(logResponse.Result)
|
|
||||||
// t.Log("{ \"bf\":", string(bf), ",\"bl\":", string(bl), "}")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -237,94 +297,7 @@ func verifyGetDetailedTransaction(t *testing.T, id int, lrs *logRequestResponse,
|
|||||||
if err := unmarshalResponses(t, id, lrs, bbResStr, &bbResponse, &logResponse); err != nil {
|
if err := unmarshalResponses(t, id, lrs, bbResStr, &bbResponse, &logResponse); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
equalInputs := func() error {
|
if err := equalTx(logResponse.Result, bbResponse.Result); err != nil {
|
||||||
if len(bbResponse.Result.Inputs) != len(logResponse.Result.Inputs) {
|
|
||||||
return errors.Errorf("mismatch number of inputs %v %v", len(bbResponse.Result.Inputs), len(logResponse.Result.Inputs))
|
|
||||||
}
|
|
||||||
for i, bbi := range bbResponse.Result.Inputs {
|
|
||||||
li := logResponse.Result.Inputs[i]
|
|
||||||
|
|
||||||
if bbi.OutputIndex != li.OutputIndex ||
|
|
||||||
bbi.Sequence != li.Sequence ||
|
|
||||||
bbi.Satoshis != li.Satoshis {
|
|
||||||
return errors.Errorf("mismatch input %v %v, %v %v, %v %v", bbi.OutputIndex, li.OutputIndex, bbi.Sequence, li.Sequence, bbi.Satoshis, li.Satoshis)
|
|
||||||
}
|
|
||||||
// both must be null or both must not be null
|
|
||||||
if bbi.Address != nil && li.Address != nil {
|
|
||||||
if *bbi.Address != *li.Address {
|
|
||||||
return errors.Errorf("mismatch input Address %v %v", *bbi.Address, *li.Address)
|
|
||||||
}
|
|
||||||
} else if bbi.Address != li.Address {
|
|
||||||
// bitcore does not parse bech P2WPKH and P2WSH addresses
|
|
||||||
if bbi.Address == nil || (*bbi.Address)[0:3] != "bc1" {
|
|
||||||
return errors.Errorf("mismatch input Address %v %v", bbi.Address, li.Address)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// both must be null or both must not be null
|
|
||||||
if bbi.Script != nil && li.Script != nil {
|
|
||||||
if *bbi.Script != *li.Script {
|
|
||||||
return errors.Errorf("mismatch input Script %v %v", *bbi.Script, *li.Script)
|
|
||||||
}
|
|
||||||
} else if bbi.Script != li.Script {
|
|
||||||
return errors.Errorf("mismatch input Script %v %v", bbi.Script, li.Script)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
equalOutputs := func() error {
|
|
||||||
if len(bbResponse.Result.Outputs) != len(logResponse.Result.Outputs) {
|
|
||||||
return errors.Errorf("mismatch number of outputs %v %v", len(bbResponse.Result.Outputs), len(logResponse.Result.Outputs))
|
|
||||||
}
|
|
||||||
for i, bbo := range bbResponse.Result.Outputs {
|
|
||||||
lo := logResponse.Result.Outputs[i]
|
|
||||||
if bbo.Satoshis != lo.Satoshis {
|
|
||||||
return errors.Errorf("mismatch output Satoshis %v %v", bbo.Satoshis, lo.Satoshis)
|
|
||||||
}
|
|
||||||
// both must be null or both must not be null
|
|
||||||
if bbo.Script != nil && lo.Script != nil {
|
|
||||||
if *bbo.Script != *lo.Script {
|
|
||||||
return errors.Errorf("mismatch output Script %v %v", *bbo.Script, *lo.Script)
|
|
||||||
}
|
|
||||||
} else if bbo.Script != lo.Script {
|
|
||||||
return errors.Errorf("mismatch output Script %v %v", bbo.Script, lo.Script)
|
|
||||||
}
|
|
||||||
// both must be null or both must not be null
|
|
||||||
if bbo.Address != nil && lo.Address != nil {
|
|
||||||
if *bbo.Address != *lo.Address {
|
|
||||||
return errors.Errorf("mismatch output Address %v %v", *bbo.Address, *lo.Address)
|
|
||||||
}
|
|
||||||
} else if bbo.Address != lo.Address {
|
|
||||||
// bitcore does not parse bech P2WPKH and P2WSH addresses
|
|
||||||
if bbo.Address == nil || (*bbo.Address)[0:3] != "bc1" {
|
|
||||||
return errors.Errorf("mismatch output Address %v %v", bbo.Address, lo.Address)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// the tx in the log could have been still in mempool with Height -1
|
|
||||||
if (bbResponse.Result.Height != logResponse.Result.Height && logResponse.Result.Height != -1) ||
|
|
||||||
bbResponse.Result.Hash != logResponse.Result.Hash {
|
|
||||||
t.Log("getDetailedTransaction", id, "mismatch bb:", bbResponse.Result.Hash, bbResponse.Result.Height,
|
|
||||||
"log:", logResponse.Result.Hash, logResponse.Result.Height)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// the tx in the log could have been still in mempool with BlockTimestamp 0
|
|
||||||
if bbResponse.Result.BlockTimestamp != logResponse.Result.BlockTimestamp && logResponse.Result.BlockTimestamp != 0 {
|
|
||||||
t.Log("getDetailedTransaction", id, "mismatch BlockTimestamp:", bbResponse.Result.BlockTimestamp,
|
|
||||||
"log:", logResponse.Result.BlockTimestamp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if bbResponse.Result.Hex != logResponse.Result.Hex {
|
|
||||||
t.Log("getDetailedTransaction", id, "mismatch Hex:", bbResponse.Result.Hex,
|
|
||||||
"log:", logResponse.Result.Hex)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := equalInputs(); err != nil {
|
|
||||||
t.Log("getDetailedTransaction", id, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := equalOutputs(); err != nil {
|
|
||||||
t.Log("getDetailedTransaction", id, err)
|
t.Log("getDetailedTransaction", id, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,7 +55,9 @@
|
|||||||
var addresses = document.getElementById('getAddressHistoryAddresses').value.split(",");
|
var addresses = document.getElementById('getAddressHistoryAddresses').value.split(",");
|
||||||
addresses = addresses.map(s => s.trim());
|
addresses = addresses.map(s => s.trim());
|
||||||
var mempool = document.getElementById("getAddressHistoryMempool").checked;
|
var mempool = document.getElementById("getAddressHistoryMempool").checked;
|
||||||
lookupAddressHistories(addresses, 0, 5, mempool, 20000000, 0, function (result) {
|
var from = parseInt(document.getElementById("getAddressHistoryFrom").value);
|
||||||
|
var to = parseInt(document.getElementById("getAddressHistoryTo").value);
|
||||||
|
lookupAddressHistories(addresses, from, to, mempool, 90000000, 0, function (result) {
|
||||||
console.log('getAddressHistory sent successfully');
|
console.log('getAddressHistory sent successfully');
|
||||||
console.log(result);
|
console.log(result);
|
||||||
document.getElementById('getAddressHistoryResult').innerText = JSON.stringify(result).replace(/,/g, ", ");
|
document.getElementById('getAddressHistoryResult').innerText = JSON.stringify(result).replace(/,/g, ", ");
|
||||||
@ -283,7 +285,11 @@
|
|||||||
<input class="btn btn-secondary" type="button" value="getAddressHistory" onclick="getAddressHistory()">
|
<input class="btn btn-secondary" type="button" value="getAddressHistory" onclick="getAddressHistory()">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8">
|
<div class="col-8">
|
||||||
<input type="text" class="form-control" id="getAddressHistoryAddresses" value="2N4Q5FhU2497BryFfUgbqkAJE87aKHUhXMp,2Mt7P2BAfE922zmfXrdcYTLyR7GUvbwSEns">
|
<div class="row" style="margin: 0;">
|
||||||
|
<input type="text" style="width: 84%" class="form-control" id="getAddressHistoryAddresses" value="2N4Q5FhU2497BryFfUgbqkAJE87aKHUhXMp,2Mt7P2BAfE922zmfXrdcYTLyR7GUvbwSEns">
|
||||||
|
<input type="text" style="width: 7%; margin-left: 5px; margin-right: 5px;" class="form-control" id="getAddressHistoryFrom" value="0">
|
||||||
|
<input type="text" style="width: 7%" class="form-control" id="getAddressHistoryTo" value="5">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col form-inline">
|
<div class="col form-inline">
|
||||||
<input type="checkbox" id="getAddressHistoryMempool">
|
<input type="checkbox" id="getAddressHistoryMempool">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user