Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions cmd/commands/assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,19 @@ func parseFeeRate(ctx *cli.Context) (uint32, error) {
return uint32(0), nil
}

func parseSatPerVByte(ctx *cli.Context) (uint32, error) {
if !ctx.IsSet(feeRateName) {
return 0, nil
}

satPerVByte := ctx.Uint64(feeRateName)
if satPerVByte > math.MaxUint32 {
return 0, fmt.Errorf("fee rate exceeds 2^32")
}

return uint32(satPerVByte), nil
}

func mintAsset(ctx *cli.Context) error {
switch {
case ctx.String(assetTagName) == "":
Expand Down Expand Up @@ -1106,7 +1119,7 @@ func sendAssets(ctx *cli.Context) error {
client, cleanUp := getClient(ctx)
defer cleanUp()

feeRate, err := parseFeeRate(ctx)
satPerVByte, err := parseSatPerVByte(ctx)
if err != nil {
return err
}
Expand Down Expand Up @@ -1177,7 +1190,7 @@ func sendAssets(ctx *cli.Context) error {
}

resp, err := client.SendAsset(ctxc, &taprpc.SendAssetRequest{
FeeRate: feeRate,
SatPerVbyte: uint64(satPerVByte),
SkipProofCourierPingCheck: ctx.Bool(
skipProofCourierPingCheckName,
),
Expand Down
7 changes: 7 additions & 0 deletions docs/release-notes/release-notes-0.8.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,13 @@
`BurnAssetResponse` adds a repeated `burn_proofs` field; the singular
`burn_proof` field is deprecated.

- [PR#2054](https://github.com/lightninglabs/taproot-assets/pull/2054)
Comment thread
sergey3bv marked this conversation as resolved.
([#1661](https://github.com/lightninglabs/taproot-assets/issues/1661))
Replaces the `fee_rate` send option with `sat_per_vbyte` in tapcli and adds
`sat_per_vbyte` to `SendAssetRequest`. The legacy `fee_rate` RPC field is
now deprecated and remains accepted as a sat/kw fallback when
`sat_per_vbyte` is not set.

## Performance Improvements

* [PR#2104](https://github.com/lightninglabs/taproot-assets/pull/2104)
Expand Down
6 changes: 3 additions & 3 deletions itest/addrs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1207,10 +1207,10 @@ func withSkipProofCourierPingCheck() sendOption {
}
}

// withFeeRate is an option to specify the fee rate for the send.
func withFeeRate(feeRate uint32) sendOption {
// withSatPerVByte is an option to specify the fee rate for the send.
func withSatPerVByte(satPerVByte uint64) sendOption {
return func(options *sendOptions) {
options.sendAssetRequest.FeeRate = feeRate
options.sendAssetRequest.SatPerVbyte = satPerVByte
}
}

Expand Down
3 changes: 2 additions & 1 deletion itest/custom_channels/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -920,7 +920,8 @@ func assertPendingChannelAssetData(t *testing.T, node *itest.IntegratedNode,
)
if err != nil {
return fmt.Errorf("error unmarshalling custom channel "+
"data: %v", err)
"data: %v", err,
)
}

if len(closeData.FundingAssets) == 0 {
Expand Down
4 changes: 2 additions & 2 deletions itest/fee_estimation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,8 @@ func testFeeEstimation(t *harnessTest) {
// fee rate fails the sanity check against the fee estimator's fee floor
// of 253 sat/kw, or 1.012 sat/vB.
_, err = t.tapd.SendAsset(ctx, &taprpc.SendAssetRequest{
TapAddrs: []string{addr3.Encoded},
FeeRate: uint32(chainfee.FeePerKwFloor) - 1,
TapAddrs: []string{addr3.Encoded},
SatPerVbyte: 1,
})
require.ErrorContains(t.t, err, "manual fee rate below floor")

Expand Down
8 changes: 5 additions & 3 deletions itest/send_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,11 @@ func testMinRelayFeeBump(t *harnessTest) {
// Set the variables for the fee rates we'll use in this test.
belowFloorFeeRate := chainfee.SatPerVByte(1).FeePerKWeight()
belowMinRelayFeeRate := chainfee.SatPerKVByte(1500).FeePerKWeight()
realWorldMinRelayFeeRate := chainfee.SatPerKVByte(1952)
realWorldMinRelayFeeRate := chainfee.SatPerKVByte(3000)
harnessMinRelayFeeRate := chainfee.SatPerKVByte(1000)
defaultFeeRate := chainfee.SatPerKWeight(3125)
sendBelowFloorFeeRate := uint32(1)
sendBelowMinRelayFeeRate := uint32(2)

t.lndHarness.SetFeeEstimateWithConf(belowFloorFeeRate, 6)
t.lndHarness.SetMinRelayFeerate(realWorldMinRelayFeeRate)
Expand Down Expand Up @@ -230,13 +232,13 @@ func testMinRelayFeeBump(t *harnessTest) {

sendAsset(
t, t.tapd, withReceiverAddresses(bobAddr),
withFeeRate(uint32(belowFloorFeeRate)),
withSatPerVByte(uint64(sendBelowFloorFeeRate)),
withError("manual fee rate below floor"),
)

sendAsset(
t, t.tapd, withReceiverAddresses(bobAddr),
withFeeRate(uint32(belowMinRelayFeeRate)),
withSatPerVByte(uint64(sendBelowMinRelayFeeRate)),
withError("feerate does not meet minrelayfee"),
)

Expand Down
28 changes: 27 additions & 1 deletion rpcserver/rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ const (
// encoded as hop hints in a bolt11 invoice.
maxRfqHopHints = 20

// maxSendSatPerVByte is the highest sat/vB value that can be converted
// to sat/kw without overflowing the internal signed fee representation.
maxSendSatPerVByte = uint64(math.MaxInt64 / 1000)

// multiRfqNegotiationTimeout is the timeout within which we expect our
// peer and our node to reach an agreement over a quote. This timeout
// is a bit shorter in the context of multi-RFQ in order to not block
Expand Down Expand Up @@ -832,6 +836,24 @@ func checkFeeRateSanity(ctx context.Context, rpcFeeRate chainfee.SatPerKWeight,
}
}

// parseSendFeeRate parses the legacy sat/kw fee field or the preferred
// sat_per_vbyte field from a SendAssetRequest.
func parseSendFeeRate(req *taprpc.SendAssetRequest) (chainfee.SatPerKWeight,
error) {

if req.SatPerVbyte == 0 {
return chainfee.SatPerKWeight(req.FeeRate), nil
}

if req.SatPerVbyte > maxSendSatPerVByte {
return 0, fmt.Errorf("manual fee rate exceeds maximum: "+
"(sat_per_vbyte=%d, max=%d)", req.SatPerVbyte,
maxSendSatPerVByte)
}

return chainfee.SatPerVByte(req.SatPerVbyte).FeePerKWeight(), nil
}

// FundBatch attempts to fund the current pending batch.
func (r *RPCServer) FundBatch(ctx context.Context,
req *mintrpc.FundBatchRequest) (*mintrpc.FundBatchResponse, error) {
Expand Down Expand Up @@ -3798,8 +3820,12 @@ func (r *RPCServer) SendAsset(ctx context.Context,
return nil, fmt.Errorf("error parsing addresses: %w", err)
}

manualFeeRate, err := parseSendFeeRate(req)
if err != nil {
return nil, err
}
feeRate, err := checkFeeRateSanity(
ctx, chainfee.SatPerKWeight(req.FeeRate), r.cfg.Lnd.WalletKit,
ctx, manualFeeRate, r.cfg.Lnd.WalletKit,
)
if err != nil {
return nil, err
Expand Down
65 changes: 65 additions & 0 deletions rpcserver/send_fee_rate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package rpcserver

import (
"math"
"testing"

"github.com/lightninglabs/taproot-assets/taprpc"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/stretchr/testify/require"
)

func TestParseSendFeeRate(t *testing.T) {
t.Parallel()

testCases := []struct {
name string
req *taprpc.SendAssetRequest
expectedFee chainfee.SatPerKWeight
expectedErrStr string
}{
{
name: "legacy fee rate",
req: &taprpc.SendAssetRequest{
FeeRate: 1234,
},
expectedFee: 1234,
},
{
name: "sat per vbyte preferred",
req: &taprpc.SendAssetRequest{
FeeRate: 1234,
SatPerVbyte: 5,
},
expectedFee: chainfee.SatPerVByte(5).FeePerKWeight(),
},
{
name: "sat per vbyte too large",
req: &taprpc.SendAssetRequest{
SatPerVbyte: uint64(math.MaxInt64/1000) + 1,
},
expectedErrStr: "manual fee rate exceeds maximum",
},
}

for _, testCase := range testCases {
testCase := testCase

t.Run(testCase.name, func(t *testing.T) {
t.Parallel()

feeRate, err := parseSendFeeRate(testCase.req)
if testCase.expectedErrStr != "" {
require.ErrorContains(
t,
err,
testCase.expectedErrStr,
)
return
}

require.NoError(t, err)
require.Equal(t, testCase.expectedFee, feeRate)
})
}
}
28 changes: 21 additions & 7 deletions taprpc/taprootassets.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions taprpc/taprootassets.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1655,8 +1655,9 @@ message SendAssetRequest {
// must be empty, and vice versa.
repeated string tap_addrs = 1;

// The optional fee rate to use for the minting transaction, in sat/kw.
uint32 fee_rate = 2;
Comment thread
sergey3bv marked this conversation as resolved.
// Deprecated: Use sat_per_vbyte instead.
// The optional fee rate to use for the anchor transaction, in sat/kw.
uint32 fee_rate = 2 [deprecated = true];

// An optional short label for the send transfer. This label can be used to
// track the progress of the transfer via the logs or an event subscription.
Expand All @@ -1675,6 +1676,9 @@ message SendAssetRequest {
// meaning that if addresses_with_amounts is set, then tap_addrs must be
// empty, and vice versa.
repeated AddressWithAmount addresses_with_amounts = 5;

// The optional fee rate to use for the anchor transaction, in sat/vB.
uint64 sat_per_vbyte = 6;
}

message AddressWithAmount {
Expand Down
7 changes: 6 additions & 1 deletion taprpc/taprootassets.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -2978,7 +2978,7 @@
"fee_rate": {
"type": "integer",
"format": "int64",
"description": "The optional fee rate to use for the minting transaction, in sat/kw."
"description": "Deprecated: Use sat_per_vbyte instead.\nThe optional fee rate to use for the anchor transaction, in sat/kw."
},
"label": {
"type": "string",
Expand All @@ -2995,6 +2995,11 @@
"$ref": "#/definitions/taprpcAddressWithAmount"
},
"description": "A list of addresses and the amounts of asset units to send to them. This\nmust be used for V2 TAP addresses that don't specify an amount in the\naddress itself and allow the sender to choose the amount to send. The\ntap_addrs and addresses_with_amounts lists are mutually exclusive,\nmeaning that if addresses_with_amounts is set, then tap_addrs must be\nempty, and vice versa."
},
"sat_per_vbyte": {
"type": "string",
"format": "uint64",
"description": "The optional fee rate to use for the anchor transaction, in sat/vB."
}
}
},
Expand Down
Loading