From 604b41f10a71db4e3d0dda896e9c70a9aad1e28d Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 26 Jun 2018 13:02:53 +0200 Subject: [PATCH] Add explorer api worker --- api/types.go | 49 ++++++++++++++++++++++++ api/worker.go | 95 ++++++++++++++++++++++++++++++++++++++++++++++ bchain/types.go | 6 +-- server/public.go | 29 ++++++++++++++ server/socketio.go | 2 +- 5 files changed, 177 insertions(+), 4 deletions(-) create mode 100644 api/types.go create mode 100644 api/worker.go diff --git a/api/types.go b/api/types.go new file mode 100644 index 00000000..b00915ad --- /dev/null +++ b/api/types.go @@ -0,0 +1,49 @@ +package api + +type ScriptSig struct { + Hex string `json:"hex"` + Asm string `json:"asm,omitempty"` +} + +type Vin struct { + Txid string `json:"txid"` + Vout uint32 `json:"vout"` + Sequence int64 `json:"sequence,omitempty"` + N int `json:"n"` + ScriptSig ScriptSig `json:"scriptSig"` + Addr string `json:"addr"` + ValueSat int64 `json:"valueSat"` + Value float64 `json:"value"` +} + +type ScriptPubKey struct { + Hex string `json:"hex"` + Asm string `json:"asm,omitempty"` + Addresses []string `json:"addresses"` + Type string `json:"type,omitempty"` +} +type Vout struct { + Value float64 `json:"value"` + N int `json:"n"` + ScriptPubKey ScriptPubKey `json:"scriptPubKey"` + SpentTxID string `json:"spentTxId,omitempty"` + SpentIndex int `json:"spentIndex,omitempty"` + SpentHeight int `json:"spentHeight,omitempty"` +} + +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 float64 `json:"valueOut"` + Size int `json:"size,omitempty"` + ValueIn float64 `json:"valueIn"` + Fees float64 `json:"fees"` +} diff --git a/api/worker.go b/api/worker.go new file mode 100644 index 00000000..9a8c91a5 --- /dev/null +++ b/api/worker.go @@ -0,0 +1,95 @@ +package api + +import ( + "blockbook/bchain" + "blockbook/db" +) + +// Worker is handle to api worker +type Worker struct { + db *db.RocksDB + txCache *db.TxCache + chain bchain.BlockChain + chainParser bchain.BlockChainParser +} + +// NewWorker creates new api worker +func NewWorker(db *db.RocksDB, chain bchain.BlockChain, txCache *db.TxCache) (*Worker, error) { + w := &Worker{ + db: db, + txCache: txCache, + chain: chain, + chainParser: chain.GetChainParser(), + } + return w, nil +} + +func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTx bool) (*Tx, error) { + bchainTx, height, err := w.txCache.GetTransaction(txid, bestheight) + if err != nil { + return nil, err + } + var blockhash string + if bchainTx.Confirmations > 0 { + blockhash, err = w.db.GetBlockHash(height) + if err != nil { + return nil, err + } + } + var valIn, valOut, fees float64 + vins := make([]Vin, len(bchainTx.Vin)) + for i := range bchainTx.Vin { + bchainVin := &bchainTx.Vin[i] + vin := &vins[i] + vin.Txid = bchainVin.Txid + vin.N = i + vin.Vout = bchainVin.Vout + vin.ScriptSig.Hex = bchainVin.ScriptSig.Hex + otx, _, err := w.txCache.GetTransaction(bchainVin.Txid, bestheight) + if err != nil { + return nil, err + } + if len(otx.Vout) > int(vin.Vout) { + vout := &otx.Vout[vin.Vout] + vin.Value = vout.Value + valIn += vout.Value + vin.ValueSat = int64(vout.Value*1E8 + 0.5) + if vout.Address != nil { + a := vout.Address.String() + vin.Addr = a + } + } + } + vouts := make([]Vout, len(bchainTx.Vout)) + for i := range bchainTx.Vout { + bchainVout := &bchainTx.Vout[i] + vout := &vouts[i] + vout.N = i + vout.Value = bchainVout.Value + valOut += bchainVout.Value + vout.ScriptPubKey.Hex = bchainVout.ScriptPubKey.Hex + vout.ScriptPubKey.Addresses = bchainVout.ScriptPubKey.Addresses + if spendingTx { + // TODO + } + } + // for now do not return size, we would have to compute vsize of segwit transactions + // size:=len(bchainTx.Hex) / 2 + fees = valIn - valOut + r := &Tx{ + Blockhash: blockhash, + Blockheight: int(height), + Blocktime: bchainTx.Blocktime, + Confirmations: bchainTx.Confirmations, + Fees: fees, + Locktime: bchainTx.LockTime, + Time: bchainTx.Time, + Txid: txid, + ValueIn: valIn, + ValueOut: valOut, + Version: bchainTx.Version, + Vin: vins, + Vout: vouts, + } + return r, nil +} diff --git a/bchain/types.go b/bchain/types.go index 582e50b3..09052918 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -58,9 +58,9 @@ type Vout struct { // Tx is blockchain transaction // unnecessary fields are commented out to avoid overhead type Tx struct { - Hex string `json:"hex"` - Txid string `json:"txid"` - // Version int32 `json:"version"` + Hex string `json:"hex"` + Txid string `json:"txid"` + Version int32 `json:"version"` LockTime uint32 `json:"locktime"` Vin []Vin `json:"vin"` Vout []Vout `json:"vout"` diff --git a/server/public.go b/server/public.go index f52da14a..368f99e7 100644 --- a/server/public.go +++ b/server/public.go @@ -1,6 +1,7 @@ package server import ( + "blockbook/api" "blockbook/bchain" "blockbook/common" "blockbook/db" @@ -27,6 +28,7 @@ type PublicServer struct { txCache *db.TxCache chain bchain.BlockChain chainParser bchain.BlockChainParser + api *api.Worker explorerURL string metrics *common.Metrics is *common.InternalState @@ -35,6 +37,11 @@ type PublicServer struct { // NewPublicServerS creates new public server http interface to blockbook and returns its handle func NewPublicServer(binding string, certFiles string, db *db.RocksDB, chain bchain.BlockChain, txCache *db.TxCache, explorerURL string, metrics *common.Metrics, is *common.InternalState) (*PublicServer, error) { + api, err := api.NewWorker(db, chain, txCache) + if err != nil { + return nil, err + } + socketio, err := NewSocketIoServer(db, chain, txCache, metrics, is) if err != nil { return nil, err @@ -51,6 +58,7 @@ func NewPublicServer(binding string, certFiles string, db *db.RocksDB, chain bch binding: binding, certFiles: certFiles, https: https, + api: api, socketio: socketio, db: db, txCache: txCache, @@ -66,6 +74,8 @@ func NewPublicServer(binding string, certFiles string, db *db.RocksDB, chain bch // redirect to Bitcore for details of transaction serveMux.HandleFunc(path+"tx/", s.txRedirect) serveMux.HandleFunc(path+"address/", s.addressRedirect) + // explorer + serveMux.HandleFunc(path+"explorer/tx/", s.explorerTx) // API call used to detect state of Blockbook serveMux.HandleFunc(path+"api/block-index/", s.apiBlockIndex) // handle socket.io @@ -139,6 +149,25 @@ func (s *PublicServer) addressRedirect(w http.ResponseWriter, r *http.Request) { } } +func (s *PublicServer) explorerTx(w http.ResponseWriter, r *http.Request) { + var tx *api.Tx + var err error + if i := strings.LastIndexByte(r.URL.Path, '/'); i > 0 { + txid := r.URL.Path[i+1:] + bestheight, _, err := s.db.GetBestBlock() + if err == nil { + tx, err = s.api.GetTransaction(txid, bestheight, true) + } + } + if err == nil { + buf, err := json.MarshalIndent(tx, "", " ") + if err != nil { + glog.Error(err) + } + w.Write(buf) + } +} + type resAboutBlockbookPublic struct { Coin string `json:"coin"` Host string `json:"host"` diff --git a/server/socketio.go b/server/socketio.go index 14293f48..e996d513 100644 --- a/server/socketio.go +++ b/server/socketio.go @@ -598,7 +598,7 @@ func (s *SocketIoServer) getDetailedTransaction(txid string) (res resultGetDetai return res, err } if len(otx.Vout) > int(vin.Vout) { - vout := otx.Vout[vin.Vout] + vout := &otx.Vout[vin.Vout] if vout.Address != nil { a := vout.Address.String() ai.Address = &a