Return inputs for socket.io getAddressHistory and getDetailedTransaction
parent
eb5781f218
commit
f9809d7507
33
api/types.go
33
api/types.go
|
@ -64,21 +64,24 @@ type Vout struct {
|
|||
}
|
||||
|
||||
type Tx struct {
|
||||
Txid string `json:"txid"`
|
||||
Version int32 `json:"version,omitempty"`
|
||||
Locktime uint32 `json:"locktime,omitempty"`
|
||||
Vin []Vin `json:"vin"`
|
||||
Vout []Vout `json:"vout"`
|
||||
Blockhash string `json:"blockhash,omitempty"`
|
||||
Blockheight int `json:"blockheight"`
|
||||
Confirmations uint32 `json:"confirmations"`
|
||||
Time int64 `json:"time,omitempty"`
|
||||
Blocktime int64 `json:"blocktime"`
|
||||
ValueOut string `json:"valueOut"`
|
||||
Size int `json:"size,omitempty"`
|
||||
ValueIn string `json:"valueIn"`
|
||||
Fees string `json:"fees"`
|
||||
Hex string `json:"hex"`
|
||||
Txid string `json:"txid"`
|
||||
Version int32 `json:"version,omitempty"`
|
||||
Locktime uint32 `json:"locktime,omitempty"`
|
||||
Vin []Vin `json:"vin"`
|
||||
Vout []Vout `json:"vout"`
|
||||
Blockhash string `json:"blockhash,omitempty"`
|
||||
Blockheight int `json:"blockheight"`
|
||||
Confirmations uint32 `json:"confirmations"`
|
||||
Time int64 `json:"time,omitempty"`
|
||||
Blocktime int64 `json:"blocktime"`
|
||||
ValueOut string `json:"valueOut"`
|
||||
ValueOutSat big.Int `json:"-"`
|
||||
Size int `json:"size,omitempty"`
|
||||
ValueIn string `json:"valueIn"`
|
||||
ValueInSat big.Int `json:"-"`
|
||||
Fees string `json:"fees"`
|
||||
FeesSat big.Int `json:"-"`
|
||||
Hex string `json:"hex"`
|
||||
}
|
||||
|
||||
type Paging struct {
|
||||
|
|
|
@ -125,6 +125,7 @@ func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTxs bool
|
|||
vin.Txid = bchainVin.Txid
|
||||
vin.N = i
|
||||
vin.Vout = bchainVin.Vout
|
||||
vin.Sequence = int64(bchainVin.Sequence)
|
||||
vin.ScriptSig.Hex = bchainVin.ScriptSig.Hex
|
||||
// bchainVin.Txid=="" is coinbase transaction
|
||||
if bchainVin.Txid != "" {
|
||||
|
@ -200,11 +201,14 @@ func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTxs bool
|
|||
Blocktime: bchainTx.Blocktime,
|
||||
Confirmations: bchainTx.Confirmations,
|
||||
Fees: w.chainParser.AmountToDecimalString(&feesSat),
|
||||
FeesSat: feesSat,
|
||||
Locktime: bchainTx.LockTime,
|
||||
Time: bchainTx.Time,
|
||||
Txid: bchainTx.Txid,
|
||||
ValueIn: w.chainParser.AmountToDecimalString(&valInSat),
|
||||
ValueInSat: valInSat,
|
||||
ValueOut: w.chainParser.AmountToDecimalString(&valOutSat),
|
||||
ValueOutSat: valOutSat,
|
||||
Version: bchainTx.Version,
|
||||
Hex: bchainTx.Hex,
|
||||
Vin: vins,
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"blockbook/common"
|
||||
"blockbook/db"
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -27,10 +28,16 @@ type SocketIoServer struct {
|
|||
chainParser bchain.BlockChainParser
|
||||
metrics *common.Metrics
|
||||
is *common.InternalState
|
||||
api *api.Worker
|
||||
}
|
||||
|
||||
// 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) {
|
||||
api, err := api.NewWorker(db, chain, txCache, is)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
server := gosocketio.NewServer(transport.GetDefaultWebsocketTransport())
|
||||
|
||||
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(),
|
||||
metrics: metrics,
|
||||
is: is,
|
||||
api: api,
|
||||
}
|
||||
|
||||
server.On("message", s.onMessage)
|
||||
|
@ -244,33 +252,33 @@ type txOutputs struct {
|
|||
Satoshis int64 `json:"satoshis"`
|
||||
Script *string `json:"script"`
|
||||
// ScriptAsm *string `json:"scriptAsm"`
|
||||
SpentTxID *string `json:"spentTxId,omitempty"`
|
||||
SpentIndex int `json:"spentIndex,omitempty"`
|
||||
SpentHeight int `json:"spentHeight,omitempty"`
|
||||
Address *string `json:"address"`
|
||||
// SpentTxID *string `json:"spentTxId,omitempty"`
|
||||
// SpentIndex int `json:"spentIndex,omitempty"`
|
||||
// SpentHeight int `json:"spentHeight,omitempty"`
|
||||
Address *string `json:"address"`
|
||||
}
|
||||
|
||||
type resTx struct {
|
||||
Hex string `json:"hex"`
|
||||
// BlockHash string `json:"blockHash,omitempty"`
|
||||
Height int `json:"height"`
|
||||
BlockTimestamp int64 `json:"blockTimestamp,omitempty"`
|
||||
// Version int `json:"version"`
|
||||
Hash string `json:"hash"`
|
||||
Locktime int `json:"locktime,omitempty"`
|
||||
Height int `json:"height"`
|
||||
BlockTimestamp int64 `json:"blockTimestamp,omitempty"`
|
||||
Version int `json:"version"`
|
||||
Hash string `json:"hash"`
|
||||
Locktime int `json:"locktime,omitempty"`
|
||||
// Size int `json:"size,omitempty"`
|
||||
Inputs []txInputs `json:"inputs"`
|
||||
// InputSatoshis int64 `json:"inputSatoshis,omitempty"`
|
||||
Outputs []txOutputs `json:"outputs"`
|
||||
// OutputSatoshis int64 `json:"outputSatoshis,omitempty"`
|
||||
// FeeSatoshis int64 `json:"feeSatoshis,omitempty"`
|
||||
Inputs []txInputs `json:"inputs"`
|
||||
InputSatoshis int64 `json:"inputSatoshis,omitempty"`
|
||||
Outputs []txOutputs `json:"outputs"`
|
||||
OutputSatoshis int64 `json:"outputSatoshis,omitempty"`
|
||||
FeeSatoshis int64 `json:"feeSatoshis,omitempty"`
|
||||
}
|
||||
|
||||
type addressHistoryItem struct {
|
||||
Addresses map[string]addressHistoryIndexes `json:"addresses"`
|
||||
Satoshis int64 `json:"satoshis"`
|
||||
Confirmations int `json:"confirmations"`
|
||||
Tx resTx `json:"tx"`
|
||||
Addresses map[string]*addressHistoryIndexes `json:"addresses"`
|
||||
Satoshis int64 `json:"satoshis"`
|
||||
Confirmations int `json:"confirmations"`
|
||||
Tx resTx `json:"tx"`
|
||||
}
|
||||
|
||||
type resultGetAddressHistory struct {
|
||||
|
@ -289,20 +297,55 @@ func stringInSlice(a string, list []string) bool {
|
|||
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{
|
||||
// BlockHash: tx.BlockHash,
|
||||
BlockTimestamp: tx.Blocktime,
|
||||
// FeeSatoshis,
|
||||
Hash: tx.Txid,
|
||||
Height: height,
|
||||
Hex: tx.Hex,
|
||||
Inputs: hi,
|
||||
// InputSatoshis,
|
||||
Locktime: int(tx.LockTime),
|
||||
Outputs: ho,
|
||||
// OutputSatoshis,
|
||||
// Version: int(tx.Version),
|
||||
FeeSatoshis: tx.FeesSat.Int64(),
|
||||
Hash: tx.Txid,
|
||||
Height: h,
|
||||
Hex: tx.Hex,
|
||||
Inputs: inputs,
|
||||
InputSatoshis: tx.ValueInSat.Int64(),
|
||||
Locktime: int(tx.Locktime),
|
||||
Outputs: outputs,
|
||||
OutputSatoshis: tx.ValueOutSat.Int64(),
|
||||
Version: int(tx.Version),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -341,54 +384,53 @@ func (s *SocketIoServer) getAddressHistory(addr []string, opts *addrOpts) (res r
|
|||
txids := txr.Result
|
||||
res.Result.TotalCount = len(txids)
|
||||
res.Result.Items = make([]addressHistoryItem, 0)
|
||||
for i, txid := range txids {
|
||||
if i >= opts.From && i < opts.To {
|
||||
tx, height, err := s.txCache.GetTransaction(txid, bestheight)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
ads := make(map[string]addressHistoryIndexes)
|
||||
hi := make([]txInputs, 0)
|
||||
ho := make([]txOutputs, 0)
|
||||
for _, vout := range tx.Vout {
|
||||
aoh := vout.ScriptPubKey.Hex
|
||||
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)
|
||||
to := len(txids)
|
||||
if to > opts.To {
|
||||
to = opts.To
|
||||
}
|
||||
for txi := opts.From; txi < to; txi++ {
|
||||
tx, err := s.api.GetTransaction(txids[txi], bestheight, false)
|
||||
// for i, txid := range txids {
|
||||
// if i >= opts.From && i < opts.To {
|
||||
// tx, err := s.api.GetTransaction(txid, bestheight, false)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
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
|
||||
}
|
||||
|
@ -594,78 +636,11 @@ func (s *SocketIoServer) getDetailedTransaction(txid string) (res resultGetDetai
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
tx, height, err := s.txCache.GetTransaction(txid, bestheight)
|
||||
tx, err := s.api.GetTransaction(txid, bestheight, false)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
hi := make([]txInputs, 0)
|
||||
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)
|
||||
res.Result = txToResTx(tx)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// +build integration
|
||||
// build integration
|
||||
|
||||
package server
|
||||
|
||||
|
@ -8,6 +8,7 @@ import (
|
|||
"encoding/json"
|
||||
"flag"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -94,16 +95,74 @@ func getFullAddressHistory(addr []string, rr addrOpts, ws *gosocketio.Client) (*
|
|||
return &bbResponse, nil
|
||||
}
|
||||
|
||||
func equalAddressHistoryItem(logItem addressHistoryItem, bbItem addressHistoryItem) error {
|
||||
if logItem.Tx.Hash != bbItem.Tx.Hash {
|
||||
return errors.Errorf("Different hash bb: %v log: %v", bbItem.Tx.Hash, logItem.Tx.Hash)
|
||||
func equalTx(logTx resTx, bbTx resTx) error {
|
||||
if logTx.Hash != bbTx.Hash {
|
||||
return errors.Errorf("Different Hash bb: %v log: %v", bbTx.Hash, logTx.Hash)
|
||||
}
|
||||
if logItem.Tx.Hex != bbItem.Tx.Hex {
|
||||
return errors.Errorf("Different hex bb: %v log: %v", bbItem.Tx.Hex, logItem.Tx.Hex)
|
||||
if logTx.Hex != bbTx.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
|
||||
}
|
||||
|
||||
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) {
|
||||
bbResponse := resultGetAddressHistory{}
|
||||
logResponse := resultGetAddressHistory{}
|
||||
|
@ -146,16 +205,17 @@ func verifyGetAddressHistory(t *testing.T, id int, lrs *logRequestResponse, bbRe
|
|||
}
|
||||
found := false
|
||||
for _, bbFullItem := range bbFullResponse.Result.Items {
|
||||
if err1 = equalAddressHistoryItem(logItem, bbFullItem); err1 == nil {
|
||||
err1 = equalAddressHistoryItem(logItem, bbFullItem)
|
||||
if err1 == nil {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
if err1.Error()[:14] != "Different Hash" {
|
||||
t.Log(err1)
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -237,94 +297,7 @@ func verifyGetDetailedTransaction(t *testing.T, id int, lrs *logRequestResponse,
|
|||
if err := unmarshalResponses(t, id, lrs, bbResStr, &bbResponse, &logResponse); err != nil {
|
||||
return
|
||||
}
|
||||
equalInputs := func() error {
|
||||
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 {
|
||||
if err := equalTx(logResponse.Result, bbResponse.Result); err != nil {
|
||||
t.Log("getDetailedTransaction", id, err)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -55,7 +55,9 @@
|
|||
var addresses = document.getElementById('getAddressHistoryAddresses').value.split(",");
|
||||
addresses = addresses.map(s => s.trim());
|
||||
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(result);
|
||||
document.getElementById('getAddressHistoryResult').innerText = JSON.stringify(result).replace(/,/g, ", ");
|
||||
|
@ -283,7 +285,11 @@
|
|||
<input class="btn btn-secondary" type="button" value="getAddressHistory" onclick="getAddressHistory()">
|
||||
</div>
|
||||
<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 class="col form-inline">
|
||||
<input type="checkbox" id="getAddressHistoryMempool">
|
||||
|
|
Loading…
Reference in New Issue