diff --git a/server/public.go b/server/public.go index 8decf464..34428785 100644 --- a/server/public.go +++ b/server/public.go @@ -9,6 +9,7 @@ import ( "encoding/json" "fmt" "html/template" + "math/big" "net/http" "reflect" "runtime" @@ -128,6 +129,8 @@ func (s *PublicServer) ConnectFullPublicInterface() { serveMux.HandleFunc(path+"api/tx-specific/", s.jsonHandler(s.apiTxSpecific)) serveMux.HandleFunc(path+"api/address/", s.jsonHandler(s.apiAddress)) serveMux.HandleFunc(path+"api/block/", s.jsonHandler(s.apiBlock)) + serveMux.HandleFunc(path+"api/sendtx/", s.jsonHandler(s.apiSendTx)) + serveMux.HandleFunc(path+"api/estimatefee/", s.jsonHandler(s.apiEstimateFee)) // socket.io interface serveMux.Handle(path+"socket.io/", s.socketio.GetHandler()) } @@ -217,7 +220,11 @@ func (s *PublicServer) jsonHandler(handler func(r *http.Request) (interface{}, e glog.Error(getFunctionName(handler), " error: ", err) } if s.debug { - data = jsonError{fmt.Sprintf("Internal server error: %v, data %+v", err, data)} + if data != nil { + data = jsonError{fmt.Sprintf("Internal server error: %v, data %+v", err, data)} + } else { + data = jsonError{fmt.Sprintf("Internal server error: %v", err)} + } } else { data = jsonError{"Internal server error"} } @@ -537,7 +544,7 @@ func (s *PublicServer) explorerSendTx(w http.ResponseWriter, r *http.Request) (t data.Error = &api.ApiError{Text: err.Error(), Public: true} return sendTransactionTpl, data, nil } - data.Status = "Transaction sent " + res + data.Status = "Transaction sent, result " + res } } return sendTransactionTpl, data, nil @@ -677,3 +684,60 @@ func (s *PublicServer) apiBlock(r *http.Request) (interface{}, error) { } return block, err } + +type resultSendTransaction struct { + Result string `json:"result"` +} + +func (s *PublicServer) apiSendTx(r *http.Request) (interface{}, error) { + var err error + var res resultSendTransaction + s.metrics.ExplorerViews.With(common.Labels{"action": "api-sendtx"}).Inc() + if i := strings.LastIndexByte(r.URL.Path, '/'); i > 0 { + hex := r.URL.Path[i+1:] + if len(hex) > 0 { + if len(hex) > 0 { + res.Result, err = s.chain.SendRawTransaction(hex) + return res, err + } + } + } + return nil, api.NewApiError("Missing tx blob", true) +} + +type resultEstimateFeeAsString struct { + Result string `json:"result"` +} + +func (s *PublicServer) apiEstimateFee(r *http.Request) (interface{}, error) { + var res resultEstimateFeeAsString + s.metrics.ExplorerViews.With(common.Labels{"action": "api-estimatefee"}).Inc() + if i := strings.LastIndexByte(r.URL.Path, '/'); i > 0 { + b := r.URL.Path[i+1:] + if len(b) > 0 { + blocks, err := strconv.Atoi(b) + if err != nil { + return nil, api.NewApiError("Parameter 'number of blocks' is not a number", true) + } + conservative := true + c := r.URL.Query().Get("conservative") + if len(c) > 0 { + conservative, err = strconv.ParseBool(c) + if err != nil { + return nil, api.NewApiError("Parameter 'conservative' cannot be converted to boolean", true) + } + } + var fee big.Int + fee, err = s.chain.EstimateSmartFee(blocks, conservative) + if err != nil { + fee, err = s.chain.EstimateFee(blocks) + if err != nil { + return nil, err + } + } + res.Result = s.chainParser.AmountToDecimalString(&fee) + return res, nil + } + } + return nil, api.NewApiError("Missing parameter 'number of blocks'", true) +} diff --git a/server/public_test.go b/server/public_test.go index 7f47cc9b..53cef7a4 100644 --- a/server/public_test.go +++ b/server/public_test.go @@ -338,7 +338,7 @@ func httpTests(t *testing.T, ts *httptest.Server) { `Fake Coin Explorer`, `

Send Raw Transaction

`, ``, - `
Not implemented
`, + `
Invalid data
`, ``, }, }, @@ -400,12 +400,21 @@ func httpTests(t *testing.T, ts *httptest.Server) { }, }, { - name: "apiBlock", - r: newGetRequest(ts.URL + "/api/block/225493"), + name: "apiSendTx", + r: newGetRequest(ts.URL + "/api/sendtx/123456"), status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"page":1,"totalPages":1,"itemsOnPage":1000,"hash":"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997","previousblockhash":"","nextblockhash":"","height":225493,"confirmations":2,"size":1234567,"time":1534858021,"version":0,"merkleroot":"","nonce":0,"bits":"","difficulty":0,"TxCount":2,"txs":[{"txid":"00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840","vin":[],"vout":[{"value":"1","n":0,"scriptPubKey":{"hex":"","addresses":["mfcWp7DB6NuaZsExybTTXpVgWz559Np4Ti"]}},{"value":"0.00012345","n":1,"scriptPubKey":{"hex":"","addresses":["mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"]}}],"blockhash":"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997","blockheight":225493,"confirmations":2,"time":1534858021,"blocktime":1534858021,"valueOut":"1.00012345","valueIn":"0","fees":"0","hex":""},{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vin":[],"vout":[{"value":"12345.67890123","n":0,"scriptPubKey":{"hex":"","addresses":["mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw"]}},{"value":"0.00000001","n":1,"scriptPubKey":{"hex":"","addresses":["2Mz1CYoppGGsLNUGF2YDhTif6J661JitALS"]}},{"value":"0.00009876","n":2,"scriptPubKey":{"hex":"","addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"]}}],"blockhash":"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997","blockheight":225493,"confirmations":2,"time":1534858021,"blocktime":1534858021,"valueOut":"12345.679","valueIn":"0","fees":"0","hex":""}]}`, + `{"result":"9876"}`, + }, + }, + { + name: "apiEstimateFee", + r: newGetRequest(ts.URL + "/api/estimatefee/123?conservative=false"), + status: http.StatusOK, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"result":"0.00012299"}`, }, }, } @@ -522,7 +531,7 @@ func socketioTests(t *testing.T, ts *httptest.Server) { { name: "sendTransaction", req: socketioReq{"sendTransaction", []interface{}{"010000000001019d64f0c72a0d206001decbffaa722eb1044534c"}}, - want: `{"error":{"message":"Not implemented"}}`, + want: `{"error":{"message":"Invalid data"}}`, }, } diff --git a/server/socketio.go b/server/socketio.go index 9e531c54..2cdd1361 100644 --- a/server/socketio.go +++ b/server/socketio.go @@ -635,10 +635,6 @@ func (s *SocketIoServer) getDetailedTransaction(txid string) (res resultGetDetai return } -type resultSendTransaction struct { - Result string `json:"result"` -} - func (s *SocketIoServer) sendTransaction(tx string) (res resultSendTransaction, err error) { txid, err := s.chain.SendRawTransaction(tx) if err != nil { diff --git a/tests/dbtestdata/fakechain.go b/tests/dbtestdata/fakechain.go index 809ccd5d..d29740b9 100644 --- a/tests/dbtestdata/fakechain.go +++ b/tests/dbtestdata/fakechain.go @@ -158,7 +158,11 @@ func (c *fakeBlockChain) GetTransactionForMempool(txid string) (v *bchain.Tx, er } func (c *fakeBlockChain) EstimateSmartFee(blocks int, conservative bool) (v big.Int, err error) { - v.SetInt64(int64(blocks) * 100) + if conservative == false { + v.SetInt64(int64(blocks)*100 - 1) + } else { + v.SetInt64(int64(blocks) * 100) + } return } @@ -168,7 +172,10 @@ func (c *fakeBlockChain) EstimateFee(blocks int) (v big.Int, err error) { } func (c *fakeBlockChain) SendRawTransaction(tx string) (v string, err error) { - return "", errors.New("Not implemented") + if tx == "123456" { + return "9876", nil + } + return "", errors.New("Invalid data") } func (c *fakeBlockChain) ResyncMempool(onNewTxAddr bchain.OnNewTxAddrFunc) (count int, err error) {