Implement apiSendTx and apiEstimateFee

pull/85/head
Martin Boehm 2018-10-25 18:26:51 +02:00
parent db35a51dde
commit d1176e17d1
4 changed files with 89 additions and 13 deletions

View File

@ -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)
}

View File

@ -338,7 +338,7 @@ func httpTests(t *testing.T, ts *httptest.Server) {
`<a href="/" class="nav-link">Fake Coin Explorer</a>`,
`<h1>Send Raw Transaction</h1>`,
`<textarea class="form-control" rows="8" name="hex">12341234</textarea>`,
`<div class="alert alert-danger">Not implemented</div></div>`,
`<div class="alert alert-danger">Invalid data</div>`,
`</html>`,
},
},
@ -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"}}`,
},
}

View File

@ -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 {

View File

@ -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) {