Implement REST interface to mempool transactions

This commit is contained in:
Martin Boehm 2018-02-01 12:56:45 +01:00
parent 1f36acc084
commit 2605f2fc1d
2 changed files with 84 additions and 32 deletions

View File

@ -121,9 +121,9 @@ func main() {
} }
} }
var httpServer *server.HttpServer var httpServer *server.HTTPServer
if *httpServerBinding != "" { if *httpServerBinding != "" {
httpServer, err = server.New(*httpServerBinding, index) httpServer, err = server.NewHTTPServer(*httpServerBinding, index, mempool)
if err != nil { if err != nil {
glog.Fatal("https: ", err) glog.Fatal("https: ", err)
} }
@ -248,7 +248,7 @@ func mqHandler(m *bchain.MQMessage) {
} }
} }
func waitForSignalAndShutdown(s *server.HttpServer, mq *bchain.MQ, timeout time.Duration) { func waitForSignalAndShutdown(s *server.HTTPServer, mq *bchain.MQ, timeout time.Duration) {
stop := make(chan os.Signal, 1) stop := make(chan os.Signal, 1)
signal.Notify(stop, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM) signal.Notify(stop, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM)

View File

@ -16,26 +16,31 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )
type HttpServer struct { // HTTPServer is handle to HttpServer
https *http.Server type HTTPServer struct {
db *db.RocksDB https *http.Server
db *db.RocksDB
mempool *bchain.Mempool
} }
func New(httpServerBinding string, db *db.RocksDB) (*HttpServer, error) { // NewHTTPServer creates new REST interface to blockbook and returns its handle
func NewHTTPServer(httpServerBinding string, db *db.RocksDB, mempool *bchain.Mempool) (*HTTPServer, error) {
https := &http.Server{ https := &http.Server{
Addr: httpServerBinding, Addr: httpServerBinding,
} }
s := &HttpServer{ s := &HTTPServer{
https: https, https: https,
db: db, db: db,
mempool: mempool,
} }
r := mux.NewRouter() r := mux.NewRouter()
r.HandleFunc("/", s.info) r.HandleFunc("/", s.info)
r.HandleFunc("/bestBlockHash", s.bestBlockHash) r.HandleFunc("/bestBlockHash", s.bestBlockHash)
r.HandleFunc("/blockHash/{height}", s.blockHash) r.HandleFunc("/blockHash/{height}", s.blockHash)
r.HandleFunc("/transactions/{address}/{lower}/{higher}", s.transactions) r.HandleFunc("/transactions/{address}/{lower}/{higher}", s.transactions)
r.HandleFunc("/confirmedTransactions/{address}/{lower}/{higher}", s.confirmedTransactions)
r.HandleFunc("/unconfirmedTransactions/{address}", s.unconfirmedTransactions)
var h http.Handler = r var h http.Handler = r
h = handlers.LoggingHandler(os.Stderr, h) h = handlers.LoggingHandler(os.Stderr, h)
@ -45,26 +50,26 @@ func New(httpServerBinding string, db *db.RocksDB) (*HttpServer, error) {
} }
// Run starts the server // Run starts the server
func (s *HttpServer) Run() error { func (s *HTTPServer) Run() error {
glog.Infof("http server starting to listen on %s", s.https.Addr) glog.Infof("http server starting to listen on %s", s.https.Addr)
return s.https.ListenAndServe() return s.https.ListenAndServe()
} }
// Close closes the server // Close closes the server
func (s *HttpServer) Close() error { func (s *HTTPServer) Close() error {
glog.Infof("http server closing") glog.Infof("http server closing")
return s.https.Close() return s.https.Close()
} }
// Shutdown shuts down the server // Shutdown shuts down the server
func (s *HttpServer) Shutdown(ctx context.Context) error { func (s *HTTPServer) Shutdown(ctx context.Context) error {
glog.Infof("http server shutdown") glog.Infof("http server shutdown")
return s.https.Shutdown(ctx) return s.https.Shutdown(ctx)
} }
func respondError(w http.ResponseWriter, err error, context string) { func respondError(w http.ResponseWriter, err error, context string) {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
glog.Errorf("http server %s error: %v", context, err) glog.Errorf("http server (context %s) error: %v", context, err)
} }
func respondHashData(w http.ResponseWriter, hash string) { func respondHashData(w http.ResponseWriter, hash string) {
@ -76,7 +81,7 @@ func respondHashData(w http.ResponseWriter, hash string) {
}) })
} }
func (s *HttpServer) info(w http.ResponseWriter, r *http.Request) { func (s *HTTPServer) info(w http.ResponseWriter, r *http.Request) {
type info struct { type info struct {
Version string `json:"version"` Version string `json:"version"`
BestBlockHeight uint32 `json:"bestBlockHeight"` BestBlockHeight uint32 `json:"bestBlockHeight"`
@ -95,7 +100,7 @@ func (s *HttpServer) info(w http.ResponseWriter, r *http.Request) {
}) })
} }
func (s *HttpServer) bestBlockHash(w http.ResponseWriter, r *http.Request) { func (s *HTTPServer) bestBlockHash(w http.ResponseWriter, r *http.Request) {
_, hash, err := s.db.GetBestBlock() _, hash, err := s.db.GetBestBlock()
if err != nil { if err != nil {
respondError(w, err, "bestBlockHash") respondError(w, err, "bestBlockHash")
@ -104,7 +109,7 @@ func (s *HttpServer) bestBlockHash(w http.ResponseWriter, r *http.Request) {
respondHashData(w, hash) respondHashData(w, hash)
} }
func (s *HttpServer) blockHash(w http.ResponseWriter, r *http.Request) { func (s *HTTPServer) blockHash(w http.ResponseWriter, r *http.Request) {
heightString := mux.Vars(r)["height"] heightString := mux.Vars(r)["height"]
var hash string var hash string
height, err := strconv.ParseUint(heightString, 10, 32) height, err := strconv.ParseUint(heightString, 10, 32)
@ -118,31 +123,78 @@ func (s *HttpServer) blockHash(w http.ResponseWriter, r *http.Request) {
} }
} }
func (s *HttpServer) transactions(w http.ResponseWriter, r *http.Request) { func getAddress(r *http.Request) (address string, script []byte, err error) {
type transactionList struct { address = mux.Vars(r)["address"]
Txid []string `json:"txid"` script, err = bchain.AddressToOutputScript(address)
} return
address := mux.Vars(r)["address"] }
higher, err := strconv.ParseUint(mux.Vars(r)["higher"], 10, 32)
func getAddressAndHeightRange(r *http.Request) (address string, script []byte, lower, higher uint32, err error) {
address, script, err = getAddress(r)
if err != nil { if err != nil {
respondError(w, err, fmt.Sprintf("address %s", address)) return
} }
lower, err := strconv.ParseUint(mux.Vars(r)["lower"], 10, 32) higher64, err := strconv.ParseUint(mux.Vars(r)["higher"], 10, 32)
if err != nil { if err != nil {
respondError(w, err, fmt.Sprintf("address %s", address)) return
} }
script, err := bchain.AddressToOutputScript(address) lower64, err := strconv.ParseUint(mux.Vars(r)["lower"], 10, 32)
if err != nil { if err != nil {
respondError(w, err, fmt.Sprintf("address %s", address)) return
}
return address, script, uint32(lower64), uint32(higher64), err
}
type transactionList struct {
Txid []string `json:"txid"`
}
func (s *HTTPServer) unconfirmedTransactions(w http.ResponseWriter, r *http.Request) {
address, script, err := getAddress(r)
if err != nil {
respondError(w, err, fmt.Sprint("unconfirmedTransactions for address", address))
}
txs, err := s.mempool.GetTransactions(script)
if err != nil {
respondError(w, err, fmt.Sprint("unconfirmedTransactions for address", address))
}
txList := transactionList{Txid: txs}
json.NewEncoder(w).Encode(txList)
}
func (s *HTTPServer) confirmedTransactions(w http.ResponseWriter, r *http.Request) {
address, script, lower, higher, err := getAddressAndHeightRange(r)
if err != nil {
respondError(w, err, fmt.Sprint("confirmedTransactions for address", address))
} }
txList := transactionList{} txList := transactionList{}
err = s.db.GetTransactions(script, uint32(lower), uint32(higher), func(txid string) error { err = s.db.GetTransactions(script, lower, higher, func(txid string) error {
txList.Txid = append(txList.Txid, txid) txList.Txid = append(txList.Txid, txid)
return nil return nil
}) })
if err != nil { if err != nil {
respondError(w, err, fmt.Sprintf("address %s", address)) respondError(w, err, fmt.Sprint("confirmedTransactions for address", address))
} }
json.NewEncoder(w).Encode(txList) json.NewEncoder(w).Encode(txList)
}
func (s *HTTPServer) transactions(w http.ResponseWriter, r *http.Request) {
address, script, lower, higher, err := getAddressAndHeightRange(r)
if err != nil {
respondError(w, err, fmt.Sprint("transactions for address", address))
}
txList := transactionList{}
err = s.db.GetTransactions(script, lower, higher, func(txid string) error {
txList.Txid = append(txList.Txid, txid)
return nil
})
if err != nil {
respondError(w, err, fmt.Sprint("transactions for address", address))
}
txs, err := s.mempool.GetTransactions(script)
if err != nil {
respondError(w, err, fmt.Sprint("transactions for address", address))
}
txList.Txid = append(txList.Txid, txs...)
json.NewEncoder(w).Encode(txList)
} }