mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-06-17 04:11:42 +02:00
multi: add label to WalletController SendOutputs and dependent rpcs
Add a label parameter to the WalletController SendOutputs endpoint and update rpcs that use it to allow optional provision of labels.
This commit is contained in:
parent
75370ce6b4
commit
099161ed0b
@ -3,6 +3,31 @@
|
|||||||
// package to avoid dependency issues.
|
// package to avoid dependency issues.
|
||||||
package labels
|
package labels
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||||
|
)
|
||||||
|
|
||||||
// External labels a transaction as user initiated via the api. This
|
// External labels a transaction as user initiated via the api. This
|
||||||
// label is only used when a custom user provided label is not given.
|
// label is only used when a custom user provided label is not given.
|
||||||
const External = "external"
|
const External = "external"
|
||||||
|
|
||||||
|
// ValidateAPI returns the generic api label if the label provided is empty.
|
||||||
|
// This allows us to label all transactions published by the api, even if
|
||||||
|
// no label is provided. If a label is provided, it is validated against
|
||||||
|
// the known restrictions.
|
||||||
|
func ValidateAPI(label string) (string, error) {
|
||||||
|
if len(label) > wtxmgr.TxLabelLimit {
|
||||||
|
return "", fmt.Errorf("label length: %v exceeds "+
|
||||||
|
"limit of %v", len(label), wtxmgr.TxLabelLimit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no label was provided by the user, add the generic user
|
||||||
|
// send label.
|
||||||
|
if len(label) == 0 {
|
||||||
|
return External, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return label, nil
|
||||||
|
}
|
||||||
|
1363
lnrpc/rpc.pb.go
1363
lnrpc/rpc.pb.go
File diff suppressed because it is too large
Load Diff
@ -1002,6 +1002,9 @@ message SendManyRequest {
|
|||||||
// A manual fee rate set in sat/byte that should be used when crafting the
|
// A manual fee rate set in sat/byte that should be used when crafting the
|
||||||
// transaction.
|
// transaction.
|
||||||
int64 sat_per_byte = 5;
|
int64 sat_per_byte = 5;
|
||||||
|
|
||||||
|
// An optional label for the transaction, limited to 500 characters.
|
||||||
|
string label = 6;
|
||||||
}
|
}
|
||||||
message SendManyResponse {
|
message SendManyResponse {
|
||||||
// The id of the transaction
|
// The id of the transaction
|
||||||
@ -1029,6 +1032,9 @@ message SendCoinsRequest {
|
|||||||
address.
|
address.
|
||||||
*/
|
*/
|
||||||
bool send_all = 6;
|
bool send_all = 6;
|
||||||
|
|
||||||
|
// An optional label for the transaction, limited to 500 characters.
|
||||||
|
string label = 7;
|
||||||
}
|
}
|
||||||
message SendCoinsResponse {
|
message SendCoinsResponse {
|
||||||
// The transaction ID of the transaction
|
// The transaction ID of the transaction
|
||||||
|
@ -4230,6 +4230,10 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"format": "boolean",
|
"format": "boolean",
|
||||||
"description": "If set, then the amount field will be ignored, and lnd will attempt to\nsend all the coins under control of the internal wallet to the specified\naddress."
|
"description": "If set, then the amount field will be ignored, and lnd will attempt to\nsend all the coins under control of the internal wallet to the specified\naddress."
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "An optional label for the transaction, limited to 500 characters."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -311,6 +311,7 @@ func (w *WalletKit) SendOutputs(ctx context.Context,
|
|||||||
// attempt to create this transaction.
|
// attempt to create this transaction.
|
||||||
tx, err := w.cfg.Wallet.SendOutputs(
|
tx, err := w.cfg.Wallet.SendOutputs(
|
||||||
outputsToCreate, chainfee.SatPerKWeight(req.SatPerKw),
|
outputsToCreate, chainfee.SatPerKWeight(req.SatPerKw),
|
||||||
|
labels.External,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -294,7 +294,7 @@ func (b *BtcWallet) IsOurAddress(a btcutil.Address) bool {
|
|||||||
//
|
//
|
||||||
// This is a part of the WalletController interface.
|
// This is a part of the WalletController interface.
|
||||||
func (b *BtcWallet) SendOutputs(outputs []*wire.TxOut,
|
func (b *BtcWallet) SendOutputs(outputs []*wire.TxOut,
|
||||||
feeRate chainfee.SatPerKWeight) (*wire.MsgTx, error) {
|
feeRate chainfee.SatPerKWeight, label string) (*wire.MsgTx, error) {
|
||||||
|
|
||||||
// Convert our fee rate from sat/kw to sat/kb since it's required by
|
// Convert our fee rate from sat/kw to sat/kb since it's required by
|
||||||
// SendOutputs.
|
// SendOutputs.
|
||||||
@ -304,7 +304,10 @@ func (b *BtcWallet) SendOutputs(outputs []*wire.TxOut,
|
|||||||
if len(outputs) < 1 {
|
if len(outputs) < 1 {
|
||||||
return nil, lnwallet.ErrNoOutputs
|
return nil, lnwallet.ErrNoOutputs
|
||||||
}
|
}
|
||||||
return b.wallet.SendOutputs(outputs, defaultAccount, 1, feeSatPerKB, "")
|
|
||||||
|
return b.wallet.SendOutputs(
|
||||||
|
outputs, defaultAccount, 1, feeSatPerKB, label,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateSimpleTx creates a Bitcoin transaction paying to the specified
|
// CreateSimpleTx creates a Bitcoin transaction paying to the specified
|
||||||
|
@ -174,7 +174,7 @@ type WalletController interface {
|
|||||||
// This method also takes the target fee expressed in sat/kw that should
|
// This method also takes the target fee expressed in sat/kw that should
|
||||||
// be used when crafting the transaction.
|
// be used when crafting the transaction.
|
||||||
SendOutputs(outputs []*wire.TxOut,
|
SendOutputs(outputs []*wire.TxOut,
|
||||||
feeRate chainfee.SatPerKWeight) (*wire.MsgTx, error)
|
feeRate chainfee.SatPerKWeight, label string) (*wire.MsgTx, error)
|
||||||
|
|
||||||
// CreateSimpleTx creates a Bitcoin transaction paying to the specified
|
// CreateSimpleTx creates a Bitcoin transaction paying to the specified
|
||||||
// outputs. The transaction is not broadcasted to the network. In the
|
// outputs. The transaction is not broadcasted to the network. In the
|
||||||
|
@ -174,7 +174,9 @@ func sendCoins(t *testing.T, miner *rpctest.Harness,
|
|||||||
|
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
tx, err := sender.SendOutputs([]*wire.TxOut{output}, 2500)
|
tx, err := sender.SendOutputs(
|
||||||
|
[]*wire.TxOut{output}, 2500, labels.External,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to send transaction: %v", err)
|
t.Fatalf("unable to send transaction: %v", err)
|
||||||
}
|
}
|
||||||
@ -1220,7 +1222,9 @@ func testListTransactionDetails(miner *rpctest.Harness,
|
|||||||
t.Fatalf("unable to make output script: %v", err)
|
t.Fatalf("unable to make output script: %v", err)
|
||||||
}
|
}
|
||||||
burnOutput := wire.NewTxOut(outputAmt, outputScript)
|
burnOutput := wire.NewTxOut(outputAmt, outputScript)
|
||||||
burnTX, err := alice.SendOutputs([]*wire.TxOut{burnOutput}, 2500)
|
burnTX, err := alice.SendOutputs(
|
||||||
|
[]*wire.TxOut{burnOutput}, 2500, labels.External,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create burn tx: %v", err)
|
t.Fatalf("unable to create burn tx: %v", err)
|
||||||
}
|
}
|
||||||
@ -1493,7 +1497,9 @@ func testTransactionSubscriptions(miner *rpctest.Harness,
|
|||||||
t.Fatalf("unable to make output script: %v", err)
|
t.Fatalf("unable to make output script: %v", err)
|
||||||
}
|
}
|
||||||
burnOutput := wire.NewTxOut(outputAmt, outputScript)
|
burnOutput := wire.NewTxOut(outputAmt, outputScript)
|
||||||
tx, err := alice.SendOutputs([]*wire.TxOut{burnOutput}, 2500)
|
tx, err := alice.SendOutputs(
|
||||||
|
[]*wire.TxOut{burnOutput}, 2500, labels.External,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create burn tx: %v", err)
|
t.Fatalf("unable to create burn tx: %v", err)
|
||||||
}
|
}
|
||||||
@ -1684,7 +1690,9 @@ func newTx(t *testing.T, r *rpctest.Harness, pubKey *btcec.PublicKey,
|
|||||||
Value: btcutil.SatoshiPerBitcoin,
|
Value: btcutil.SatoshiPerBitcoin,
|
||||||
PkScript: keyScript,
|
PkScript: keyScript,
|
||||||
}
|
}
|
||||||
tx, err := alice.SendOutputs([]*wire.TxOut{newOutput}, 2500)
|
tx, err := alice.SendOutputs(
|
||||||
|
[]*wire.TxOut{newOutput}, 2500, labels.External,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create output: %v", err)
|
t.Fatalf("unable to create output: %v", err)
|
||||||
}
|
}
|
||||||
@ -1973,7 +1981,9 @@ func testSignOutputUsingTweaks(r *rpctest.Harness,
|
|||||||
Value: btcutil.SatoshiPerBitcoin,
|
Value: btcutil.SatoshiPerBitcoin,
|
||||||
PkScript: keyScript,
|
PkScript: keyScript,
|
||||||
}
|
}
|
||||||
tx, err := alice.SendOutputs([]*wire.TxOut{newOutput}, 2500)
|
tx, err := alice.SendOutputs(
|
||||||
|
[]*wire.TxOut{newOutput}, 2500, labels.External,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create output: %v", err)
|
t.Fatalf("unable to create output: %v", err)
|
||||||
}
|
}
|
||||||
@ -2095,7 +2105,9 @@ func testReorgWalletBalance(r *rpctest.Harness, w *lnwallet.LightningWallet,
|
|||||||
Value: 1e8,
|
Value: 1e8,
|
||||||
PkScript: script,
|
PkScript: script,
|
||||||
}
|
}
|
||||||
tx, err := w.SendOutputs([]*wire.TxOut{output}, 2500)
|
tx, err := w.SendOutputs(
|
||||||
|
[]*wire.TxOut{output}, 2500, labels.External,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to send outputs: %v", err)
|
t.Fatalf("unable to send outputs: %v", err)
|
||||||
}
|
}
|
||||||
@ -2476,7 +2488,7 @@ func testCreateSimpleTx(r *rpctest.Harness, w *lnwallet.LightningWallet,
|
|||||||
// _very_ similar to the one we just created being sent. The
|
// _very_ similar to the one we just created being sent. The
|
||||||
// only difference is that the dry run tx is not signed, and
|
// only difference is that the dry run tx is not signed, and
|
||||||
// that the change output position might be different.
|
// that the change output position might be different.
|
||||||
tx, sendErr := w.SendOutputs(outputs, feeRate)
|
tx, sendErr := w.SendOutputs(outputs, feeRate, labels.External)
|
||||||
switch {
|
switch {
|
||||||
case test.valid && sendErr != nil:
|
case test.valid && sendErr != nil:
|
||||||
t.Fatalf("got unexpected error when sending tx: %v",
|
t.Fatalf("got unexpected error when sending tx: %v",
|
||||||
|
2
mock.go
2
mock.go
@ -281,7 +281,7 @@ func (*mockWalletController) IsOurAddress(a btcutil.Address) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (*mockWalletController) SendOutputs(outputs []*wire.TxOut,
|
func (*mockWalletController) SendOutputs(outputs []*wire.TxOut,
|
||||||
_ chainfee.SatPerKWeight) (*wire.MsgTx, error) {
|
_ chainfee.SatPerKWeight, _ string) (*wire.MsgTx, error) {
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
25
rpcserver.go
25
rpcserver.go
@ -44,6 +44,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/invoices"
|
"github.com/lightningnetwork/lnd/invoices"
|
||||||
"github.com/lightningnetwork/lnd/keychain"
|
"github.com/lightningnetwork/lnd/keychain"
|
||||||
|
"github.com/lightningnetwork/lnd/labels"
|
||||||
"github.com/lightningnetwork/lnd/lncfg"
|
"github.com/lightningnetwork/lnd/lncfg"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
|
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
|
||||||
@ -924,14 +925,14 @@ func addrPairsToOutputs(addrPairs map[string]int64) ([]*wire.TxOut, error) {
|
|||||||
// more addresses specified in the passed payment map. The payment map maps an
|
// more addresses specified in the passed payment map. The payment map maps an
|
||||||
// address to a specified output value to be sent to that address.
|
// address to a specified output value to be sent to that address.
|
||||||
func (r *rpcServer) sendCoinsOnChain(paymentMap map[string]int64,
|
func (r *rpcServer) sendCoinsOnChain(paymentMap map[string]int64,
|
||||||
feeRate chainfee.SatPerKWeight) (*chainhash.Hash, error) {
|
feeRate chainfee.SatPerKWeight, label string) (*chainhash.Hash, error) {
|
||||||
|
|
||||||
outputs, err := addrPairsToOutputs(paymentMap)
|
outputs, err := addrPairsToOutputs(paymentMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tx, err := r.server.cc.wallet.SendOutputs(outputs, feeRate)
|
tx, err := r.server.cc.wallet.SendOutputs(outputs, feeRate, label)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -1147,6 +1148,11 @@ func (r *rpcServer) SendCoins(ctx context.Context,
|
|||||||
return nil, fmt.Errorf("cannot send coins to pubkeys")
|
return nil, fmt.Errorf("cannot send coins to pubkeys")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
label, err := labels.ValidateAPI(in.Label)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
var txid *chainhash.Hash
|
var txid *chainhash.Hash
|
||||||
|
|
||||||
wallet := r.server.cc.wallet
|
wallet := r.server.cc.wallet
|
||||||
@ -1187,9 +1193,7 @@ func (r *rpcServer) SendCoins(ctx context.Context,
|
|||||||
// As our sweep transaction was created, successfully, we'll
|
// As our sweep transaction was created, successfully, we'll
|
||||||
// now attempt to publish it, cancelling the sweep pkg to
|
// now attempt to publish it, cancelling the sweep pkg to
|
||||||
// return all outputs if it fails.
|
// return all outputs if it fails.
|
||||||
err = wallet.PublishTransaction(
|
err = wallet.PublishTransaction(sweepTxPkg.SweepTx, label)
|
||||||
sweepTxPkg.SweepTx, "",
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sweepTxPkg.CancelSweepAttempt()
|
sweepTxPkg.CancelSweepAttempt()
|
||||||
|
|
||||||
@ -1207,7 +1211,9 @@ func (r *rpcServer) SendCoins(ctx context.Context,
|
|||||||
// while we instruct the wallet to send this transaction.
|
// while we instruct the wallet to send this transaction.
|
||||||
paymentMap := map[string]int64{targetAddr.String(): in.Amount}
|
paymentMap := map[string]int64{targetAddr.String(): in.Amount}
|
||||||
err := wallet.WithCoinSelectLock(func() error {
|
err := wallet.WithCoinSelectLock(func() error {
|
||||||
newTXID, err := r.sendCoinsOnChain(paymentMap, feePerKw)
|
newTXID, err := r.sendCoinsOnChain(
|
||||||
|
paymentMap, feePerKw, label,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -1244,6 +1250,11 @@ func (r *rpcServer) SendMany(ctx context.Context,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
label, err := labels.ValidateAPI(in.Label)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
rpcsLog.Infof("[sendmany] outputs=%v, sat/kw=%v",
|
rpcsLog.Infof("[sendmany] outputs=%v, sat/kw=%v",
|
||||||
spew.Sdump(in.AddrToAmount), int64(feePerKw))
|
spew.Sdump(in.AddrToAmount), int64(feePerKw))
|
||||||
|
|
||||||
@ -1255,7 +1266,7 @@ func (r *rpcServer) SendMany(ctx context.Context,
|
|||||||
wallet := r.server.cc.wallet
|
wallet := r.server.cc.wallet
|
||||||
err = wallet.WithCoinSelectLock(func() error {
|
err = wallet.WithCoinSelectLock(func() error {
|
||||||
sendManyTXID, err := r.sendCoinsOnChain(
|
sendManyTXID, err := r.sendCoinsOnChain(
|
||||||
in.AddrToAmount, feePerKw,
|
in.AddrToAmount, feePerKw, label,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
Loading…
x
Reference in New Issue
Block a user