lnrpc+rpcserver: define and implement EstimateFee RPC

This commit is contained in:
Johan T. Halseth 2019-03-05 14:22:30 +01:00
parent bb092bc61e
commit 1e2af38f5a
No known key found for this signature in database
GPG Key ID: 15BAADA29DA20D26
5 changed files with 886 additions and 567 deletions

File diff suppressed because it is too large Load Diff

View File

@ -111,6 +111,23 @@ func request_Lightning_GetTransactions_0(ctx context.Context, marshaler runtime.
}
var (
filter_Lightning_EstimateFee_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Lightning_EstimateFee_0(ctx context.Context, marshaler runtime.Marshaler, client LightningClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq EstimateFeeRequest
var metadata runtime.ServerMetadata
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_Lightning_EstimateFee_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.EstimateFee(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func request_Lightning_SendCoins_0(ctx context.Context, marshaler runtime.Marshaler, client LightningClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq SendCoinsRequest
var metadata runtime.ServerMetadata
@ -1006,6 +1023,35 @@ func RegisterLightningHandler(ctx context.Context, mux *runtime.ServeMux, conn *
})
mux.Handle("GET", pattern_Lightning_EstimateFee_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Lightning_EstimateFee_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Lightning_EstimateFee_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Lightning_SendCoins_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
@ -1944,6 +1990,8 @@ var (
pattern_Lightning_GetTransactions_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "transactions"}, ""))
pattern_Lightning_EstimateFee_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "transactions", "fee"}, ""))
pattern_Lightning_SendCoins_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "transactions"}, ""))
pattern_Lightning_ListUnspent_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "utxos"}, ""))
@ -2016,6 +2064,8 @@ var (
forward_Lightning_GetTransactions_0 = runtime.ForwardResponseMessage
forward_Lightning_EstimateFee_0 = runtime.ForwardResponseMessage
forward_Lightning_SendCoins_0 = runtime.ForwardResponseMessage
forward_Lightning_ListUnspent_0 = runtime.ForwardResponseMessage

View File

@ -220,6 +220,16 @@ service Lightning {
};
}
/** lncli: `estimatefee`
EstimateFee asks the chain backend to estimate the fee rate and total fees
for a transaction that pays to multiple specified outputs.
*/
rpc EstimateFee (EstimateFeeRequest) returns (EstimateFeeResponse) {
option (google.api.http) = {
get: "/v1/transactions/fee"
};
}
/** lncli: `sendcoins`
SendCoins executes a request to send coins to a particular address. Unlike
SendMany, this RPC call only allows creating a single output at a time. If
@ -839,6 +849,22 @@ message LightningAddress {
string host = 2 [json_name = "host"];
}
message EstimateFeeRequest {
/// The map from addresses to amounts for the transaction.
map<string, int64> AddrToAmount = 1;
/// The target number of blocks that this transaction should be confirmed by.
int32 target_conf = 2;
}
message EstimateFeeResponse {
/// The total fee in satoshis.
int64 fee_sat = 1 [json_name = "fee_sat"];
/// The fee rate in satoshi/byte.
int64 feerate_sat_per_byte = 2 [json_name = "feerate_sat_per_byte"];
}
message SendManyRequest {
/// The map from addresses to amounts
map<string, int64> AddrToAmount = 1;

View File

@ -1044,6 +1044,33 @@
]
}
},
"/v1/transactions/fee": {
"get": {
"summary": "* lncli: `estimatefee`\nEstimateFee asks the chain backend to estimate the fee rate and total fees\nfor a transaction that pays to multiple specified outputs.",
"operationId": "EstimateFee",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/lnrpcEstimateFeeResponse"
}
}
},
"parameters": [
{
"name": "target_conf",
"description": "/ The target number of blocks that this transaction should be confirmed by.",
"in": "query",
"required": false,
"type": "integer",
"format": "int32"
}
],
"tags": [
"Lightning"
]
}
},
"/v1/unlockwallet": {
"post": {
"summary": "* lncli: `unlock`\nUnlockWallet is used at startup of lnd to provide a password to unlock\nthe wallet database.",
@ -1747,6 +1774,21 @@
"lnrpcDisconnectPeerResponse": {
"type": "object"
},
"lnrpcEstimateFeeResponse": {
"type": "object",
"properties": {
"fee_sat": {
"type": "string",
"format": "int64",
"description": "/ The total fee in satoshis."
},
"feerate_sat_per_byte": {
"type": "string",
"format": "int64",
"description": "/ The fee rate in satoshi/byte."
}
}
},
"lnrpcFeeLimit": {
"type": "object",
"properties": {

View File

@ -25,6 +25,7 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/wallet/txauthor"
"github.com/coreos/bbolt"
"github.com/davecgh/go-spew/spew"
proxy "github.com/grpc-ecosystem/grpc-gateway/runtime"
@ -236,6 +237,10 @@ var (
Entity: "onchain",
Action: "read",
}},
"/lnrpc.Lightning/EstimateFee": {{
Entity: "onchain",
Action: "read",
}},
"/lnrpc.Lightning/ChannelBalance": {{
Entity: "offchain",
Action: "read",
@ -800,6 +805,59 @@ func (r *rpcServer) ListUnspent(ctx context.Context,
return resp, nil
}
// EstimateFee handles a request for estimating the fee for sending a
// transaction spending to multiple specified outputs in parallel.
func (r *rpcServer) EstimateFee(ctx context.Context,
in *lnrpc.EstimateFeeRequest) (*lnrpc.EstimateFeeResponse, error) {
// Create the list of outputs we are spending to.
outputs, err := addrPairsToOutputs(in.AddrToAmount)
if err != nil {
return nil, err
}
// Query the fee estimator for the fee rate for the given confirmation
// target.
target := in.TargetConf
feePerKw, err := sweep.DetermineFeePerKw(
r.server.cc.feeEstimator, sweep.FeePreference{
ConfTarget: uint32(target),
},
)
if err != nil {
return nil, err
}
// We will ask the wallet to create a tx using this fee rate. We set
// dryRun=true to avoid inflating the change addresses in the db.
var tx *txauthor.AuthoredTx
wallet := r.server.cc.wallet
err = wallet.WithCoinSelectLock(func() error {
tx, err = wallet.CreateSimpleTx(outputs, feePerKw, true)
return err
})
if err != nil {
return nil, err
}
// Use the created tx to calculate the total fee.
totalOutput := int64(0)
for _, out := range tx.Tx.TxOut {
totalOutput += out.Value
}
totalFee := int64(tx.TotalInput) - totalOutput
resp := &lnrpc.EstimateFeeResponse{
FeeSat: totalFee,
FeerateSatPerByte: int64(feePerKw.FeePerKVByte() / 1000),
}
rpcsLog.Debugf("[estimatefee] fee estimate for conf target %d: %v",
target, resp)
return resp, nil
}
// SendCoins executes a request to send coins to a particular address. Unlike
// SendMany, this RPC call only allows creating a single output at a time.
func (r *rpcServer) SendCoins(ctx context.Context,