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:
carla 2020-05-18 14:13:24 +02:00
parent 75370ce6b4
commit 099161ed0b
No known key found for this signature in database
GPG Key ID: 4CA7FE54A6213C91
10 changed files with 773 additions and 692 deletions

View File

@ -3,6 +3,31 @@
// package to avoid dependency issues.
package labels
import (
"fmt"
"github.com/btcsuite/btcwallet/wtxmgr"
)
// External labels a transaction as user initiated via the api. This
// label is only used when a custom user provided label is not given.
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
}

File diff suppressed because it is too large Load Diff

View File

@ -1002,6 +1002,9 @@ message SendManyRequest {
// A manual fee rate set in sat/byte that should be used when crafting the
// transaction.
int64 sat_per_byte = 5;
// An optional label for the transaction, limited to 500 characters.
string label = 6;
}
message SendManyResponse {
// The id of the transaction
@ -1029,6 +1032,9 @@ message SendCoinsRequest {
address.
*/
bool send_all = 6;
// An optional label for the transaction, limited to 500 characters.
string label = 7;
}
message SendCoinsResponse {
// The transaction ID of the transaction

View File

@ -4230,6 +4230,10 @@
"type": "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."
},
"label": {
"type": "string",
"description": "An optional label for the transaction, limited to 500 characters."
}
}
},

View File

@ -311,6 +311,7 @@ func (w *WalletKit) SendOutputs(ctx context.Context,
// attempt to create this transaction.
tx, err := w.cfg.Wallet.SendOutputs(
outputsToCreate, chainfee.SatPerKWeight(req.SatPerKw),
labels.External,
)
if err != nil {
return nil, err

View File

@ -294,7 +294,7 @@ func (b *BtcWallet) IsOurAddress(a btcutil.Address) bool {
//
// This is a part of the WalletController interface.
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
// SendOutputs.
@ -304,7 +304,10 @@ func (b *BtcWallet) SendOutputs(outputs []*wire.TxOut,
if len(outputs) < 1 {
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

View File

@ -174,7 +174,7 @@ type WalletController interface {
// This method also takes the target fee expressed in sat/kw that should
// be used when crafting the transaction.
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
// outputs. The transaction is not broadcasted to the network. In the

View File

@ -174,7 +174,9 @@ func sendCoins(t *testing.T, miner *rpctest.Harness,
t.Helper()
tx, err := sender.SendOutputs([]*wire.TxOut{output}, 2500)
tx, err := sender.SendOutputs(
[]*wire.TxOut{output}, 2500, labels.External,
)
if err != nil {
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)
}
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 {
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)
}
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 {
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,
PkScript: keyScript,
}
tx, err := alice.SendOutputs([]*wire.TxOut{newOutput}, 2500)
tx, err := alice.SendOutputs(
[]*wire.TxOut{newOutput}, 2500, labels.External,
)
if err != nil {
t.Fatalf("unable to create output: %v", err)
}
@ -1973,7 +1981,9 @@ func testSignOutputUsingTweaks(r *rpctest.Harness,
Value: btcutil.SatoshiPerBitcoin,
PkScript: keyScript,
}
tx, err := alice.SendOutputs([]*wire.TxOut{newOutput}, 2500)
tx, err := alice.SendOutputs(
[]*wire.TxOut{newOutput}, 2500, labels.External,
)
if err != nil {
t.Fatalf("unable to create output: %v", err)
}
@ -2095,7 +2105,9 @@ func testReorgWalletBalance(r *rpctest.Harness, w *lnwallet.LightningWallet,
Value: 1e8,
PkScript: script,
}
tx, err := w.SendOutputs([]*wire.TxOut{output}, 2500)
tx, err := w.SendOutputs(
[]*wire.TxOut{output}, 2500, labels.External,
)
if err != nil {
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
// only difference is that the dry run tx is not signed, and
// that the change output position might be different.
tx, sendErr := w.SendOutputs(outputs, feeRate)
tx, sendErr := w.SendOutputs(outputs, feeRate, labels.External)
switch {
case test.valid && sendErr != nil:
t.Fatalf("got unexpected error when sending tx: %v",

View File

@ -281,7 +281,7 @@ func (*mockWalletController) IsOurAddress(a btcutil.Address) bool {
}
func (*mockWalletController) SendOutputs(outputs []*wire.TxOut,
_ chainfee.SatPerKWeight) (*wire.MsgTx, error) {
_ chainfee.SatPerKWeight, _ string) (*wire.MsgTx, error) {
return nil, nil
}

View File

@ -44,6 +44,7 @@ import (
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/invoices"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/labels"
"github.com/lightningnetwork/lnd/lncfg"
"github.com/lightningnetwork/lnd/lnrpc"
"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
// address to a specified output value to be sent to that address.
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)
if err != nil {
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 {
return nil, err
}
@ -1147,6 +1148,11 @@ func (r *rpcServer) SendCoins(ctx context.Context,
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
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
// now attempt to publish it, cancelling the sweep pkg to
// return all outputs if it fails.
err = wallet.PublishTransaction(
sweepTxPkg.SweepTx, "",
)
err = wallet.PublishTransaction(sweepTxPkg.SweepTx, label)
if err != nil {
sweepTxPkg.CancelSweepAttempt()
@ -1207,7 +1211,9 @@ func (r *rpcServer) SendCoins(ctx context.Context,
// while we instruct the wallet to send this transaction.
paymentMap := map[string]int64{targetAddr.String(): in.Amount}
err := wallet.WithCoinSelectLock(func() error {
newTXID, err := r.sendCoinsOnChain(paymentMap, feePerKw)
newTXID, err := r.sendCoinsOnChain(
paymentMap, feePerKw, label,
)
if err != nil {
return err
}
@ -1244,6 +1250,11 @@ func (r *rpcServer) SendMany(ctx context.Context,
return nil, err
}
label, err := labels.ValidateAPI(in.Label)
if err != nil {
return nil, err
}
rpcsLog.Infof("[sendmany] outputs=%v, sat/kw=%v",
spew.Sdump(in.AddrToAmount), int64(feePerKw))
@ -1255,7 +1266,7 @@ func (r *rpcServer) SendMany(ctx context.Context,
wallet := r.server.cc.wallet
err = wallet.WithCoinSelectLock(func() error {
sendManyTXID, err := r.sendCoinsOnChain(
in.AddrToAmount, feePerKw,
in.AddrToAmount, feePerKw, label,
)
if err != nil {
return err