Merge pull request #4213 from carlaKC/txdetails-addlabel

lnrpc: add optional labels to on chain transactions
This commit is contained in:
Conner Fromknecht 2020-05-19 14:44:55 -07:00 committed by GitHub
commit afc60353a1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 1084 additions and 882 deletions

View file

@ -90,7 +90,7 @@ type BreachConfig struct {
// PublishTransaction facilitates the process of broadcasting a
// transaction to the network.
PublishTransaction func(*wire.MsgTx) error
PublishTransaction func(*wire.MsgTx, string) error
// ContractBreaches is a channel where the breachArbiter will receive
// notifications in the event of a contract breach being observed. A
@ -566,7 +566,7 @@ justiceTxBroadcast:
// We'll now attempt to broadcast the transaction which finalized the
// channel's retribution against the cheating counter party.
err = b.cfg.PublishTransaction(finalTx)
err = b.cfg.PublishTransaction(finalTx, "")
if err != nil {
brarLog.Errorf("Unable to broadcast justice tx: %v", err)

View file

@ -1371,7 +1371,7 @@ func testBreachSpends(t *testing.T, test breachTest) {
// Make PublishTransaction always return ErrDoubleSpend to begin with.
publErr = lnwallet.ErrDoubleSpend
brar.cfg.PublishTransaction = func(tx *wire.MsgTx) error {
brar.cfg.PublishTransaction = func(tx *wire.MsgTx, _ string) error {
publTx <- tx
publMtx.Lock()
@ -1681,7 +1681,7 @@ func createTestArbiter(t *testing.T, contractBreaches chan *ContractBreachEvent,
ContractBreaches: contractBreaches,
Signer: signer,
Notifier: notifier,
PublishTransaction: func(_ *wire.MsgTx) error { return nil },
PublishTransaction: func(_ *wire.MsgTx, _ string) error { return nil },
Store: store,
})

View file

@ -83,7 +83,7 @@ type chanCloseCfg struct {
unregisterChannel func(lnwire.ChannelID)
// broadcastTx broadcasts the passed transaction to the network.
broadcastTx func(*wire.MsgTx) error
broadcastTx func(*wire.MsgTx, string) error
// disableChannel disables a channel, resulting in it not being able to
// forward payments.
@ -544,7 +544,8 @@ func (c *channelCloser) ProcessCloseMsg(msg lnwire.Message) ([]lnwire.Message, b
newLogClosure(func() string {
return spew.Sdump(closeTx)
}))
if err := c.cfg.broadcastTx(closeTx); err != nil {
err = c.cfg.broadcastTx(closeTx, "")
if err != nil {
return nil, false, err
}

View file

@ -194,6 +194,11 @@ func estimateFees(ctx *cli.Context) error {
return nil
}
var txLabelFlag = cli.StringFlag{
Name: "label",
Usage: "(optional) a label for the transaction",
}
var sendCoinsCommand = cli.Command{
Name: "sendcoins",
Category: "On-chain",
@ -236,6 +241,7 @@ var sendCoinsCommand = cli.Command{
"sat/byte that should be used when crafting " +
"the transaction",
},
txLabelFlag,
},
Action: actionDecorator(sendCoins),
}
@ -295,6 +301,7 @@ func sendCoins(ctx *cli.Context) error {
TargetConf: int32(ctx.Int64("conf_target")),
SatPerByte: ctx.Int64("sat_per_byte"),
SendAll: ctx.Bool("sweepall"),
Label: ctx.String(txLabelFlag.Name),
}
txid, err := client.SendCoins(ctxb, req)
if err != nil {
@ -450,6 +457,7 @@ var sendManyCommand = cli.Command{
Usage: "(optional) a manual fee expressed in sat/byte that should be " +
"used when crafting the transaction",
},
txLabelFlag,
},
Action: actionDecorator(sendMany),
}
@ -475,6 +483,7 @@ func sendMany(ctx *cli.Context) error {
AddrToAmount: amountToAddr,
TargetConf: int32(ctx.Int64("conf_target")),
SatPerByte: ctx.Int64("sat_per_byte"),
Label: ctx.String(txLabelFlag.Name),
})
if err != nil {
return err

View file

@ -79,7 +79,7 @@ type ChainArbitratorConfig struct {
// PublishTx reliably broadcasts a transaction to the network. Once
// this function exits without an error, then they transaction MUST
// continually be rebroadcast if needed.
PublishTx func(*wire.MsgTx) error
PublishTx func(*wire.MsgTx, string) error
// DeliverResolutionMsg is a function that will append an outgoing
// message to the "out box" for a ChannelLink. This is used to cancel
@ -699,7 +699,7 @@ func (c *ChainArbitrator) rebroadcast(channel *channeldb.OpenChannel,
log.Infof("Re-publishing %s close tx(%v) for channel %v",
kind, closeTx.TxHash(), chanPoint)
err = c.cfg.PublishTx(closeTx)
err = c.cfg.PublishTx(closeTx, "")
if err != nil && err != lnwallet.ErrDoubleSpend {
log.Warnf("Unable to broadcast %s close tx(%v): %v",
kind, closeTx.TxHash(), err)

View file

@ -82,7 +82,7 @@ func TestChainArbitratorRepublishCloses(t *testing.T) {
chainArbCfg := ChainArbitratorConfig{
ChainIO: &mockChainIO{},
Notifier: &mockNotifier{},
PublishTx: func(tx *wire.MsgTx) error {
PublishTx: func(tx *wire.MsgTx, _ string) error {
published[tx.TxHash()]++
return nil
},
@ -174,7 +174,7 @@ func TestResolveContract(t *testing.T) {
chainArbCfg := ChainArbitratorConfig{
ChainIO: &mockChainIO{},
Notifier: &mockNotifier{},
PublishTx: func(tx *wire.MsgTx) error {
PublishTx: func(tx *wire.MsgTx, _ string) error {
return nil
},
Clock: clock.NewDefaultClock(),

View file

@ -851,7 +851,7 @@ func (c *ChannelArbitrator) stateStep(
// At this point, we'll now broadcast the commitment
// transaction itself.
if err := c.cfg.PublishTx(closeTx); err != nil {
if err := c.cfg.PublishTx(closeTx, ""); err != nil {
log.Errorf("ChannelArbitrator(%v): unable to broadcast "+
"close tx: %v", c.cfg.ChanPoint, err)
if err != lnwallet.ErrDoubleSpend {

View file

@ -324,7 +324,7 @@ func createTestChannelArbitrator(t *testing.T, log ArbitratorLog,
mockSweeper := newMockSweeper()
chainArbCfg := ChainArbitratorConfig{
ChainIO: chainIO,
PublishTx: func(*wire.MsgTx) error {
PublishTx: func(*wire.MsgTx, string) error {
return nil
},
DeliverResolutionMsg: func(msgs ...ResolutionMsg) error {
@ -575,7 +575,7 @@ func TestChannelArbitratorLocalForceClose(t *testing.T) {
// We create a channel we can use to pause the ChannelArbitrator at the
// point where it broadcasts the close tx, and check its state.
stateChan := make(chan ArbitratorState)
chanArb.cfg.PublishTx = func(*wire.MsgTx) error {
chanArb.cfg.PublishTx = func(*wire.MsgTx, string) error {
// When the force close tx is being broadcasted, check that the
// state is correct at that point.
select {
@ -998,7 +998,7 @@ func TestChannelArbitratorLocalForceCloseRemoteConfirmed(t *testing.T) {
// Create a channel we can use to assert the state when it publishes
// the close tx.
stateChan := make(chan ArbitratorState)
chanArb.cfg.PublishTx = func(*wire.MsgTx) error {
chanArb.cfg.PublishTx = func(*wire.MsgTx, string) error {
// When the force close tx is being broadcasted, check that the
// state is correct at that point.
select {
@ -1106,7 +1106,7 @@ func TestChannelArbitratorLocalForceDoubleSpend(t *testing.T) {
// Return ErrDoubleSpend when attempting to publish the tx.
stateChan := make(chan ArbitratorState)
chanArb.cfg.PublishTx = func(*wire.MsgTx) error {
chanArb.cfg.PublishTx = func(*wire.MsgTx, string) error {
// When the force close tx is being broadcasted, check that the
// state is correct at that point.
select {
@ -1339,7 +1339,7 @@ func TestChannelArbitratorForceCloseBreachedChannel(t *testing.T) {
// unexpected publication error, causing the state machine to halt.
expErr := errors.New("intentional publication error")
stateChan := make(chan ArbitratorState)
chanArb.cfg.PublishTx = func(*wire.MsgTx) error {
chanArb.cfg.PublishTx = func(*wire.MsgTx, string) error {
// When the force close tx is being broadcasted, check that the
// state is correct at that point.
select {

View file

@ -155,7 +155,7 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) {
// Regardless of whether an existing transaction was found or newly
// constructed, we'll broadcast the sweep transaction to the
// network.
err := h.PublishTx(h.sweepTx)
err := h.PublishTx(h.sweepTx, "")
if err != nil {
log.Infof("%T(%x): unable to publish tx: %v",
h, h.htlc.RHash[:], err)
@ -199,7 +199,7 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) {
// the claiming process.
//
// TODO(roasbeef): after changing sighashes send to tx bundler
err := h.PublishTx(h.htlcResolution.SignedSuccessTx)
err := h.PublishTx(h.htlcResolution.SignedSuccessTx, "")
if err != nil {
return nil, err
}

View file

@ -236,7 +236,7 @@ type fundingConfig struct {
// PublishTransaction facilitates the process of broadcasting a
// transaction to the network.
PublishTransaction func(*wire.MsgTx) error
PublishTransaction func(*wire.MsgTx, string) error
// FeeEstimator calculates appropriate fee rates based on historical
// transaction information.
@ -553,7 +553,7 @@ func (f *fundingManager) start() error {
channel.IsInitiator {
err := f.cfg.PublishTransaction(
channel.FundingTxn,
channel.FundingTxn, "",
)
if err != nil {
fndgLog.Errorf("Unable to rebroadcast "+
@ -1995,7 +1995,7 @@ func (f *fundingManager) handleFundingSigned(fmsg *fundingSignedMsg) {
fndgLog.Infof("Broadcasting funding tx for ChannelPoint(%v): %v",
completeChan.FundingOutpoint, spew.Sdump(fundingTx))
err = f.cfg.PublishTransaction(fundingTx)
err = f.cfg.PublishTransaction(fundingTx, "")
if err != nil {
fndgLog.Errorf("Unable to broadcast funding tx for "+
"ChannelPoint(%v): %v",

View file

@ -412,7 +412,7 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey,
ReportShortChanID: func(wire.OutPoint) error {
return nil
},
PublishTransaction: func(txn *wire.MsgTx) error {
PublishTransaction: func(txn *wire.MsgTx, _ string) error {
publTxChan <- txn
return nil
},
@ -515,7 +515,7 @@ func recreateAliceFundingManager(t *testing.T, alice *testNode) {
},
DefaultMinHtlcIn: 5,
RequiredRemoteMaxValue: oldCfg.RequiredRemoteMaxValue,
PublishTransaction: func(txn *wire.MsgTx) error {
PublishTransaction: func(txn *wire.MsgTx, _ string) error {
publishChan <- txn
return nil
},

33
labels/labels.go Normal file
View file

@ -0,0 +1,33 @@
// Package labels contains labels used to label transactions broadcast by lnd.
// These labels are used across packages, so they are declared in a separate
// 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

@ -708,6 +708,9 @@ message Transaction {
// The raw transaction hex.
string raw_tx_hex = 9;
// A label that was optionally set on transaction broadcast.
string label = 10;
}
message GetTransactionsRequest {
/*
@ -1002,6 +1005,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 +1035,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."
}
}
},
@ -4451,6 +4455,10 @@
"raw_tx_hex": {
"type": "string",
"description": "The raw transaction hex."
},
"label": {
"type": "string",
"description": "A label that was optionally set on transaction broadcast."
}
}
},

View file

@ -36,6 +36,7 @@ func RPCTransactionDetails(txns []*lnwallet.TransactionDetail) *TransactionDetai
TotalFees: tx.TotalFees,
DestAddresses: destAddresses,
RawTxHex: hex.EncodeToString(tx.RawTx),
Label: tx.Label,
}
}

View file

@ -261,7 +261,10 @@ func (m *AddrResponse) GetAddr() string {
type Transaction struct {
//
//The raw serialized transaction.
TxHex []byte `protobuf:"bytes,1,opt,name=tx_hex,json=txHex,proto3" json:"tx_hex,omitempty"`
TxHex []byte `protobuf:"bytes,1,opt,name=tx_hex,json=txHex,proto3" json:"tx_hex,omitempty"`
//
//An optional label to save with the transaction. Limited to 500 characters.
Label string `protobuf:"bytes,2,opt,name=label,proto3" json:"label,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -299,6 +302,13 @@ func (m *Transaction) GetTxHex() []byte {
return nil
}
func (m *Transaction) GetLabel() string {
if m != nil {
return m.Label
}
return ""
}
type PublishResponse struct {
//
//If blank, then no error occurred and the transaction was successfully
@ -351,10 +361,12 @@ type SendOutputsRequest struct {
SatPerKw int64 `protobuf:"varint,1,opt,name=sat_per_kw,json=satPerKw,proto3" json:"sat_per_kw,omitempty"`
//
//A slice of the outputs that should be created in the transaction produced.
Outputs []*signrpc.TxOut `protobuf:"bytes,2,rep,name=outputs,proto3" json:"outputs,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
Outputs []*signrpc.TxOut `protobuf:"bytes,2,rep,name=outputs,proto3" json:"outputs,omitempty"`
// An optional label for the transaction, limited to 500 characters.
Label string `protobuf:"bytes,3,opt,name=label,proto3" json:"label,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SendOutputsRequest) Reset() { *m = SendOutputsRequest{} }
@ -396,6 +408,13 @@ func (m *SendOutputsRequest) GetOutputs() []*signrpc.TxOut {
return nil
}
func (m *SendOutputsRequest) GetLabel() string {
if m != nil {
return m.Label
}
return ""
}
type SendOutputsResponse struct {
//
//The serialized transaction sent out on the network.
@ -1002,81 +1021,82 @@ func init() {
func init() { proto.RegisterFile("walletrpc/walletkit.proto", fileDescriptor_6cc6942ac78249e5) }
var fileDescriptor_6cc6942ac78249e5 = []byte{
// 1178 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x56, 0x6d, 0x6f, 0xe2, 0x46,
0x10, 0x3e, 0x42, 0x42, 0x60, 0x78, 0x09, 0x59, 0xf2, 0xe2, 0xe3, 0x72, 0x0d, 0x75, 0xdf, 0xa2,
0xf6, 0x8e, 0xa8, 0x39, 0xb5, 0xea, 0xb5, 0x52, 0xd5, 0x04, 0x1c, 0x11, 0x41, 0x70, 0x6a, 0xfb,
0x2e, 0xba, 0xaa, 0xd2, 0xca, 0xe0, 0x0d, 0xb1, 0x02, 0xb6, 0x6f, 0xbd, 0x1c, 0xf0, 0xad, 0x5f,
0xfa, 0x17, 0x2a, 0xdd, 0xbf, 0xad, 0xbc, 0x7e, 0x61, 0x0d, 0xcd, 0x49, 0xfd, 0x14, 0x76, 0x9e,
0x67, 0x9e, 0x9d, 0x9d, 0x19, 0xcf, 0x04, 0x9e, 0xce, 0xcc, 0xf1, 0x98, 0x30, 0xea, 0x0d, 0x4f,
0xc3, 0x5f, 0x0f, 0x36, 0x6b, 0x7a, 0xd4, 0x65, 0x2e, 0x2a, 0x24, 0x50, 0xbd, 0x40, 0xbd, 0x61,
0x68, 0xad, 0xef, 0xf9, 0xf6, 0xc8, 0x09, 0xe8, 0xc1, 0x5f, 0x42, 0x43, 0xab, 0xfc, 0x3b, 0xe4,
0xba, 0x64, 0xa1, 0x91, 0xf7, 0xe8, 0x04, 0xaa, 0x0f, 0x64, 0x81, 0xef, 0x6c, 0x67, 0x44, 0x28,
0xf6, 0xa8, 0xed, 0x30, 0x29, 0xd3, 0xc8, 0x9c, 0x6c, 0x69, 0x95, 0x07, 0xb2, 0xb8, 0xe4, 0xe6,
0x9b, 0xc0, 0x8a, 0x9e, 0x03, 0x70, 0xa6, 0x39, 0xb1, 0xc7, 0x0b, 0x69, 0x83, 0x73, 0x0a, 0x01,
0x87, 0x1b, 0xe4, 0x32, 0x14, 0xcf, 0x2d, 0x8b, 0x6a, 0xe4, 0xfd, 0x94, 0xf8, 0x4c, 0x96, 0xa1,
0x14, 0x1e, 0x7d, 0xcf, 0x75, 0x7c, 0x82, 0x10, 0x6c, 0x9a, 0x96, 0x45, 0xb9, 0x76, 0x41, 0xe3,
0xbf, 0xe5, 0x2f, 0xa1, 0x68, 0x50, 0xd3, 0xf1, 0xcd, 0x21, 0xb3, 0x5d, 0x07, 0xed, 0x43, 0x8e,
0xcd, 0xf1, 0x3d, 0x99, 0x73, 0x52, 0x49, 0xdb, 0x62, 0xf3, 0x0e, 0x99, 0xcb, 0x3f, 0xc2, 0xce,
0xcd, 0x74, 0x30, 0xb6, 0xfd, 0xfb, 0x44, 0xec, 0x0b, 0x28, 0x7b, 0xa1, 0x09, 0x13, 0x4a, 0xdd,
0x58, 0xb5, 0x14, 0x19, 0x95, 0xc0, 0x26, 0xff, 0x09, 0x48, 0x27, 0x8e, 0xa5, 0x4e, 0x99, 0x37,
0x65, 0x7e, 0x14, 0x17, 0x3a, 0x02, 0xf0, 0x4d, 0x86, 0x3d, 0x42, 0xf1, 0xc3, 0x8c, 0xfb, 0x65,
0xb5, 0xbc, 0x6f, 0xb2, 0x1b, 0x42, 0xbb, 0x33, 0x74, 0x02, 0xdb, 0x6e, 0xc8, 0x97, 0x36, 0x1a,
0xd9, 0x93, 0xe2, 0x59, 0xa5, 0x19, 0xe5, 0xaf, 0x69, 0xcc, 0xd5, 0x29, 0xd3, 0x62, 0x58, 0x7e,
0x01, 0xb5, 0x94, 0x7a, 0x14, 0xd9, 0x3e, 0xe4, 0xa8, 0x39, 0xc3, 0x2c, 0x79, 0x03, 0x35, 0x67,
0xc6, 0x5c, 0xfe, 0x01, 0x90, 0xe2, 0x33, 0x7b, 0x62, 0x32, 0x72, 0x49, 0x48, 0x1c, 0xcb, 0x31,
0x14, 0x87, 0xae, 0x73, 0x87, 0x99, 0x49, 0x47, 0x24, 0x4e, 0x3b, 0x04, 0x26, 0x83, 0x5b, 0xe4,
0x57, 0x50, 0x4b, 0xb9, 0x45, 0x97, 0x7c, 0xf2, 0x0d, 0xf2, 0xc7, 0x2c, 0x94, 0x6e, 0x88, 0x63,
0xd9, 0xce, 0x48, 0x9f, 0x11, 0xe2, 0xa1, 0xef, 0x20, 0x1f, 0x44, 0xed, 0xc6, 0xa5, 0x2d, 0x9e,
0xed, 0x34, 0xc7, 0xfc, 0x4d, 0xea, 0x94, 0xdd, 0x04, 0x66, 0x2d, 0x21, 0xa0, 0xd7, 0x50, 0x9a,
0xd9, 0xcc, 0x21, 0xbe, 0x8f, 0xd9, 0xc2, 0x23, 0xbc, 0xce, 0x95, 0xb3, 0x83, 0x66, 0xd2, 0x5c,
0xcd, 0xdb, 0x10, 0x36, 0x16, 0x1e, 0xd1, 0x8a, 0xb3, 0xe5, 0x21, 0x68, 0x10, 0x73, 0xe2, 0x4e,
0x1d, 0x86, 0x7d, 0x93, 0x49, 0xd9, 0x46, 0xe6, 0xa4, 0xac, 0x15, 0x42, 0x8b, 0x6e, 0x32, 0xd4,
0x80, 0x52, 0x1c, 0xf5, 0x60, 0xc1, 0x88, 0xb4, 0xc9, 0x09, 0x10, 0xc6, 0x7d, 0xb1, 0x60, 0x04,
0xbd, 0x04, 0x34, 0xa0, 0xae, 0x69, 0x0d, 0x4d, 0x9f, 0x61, 0x93, 0x31, 0x32, 0xf1, 0x98, 0x2f,
0x6d, 0x71, 0xde, 0x6e, 0x82, 0x9c, 0x47, 0x00, 0x3a, 0x83, 0x7d, 0x87, 0xcc, 0x19, 0x5e, 0xfa,
0xdc, 0x13, 0x7b, 0x74, 0xcf, 0xa4, 0x1c, 0xf7, 0xa8, 0x05, 0xe0, 0x45, 0x8c, 0x75, 0x38, 0x14,
0xf8, 0xd0, 0x30, 0xfb, 0xc4, 0xc2, 0x62, 0xf2, 0xf3, 0xa1, 0x4f, 0x02, 0xb6, 0x92, 0x2a, 0xa0,
0x57, 0x70, 0xb0, 0xf4, 0x49, 0x3d, 0xa1, 0xb0, 0xe2, 0xa4, 0x2f, 0xdf, 0xb2, 0x07, 0x5b, 0x77,
0x2e, 0x1d, 0x12, 0x69, 0xbb, 0x91, 0x39, 0xc9, 0x6b, 0xe1, 0x41, 0x3e, 0x80, 0x3d, 0xb1, 0x34,
0x71, 0x57, 0xca, 0xb7, 0xb0, 0xbf, 0x62, 0x8f, 0x4a, 0xfd, 0x2b, 0x54, 0xbc, 0x10, 0xc0, 0x3e,
0x47, 0xa4, 0x0c, 0xef, 0xcb, 0x43, 0xa1, 0x20, 0xa2, 0xa7, 0x56, 0xf6, 0x44, 0x1d, 0xf9, 0x9f,
0x0c, 0x54, 0x2e, 0xa6, 0x13, 0x4f, 0xe8, 0xba, 0xff, 0xd5, 0x0e, 0xc7, 0x50, 0x0c, 0x13, 0xc4,
0x93, 0xc5, 0xbb, 0xa1, 0xac, 0x41, 0x68, 0x0a, 0x52, 0xb4, 0x56, 0xd5, 0xec, 0x5a, 0x55, 0x93,
0x4c, 0x6c, 0x8a, 0x99, 0xd8, 0x85, 0x9d, 0x24, 0xae, 0xf0, 0xad, 0xf2, 0x4b, 0xd8, 0xed, 0xd9,
0x3e, 0x4b, 0x65, 0x06, 0x49, 0xb0, 0xfd, 0x81, 0xd0, 0x81, 0xeb, 0x13, 0x1e, 0x6c, 0x5e, 0x8b,
0x8f, 0xf2, 0x5f, 0x1b, 0x80, 0x44, 0x7e, 0x94, 0xb1, 0x1e, 0xd4, 0xd8, 0x72, 0xa8, 0x60, 0x8b,
0x30, 0xd3, 0x1e, 0xfb, 0xd1, 0x4b, 0x9f, 0x46, 0x2f, 0x15, 0xc6, 0x4e, 0x3b, 0x24, 0x74, 0x9e,
0x68, 0x88, 0xad, 0x59, 0xd1, 0x2d, 0xec, 0x88, 0x6a, 0xb6, 0xe5, 0xf3, 0x1c, 0x14, 0xcf, 0x5e,
0x08, 0x05, 0x58, 0x8f, 0x42, 0xbc, 0xe0, 0xaa, 0x1d, 0x88, 0x57, 0x04, 0x99, 0x2b, 0xcb, 0xaf,
0xbf, 0x86, 0x4a, 0x9a, 0x83, 0xbe, 0x59, 0xbf, 0x2a, 0xa8, 0x75, 0x61, 0xd5, 0xf5, 0x22, 0x0f,
0xb9, 0xb0, 0x17, 0xbe, 0xfd, 0x98, 0x85, 0xa2, 0xf0, 0x39, 0xa2, 0x1a, 0xec, 0xbc, 0xe9, 0x77,
0xfb, 0xea, 0x6d, 0x1f, 0xdf, 0x5e, 0x19, 0x7d, 0x45, 0xd7, 0xab, 0x4f, 0x90, 0x04, 0x7b, 0x2d,
0xf5, 0xfa, 0xfa, 0xca, 0xb8, 0x56, 0xfa, 0x06, 0x36, 0xae, 0xae, 0x15, 0xdc, 0x53, 0x5b, 0xdd,
0x6a, 0x06, 0x1d, 0x42, 0x4d, 0x40, 0xfa, 0x2a, 0x6e, 0x2b, 0xbd, 0xf3, 0x77, 0xd5, 0x0d, 0xb4,
0x0f, 0xbb, 0x02, 0xa0, 0x29, 0x6f, 0xd5, 0xae, 0x52, 0xcd, 0x06, 0xfc, 0x8e, 0xd1, 0x6b, 0x61,
0xf5, 0xf2, 0x52, 0xd1, 0x94, 0x76, 0x0c, 0x6c, 0x06, 0x57, 0x70, 0xe0, 0xbc, 0xd5, 0x52, 0x6e,
0x8c, 0x25, 0xb2, 0x85, 0xbe, 0x82, 0xcf, 0x53, 0x2e, 0xc1, 0xf5, 0xea, 0x1b, 0x03, 0xeb, 0x4a,
0x4b, 0xed, 0xb7, 0x71, 0x4f, 0x79, 0xab, 0xf4, 0xaa, 0x39, 0xf4, 0x35, 0xc8, 0x69, 0x01, 0xfd,
0x4d, 0xab, 0xa5, 0xe8, 0x7a, 0x9a, 0xb7, 0x8d, 0x8e, 0xe1, 0xd9, 0x4a, 0x04, 0xd7, 0xaa, 0xa1,
0xc4, 0xaa, 0xd5, 0x3c, 0x6a, 0xc0, 0xd1, 0x6a, 0x24, 0x9c, 0x11, 0xe9, 0x55, 0x0b, 0xe8, 0x08,
0x24, 0xce, 0x10, 0x95, 0xe3, 0x78, 0x01, 0xed, 0x41, 0x35, 0xca, 0x1c, 0xee, 0x2a, 0xef, 0x70,
0xe7, 0x5c, 0xef, 0x54, 0x8b, 0xe8, 0x19, 0x1c, 0xf6, 0x15, 0x3d, 0x90, 0x5b, 0x03, 0x4b, 0x2b,
0xc9, 0x3a, 0xef, 0xb7, 0x3a, 0xaa, 0x56, 0x2d, 0x9f, 0xfd, 0xbd, 0x05, 0x85, 0x5b, 0xde, 0x22,
0x5d, 0x9b, 0xa1, 0x9f, 0xa1, 0xdc, 0x26, 0xd4, 0xfe, 0x40, 0xfa, 0x64, 0xce, 0xba, 0x64, 0x81,
0x76, 0x85, 0xfe, 0x09, 0x57, 0x71, 0xfd, 0x20, 0xd9, 0x35, 0x5d, 0xb2, 0x68, 0x13, 0x7f, 0x48,
0x6d, 0x8f, 0xb9, 0x14, 0xfd, 0x04, 0x85, 0xd0, 0x37, 0xf0, 0xab, 0x89, 0xa4, 0x9e, 0x3b, 0x34,
0x99, 0x4b, 0x1f, 0xf5, 0xfc, 0x05, 0xf2, 0xc1, 0x7d, 0xc1, 0x22, 0x46, 0xe2, 0x08, 0x17, 0x16,
0x75, 0xfd, 0x70, 0xcd, 0x1e, 0x7d, 0x48, 0x1d, 0x40, 0xd1, 0xde, 0x15, 0x97, 0xb4, 0x28, 0x23,
0xd8, 0xeb, 0x75, 0x71, 0x20, 0xad, 0xac, 0xeb, 0x1e, 0x14, 0x85, 0x5d, 0x89, 0x9e, 0x0b, 0xd4,
0xf5, 0x0d, 0x5d, 0xff, 0xec, 0x31, 0x78, 0xa9, 0x26, 0x2c, 0xc5, 0x94, 0xda, 0xfa, 0x8e, 0x4d,
0xa9, 0xfd, 0xd7, 0x2e, 0xd5, 0xa0, 0x9c, 0x9a, 0xbc, 0xe8, 0xf8, 0x91, 0xc9, 0x9a, 0xc4, 0xd7,
0x78, 0x9c, 0x10, 0x69, 0xfe, 0x06, 0xdb, 0xd1, 0x6c, 0x43, 0x4f, 0x05, 0x72, 0x7a, 0x0e, 0xa7,
0x32, 0xb6, 0x32, 0x0a, 0xd1, 0x15, 0xc0, 0x72, 0xa8, 0xa0, 0xa3, 0x47, 0x66, 0x4d, 0xa8, 0xf3,
0xfc, 0x93, 0x93, 0xe8, 0xe2, 0xfb, 0x3f, 0x4e, 0x47, 0x36, 0xbb, 0x9f, 0x0e, 0x9a, 0x43, 0x77,
0x72, 0x3a, 0x0e, 0xb6, 0xa0, 0x63, 0x3b, 0x23, 0x87, 0xb0, 0x99, 0x4b, 0x1f, 0x4e, 0xc7, 0x8e,
0x75, 0xca, 0x67, 0xe2, 0x69, 0xa2, 0x32, 0xc8, 0xf1, 0x7f, 0x12, 0x5f, 0xfd, 0x1b, 0x00, 0x00,
0xff, 0xff, 0x88, 0x6c, 0x32, 0x8c, 0x6d, 0x0a, 0x00, 0x00,
// 1190 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x56, 0x6d, 0x6f, 0xda, 0xd6,
0x17, 0x6f, 0x42, 0x20, 0x70, 0x78, 0x08, 0xb9, 0xe4, 0x81, 0xd2, 0xf4, 0x9f, 0xfc, 0x3d, 0x6d,
0x8b, 0xb6, 0x96, 0x68, 0xa9, 0x36, 0xad, 0x9d, 0x34, 0x8d, 0x80, 0x23, 0x10, 0x04, 0x33, 0xdb,
0x2d, 0xea, 0xde, 0x5c, 0x19, 0x7c, 0x4b, 0xac, 0x80, 0xed, 0x5e, 0x5f, 0x0a, 0xbc, 0xdb, 0x9b,
0x7d, 0x85, 0x49, 0xfd, 0xb6, 0x93, 0xaf, 0x1f, 0xb8, 0x86, 0xa5, 0xd2, 0x5e, 0x85, 0x7b, 0x7e,
0xbf, 0xf3, 0x7c, 0x7c, 0x4e, 0xe0, 0xe9, 0xc2, 0x98, 0x4e, 0x09, 0xa3, 0xee, 0xf8, 0x2a, 0xf8,
0xf5, 0x60, 0xb1, 0xba, 0x4b, 0x1d, 0xe6, 0xa0, 0x5c, 0x0c, 0xd5, 0x72, 0xd4, 0x1d, 0x07, 0xd2,
0xda, 0x91, 0x67, 0x4d, 0x6c, 0x9f, 0xee, 0xff, 0x25, 0x34, 0x90, 0x4a, 0xbf, 0x43, 0xa6, 0x4b,
0x56, 0x2a, 0xf9, 0x88, 0x2e, 0xa1, 0xfc, 0x40, 0x56, 0xf8, 0x83, 0x65, 0x4f, 0x08, 0xc5, 0x2e,
0xb5, 0x6c, 0x56, 0xdd, 0xb9, 0xd8, 0xb9, 0x4c, 0xab, 0xa5, 0x07, 0xb2, 0xba, 0xe5, 0xe2, 0x81,
0x2f, 0x45, 0xcf, 0x01, 0x38, 0xd3, 0x98, 0x59, 0xd3, 0x55, 0x75, 0x97, 0x73, 0x72, 0x3e, 0x87,
0x0b, 0xa4, 0x22, 0xe4, 0x1b, 0xa6, 0x49, 0x55, 0xf2, 0x71, 0x4e, 0x3c, 0x26, 0x49, 0x50, 0x08,
0x9e, 0x9e, 0xeb, 0xd8, 0x1e, 0x41, 0x08, 0xf6, 0x0c, 0xd3, 0xa4, 0xdc, 0x76, 0x4e, 0xe5, 0xbf,
0xa5, 0x37, 0x90, 0xd7, 0xa9, 0x61, 0x7b, 0xc6, 0x98, 0x59, 0x8e, 0x8d, 0x8e, 0x21, 0xc3, 0x96,
0xf8, 0x9e, 0x2c, 0x39, 0xa9, 0xa0, 0xa6, 0xd9, 0xb2, 0x4d, 0x96, 0xe8, 0x08, 0xd2, 0x53, 0x63,
0x44, 0xa6, 0xdc, 0x65, 0x4e, 0x0d, 0x1e, 0xd2, 0x4f, 0x70, 0x30, 0x98, 0x8f, 0xa6, 0x96, 0x77,
0x1f, 0xbb, 0xf8, 0x0a, 0x8a, 0x6e, 0x20, 0xc2, 0x84, 0x52, 0x27, 0xf2, 0x55, 0x08, 0x85, 0xb2,
0x2f, 0x93, 0x28, 0x20, 0x8d, 0xd8, 0xa6, 0x32, 0x67, 0xee, 0x9c, 0x79, 0x61, 0xb4, 0xe8, 0x0c,
0xc0, 0x33, 0x18, 0x76, 0x09, 0xc5, 0x0f, 0x0b, 0xae, 0x97, 0x52, 0xb3, 0x9e, 0xc1, 0x06, 0x84,
0x76, 0x17, 0xe8, 0x12, 0xf6, 0x9d, 0x80, 0x5f, 0xdd, 0xbd, 0x48, 0x5d, 0xe6, 0xaf, 0x4b, 0xf5,
0xb0, 0xaa, 0x75, 0x7d, 0xa9, 0xcc, 0x99, 0x1a, 0xc1, 0xeb, 0x58, 0x53, 0x62, 0xac, 0x2f, 0xa0,
0x92, 0xf0, 0x19, 0xc6, 0x7b, 0x0c, 0x19, 0x6a, 0x2c, 0x30, 0x8b, 0xf3, 0xa5, 0xc6, 0x42, 0x5f,
0x4a, 0x3f, 0x02, 0x92, 0x3d, 0x66, 0xcd, 0x0c, 0x46, 0x6e, 0x09, 0x89, 0x22, 0x3c, 0x87, 0xfc,
0xd8, 0xb1, 0x3f, 0x60, 0x66, 0xd0, 0x09, 0x89, 0x5a, 0x04, 0xbe, 0x48, 0xe7, 0x12, 0xe9, 0x15,
0x54, 0x12, 0x6a, 0xa1, 0x93, 0x2f, 0x66, 0x26, 0x7d, 0x4e, 0x41, 0x61, 0x40, 0x6c, 0xd3, 0xb2,
0x27, 0xda, 0x82, 0x10, 0x17, 0x7d, 0x0f, 0x59, 0x3f, 0x17, 0x27, 0x1a, 0x83, 0xfc, 0xf5, 0x41,
0x7d, 0xca, 0x33, 0x55, 0xe6, 0x6c, 0xe0, 0x8b, 0xd5, 0x98, 0x80, 0x5e, 0x43, 0x61, 0x61, 0x31,
0x9b, 0x78, 0x1e, 0x66, 0x2b, 0x97, 0xf0, 0x06, 0x95, 0xae, 0x4f, 0xea, 0xf1, 0x20, 0xd6, 0x87,
0x01, 0xac, 0xaf, 0x5c, 0xa2, 0xe6, 0x17, 0xeb, 0x87, 0x3f, 0x4c, 0xc6, 0xcc, 0x99, 0xdb, 0x0c,
0x7b, 0x06, 0xe3, 0xd5, 0x2a, 0xaa, 0xb9, 0x40, 0xa2, 0x19, 0x0c, 0x5d, 0x40, 0x21, 0x8a, 0x7a,
0xb4, 0x62, 0xa4, 0xba, 0xc7, 0x09, 0x10, 0xc4, 0x7d, 0xb3, 0x62, 0x04, 0xbd, 0x04, 0x34, 0xa2,
0x8e, 0x61, 0x8e, 0x0d, 0x8f, 0x61, 0x83, 0x31, 0x32, 0x73, 0x99, 0x57, 0x4d, 0x73, 0xde, 0x61,
0x8c, 0x34, 0x42, 0x00, 0x5d, 0xc3, 0xb1, 0x4d, 0x96, 0x0c, 0xaf, 0x75, 0xee, 0x89, 0x35, 0xb9,
0x67, 0xd5, 0x0c, 0xd7, 0xa8, 0xf8, 0xe0, 0x4d, 0x84, 0xb5, 0x39, 0xe4, 0xeb, 0xd0, 0xa0, 0xfa,
0xc4, 0xc4, 0x62, 0xf1, 0xb3, 0x81, 0x4e, 0x0c, 0x36, 0xe3, 0x2e, 0xa0, 0x57, 0x70, 0xb2, 0xd6,
0x49, 0xa4, 0x90, 0xdb, 0x50, 0xd2, 0xd6, 0xb9, 0x1c, 0x41, 0xfa, 0x83, 0x43, 0xc7, 0xa4, 0xba,
0x7f, 0xb1, 0x73, 0x99, 0x55, 0x83, 0x87, 0x74, 0x02, 0x47, 0x62, 0x6b, 0xa2, 0x59, 0x95, 0x86,
0x70, 0xbc, 0x21, 0x0f, 0x5b, 0xfd, 0x2b, 0x94, 0xdc, 0x00, 0xc0, 0x1e, 0x47, 0xaa, 0x3b, 0x7c,
0x5a, 0x4f, 0x85, 0x86, 0x88, 0x9a, 0x6a, 0xd1, 0x15, 0xed, 0x48, 0x7f, 0xef, 0x40, 0xe9, 0x66,
0x3e, 0x73, 0x85, 0xa9, 0xfb, 0x4f, 0xe3, 0x70, 0x0e, 0xf9, 0xa0, 0x40, 0xbc, 0x58, 0x7c, 0x1a,
0x8a, 0x2a, 0x04, 0x22, 0xbf, 0x44, 0x5b, 0x5d, 0x4d, 0x6d, 0x75, 0x35, 0xae, 0xc4, 0x9e, 0x58,
0x89, 0x43, 0x38, 0x88, 0xe3, 0x0a, 0x72, 0x95, 0x5e, 0xc2, 0x61, 0xcf, 0xf2, 0x58, 0xa2, 0x32,
0xa8, 0x0a, 0xfb, 0x9f, 0x08, 0x1d, 0x39, 0x1e, 0xe1, 0xc1, 0x66, 0xd5, 0xe8, 0x29, 0xfd, 0xb9,
0x0b, 0x48, 0xe4, 0x87, 0x15, 0xeb, 0x41, 0x85, 0xad, 0x17, 0x10, 0x36, 0x09, 0x33, 0xac, 0xa9,
0x17, 0x66, 0xfa, 0x34, 0xcc, 0x54, 0x58, 0x51, 0xad, 0x80, 0xd0, 0x7e, 0xa2, 0x22, 0xb6, 0x25,
0x45, 0x43, 0x38, 0x10, 0xad, 0x59, 0xa6, 0xc7, 0x6b, 0x90, 0xbf, 0x7e, 0x21, 0x34, 0x60, 0x3b,
0x0a, 0xd1, 0x41, 0xa7, 0xe5, 0x1b, 0x2f, 0x09, 0x66, 0x3a, 0xa6, 0x57, 0x7b, 0x0d, 0xa5, 0x24,
0x07, 0x7d, 0xbb, 0xed, 0xca, 0xef, 0x75, 0x6e, 0x53, 0xf5, 0x26, 0x0b, 0x99, 0x60, 0x16, 0xbe,
0xfb, 0x9c, 0x82, 0xbc, 0xf0, 0x39, 0xa2, 0x0a, 0x1c, 0xbc, 0xed, 0x77, 0xfb, 0xca, 0xb0, 0x8f,
0x87, 0x1d, 0xbd, 0x2f, 0x6b, 0x5a, 0xf9, 0x09, 0xaa, 0xc2, 0x51, 0x53, 0xb9, 0xbb, 0xeb, 0xe8,
0x77, 0x72, 0x5f, 0xc7, 0x7a, 0xe7, 0x4e, 0xc6, 0x3d, 0xa5, 0xd9, 0x2d, 0xef, 0xa0, 0x53, 0xa8,
0x08, 0x48, 0x5f, 0xc1, 0x2d, 0xb9, 0xd7, 0x78, 0x5f, 0xde, 0x45, 0xc7, 0x70, 0x28, 0x00, 0xaa,
0xfc, 0x4e, 0xe9, 0xca, 0xe5, 0x94, 0xcf, 0x6f, 0xeb, 0xbd, 0x26, 0x56, 0x6e, 0x6f, 0x65, 0x55,
0x6e, 0x45, 0xc0, 0x9e, 0xef, 0x82, 0x03, 0x8d, 0x66, 0x53, 0x1e, 0xe8, 0x6b, 0x24, 0x8d, 0xbe,
0x86, 0xff, 0x27, 0x54, 0x7c, 0xf7, 0xca, 0x5b, 0x1d, 0x6b, 0x72, 0x53, 0xe9, 0xb7, 0x70, 0x4f,
0x7e, 0x27, 0xf7, 0xca, 0x19, 0xf4, 0x0d, 0x48, 0x49, 0x03, 0xda, 0xdb, 0x66, 0x53, 0xd6, 0xb4,
0x24, 0x6f, 0x1f, 0x9d, 0xc3, 0xb3, 0x8d, 0x08, 0xee, 0x14, 0x5d, 0x8e, 0xac, 0x96, 0xb3, 0xe8,
0x02, 0xce, 0x36, 0x23, 0xe1, 0x8c, 0xd0, 0x5e, 0x39, 0x87, 0xce, 0xa0, 0xca, 0x19, 0xa2, 0xe5,
0x28, 0x5e, 0x40, 0x47, 0x50, 0x0e, 0x2b, 0x87, 0xbb, 0xf2, 0x7b, 0xdc, 0x6e, 0x68, 0xed, 0x72,
0x1e, 0x3d, 0x83, 0xd3, 0xbe, 0xac, 0xf9, 0xe6, 0xb6, 0xc0, 0xc2, 0x46, 0xb1, 0x1a, 0xfd, 0x66,
0x5b, 0x51, 0xcb, 0xc5, 0xeb, 0xbf, 0xd2, 0x90, 0x1b, 0xf2, 0x11, 0xe9, 0x5a, 0x0c, 0xbd, 0x81,
0x62, 0x8b, 0x50, 0xeb, 0x13, 0xe9, 0x93, 0x25, 0xeb, 0x92, 0x15, 0x3a, 0x14, 0xe6, 0x27, 0x38,
0xdb, 0xb5, 0x93, 0xf8, 0x02, 0x75, 0xc9, 0xaa, 0x45, 0xbc, 0x31, 0xb5, 0x5c, 0xe6, 0x50, 0xf4,
0x33, 0xe4, 0x02, 0x5d, 0x5f, 0xaf, 0x22, 0x92, 0x7a, 0xce, 0xd8, 0x60, 0x0e, 0x7d, 0x54, 0xf3,
0x17, 0xc8, 0xfa, 0xfe, 0xfc, 0xa3, 0x8d, 0xc4, 0x15, 0x2e, 0x1c, 0xf5, 0xda, 0xe9, 0x96, 0x3c,
0xfc, 0x90, 0xda, 0x80, 0xc2, 0x6b, 0x2c, 0x1e, 0x74, 0xd1, 0x8c, 0x20, 0xaf, 0xd5, 0xc4, 0x85,
0xb4, 0x71, 0xc4, 0x7b, 0x90, 0x17, 0x6e, 0x25, 0x7a, 0x2e, 0x50, 0xb7, 0xef, 0x76, 0xed, 0x7f,
0x8f, 0xc1, 0x6b, 0x6b, 0xc2, 0x51, 0x4c, 0x58, 0xdb, 0xbe, 0xb1, 0x09, 0x6b, 0xff, 0x76, 0x4b,
0x55, 0x28, 0x26, 0x36, 0x2f, 0x3a, 0x7f, 0x64, 0xb3, 0xc6, 0xf1, 0x5d, 0x3c, 0x4e, 0x08, 0x6d,
0xfe, 0x06, 0xfb, 0xe1, 0x6e, 0x43, 0x4f, 0x05, 0x72, 0x72, 0x0f, 0x27, 0x2a, 0xb6, 0xb1, 0x0a,
0x51, 0x07, 0x60, 0xbd, 0x54, 0xd0, 0xd9, 0x23, 0xbb, 0x26, 0xb0, 0xf3, 0xfc, 0x8b, 0x9b, 0xe8,
0xe6, 0x87, 0x3f, 0xae, 0x26, 0x16, 0xbb, 0x9f, 0x8f, 0xea, 0x63, 0x67, 0x76, 0x35, 0xf5, 0xaf,
0xa0, 0x6d, 0xd9, 0x13, 0x9b, 0xb0, 0x85, 0x43, 0x1f, 0xae, 0xa6, 0xb6, 0x79, 0xc5, 0x77, 0xe2,
0x55, 0x6c, 0x65, 0x94, 0xe1, 0xff, 0x50, 0xbe, 0xfa, 0x27, 0x00, 0x00, 0xff, 0xff, 0xde, 0x94,
0x5b, 0x29, 0x99, 0x0a, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.

View file

@ -129,6 +129,11 @@ message Transaction {
The raw serialized transaction.
*/
bytes tx_hex = 1;
/*
An optional label to save with the transaction. Limited to 500 characters.
*/
string label = 2;
}
message PublishResponse {
/*
@ -152,6 +157,9 @@ message SendOutputsRequest {
A slice of the outputs that should be created in the transaction produced.
*/
repeated signrpc.TxOut outputs = 2;
// An optional label for the transaction, limited to 500 characters.
string label = 3;
}
message SendOutputsResponse {
/*

View file

@ -93,6 +93,10 @@
"raw_tx_hex": {
"type": "string",
"description": "The raw transaction hex."
},
"label": {
"type": "string",
"description": "A label that was optionally set on transaction broadcast."
}
}
},

View file

@ -16,6 +16,7 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/labels"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/signrpc"
"github.com/lightningnetwork/lnd/lnwallet"
@ -273,7 +274,12 @@ func (w *WalletKit) PublishTransaction(ctx context.Context,
return nil, err
}
err := w.cfg.Wallet.PublishTransaction(tx)
label, err := labels.ValidateAPI(req.Label)
if err != nil {
return nil, err
}
err = w.cfg.Wallet.PublishTransaction(tx, label)
if err != nil {
return nil, err
}
@ -306,10 +312,15 @@ func (w *WalletKit) SendOutputs(ctx context.Context,
})
}
label, err := labels.ValidateAPI(req.Label)
if err != nil {
return nil, err
}
// Now that we have the outputs mapped, we can request that the wallet
// attempt to create this transaction.
tx, err := w.cfg.Wallet.SendOutputs(
outputsToCreate, chainfee.SatPerKWeight(req.SatPerKw),
outputsToCreate, chainfee.SatPerKWeight(req.SatPerKw), label,
)
if err != nil {
return nil, err

View file

@ -12486,9 +12486,13 @@ func testSweepAllCoins(net *lntest.NetworkHarness, t *harnessTest) {
t.Fatalf("unable to get node info: %v", err)
}
// Create a label that we will used to label the transaction with.
sendCoinsLabel := "send all coins"
sweepReq := &lnrpc.SendCoinsRequest{
Addr: info.IdentityPubkey,
SendAll: true,
Label: sendCoinsLabel,
}
_, err = ainz.SendCoins(ctxt, sweepReq)
if err == nil {
@ -12504,6 +12508,7 @@ func testSweepAllCoins(net *lntest.NetworkHarness, t *harnessTest) {
sweepReq = &lnrpc.SendCoinsRequest{
Addr: info.IdentityPubkey,
SendAll: true,
Label: sendCoinsLabel,
}
_, err = ainz.SendCoins(ctxt, sweepReq)
if err == nil {
@ -12522,6 +12527,7 @@ func testSweepAllCoins(net *lntest.NetworkHarness, t *harnessTest) {
sweepReq = &lnrpc.SendCoinsRequest{
Addr: "tb1qfc8fusa98jx8uvnhzavxccqlzvg749tvjw82tg",
SendAll: true,
Label: sendCoinsLabel,
}
_, err = ainz.SendCoins(ctxt, sweepReq)
if err == nil {
@ -12533,6 +12539,7 @@ func testSweepAllCoins(net *lntest.NetworkHarness, t *harnessTest) {
sweepReq = &lnrpc.SendCoinsRequest{
Addr: "1MPaXKp5HhsLNjVSqaL7fChE3TVyrTMRT3",
SendAll: true,
Label: sendCoinsLabel,
}
_, err = ainz.SendCoins(ctxt, sweepReq)
if err == nil {
@ -12548,6 +12555,7 @@ func testSweepAllCoins(net *lntest.NetworkHarness, t *harnessTest) {
sweepReq = &lnrpc.SendCoinsRequest{
Addr: minerAddr.String(),
SendAll: true,
Label: sendCoinsLabel,
}
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
_, err = ainz.SendCoins(ctxt, sweepReq)
@ -12566,6 +12574,24 @@ func testSweepAllCoins(net *lntest.NetworkHarness, t *harnessTest) {
t.Fatalf("expected 2 inputs instead have %v", len(sweepTx.TxIn))
}
// List all transactions relevant to our wallet, and find the sweep tx
// so that we can check the correct label has been set.
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
txResp, err := ainz.GetTransactions(ctxt, &lnrpc.GetTransactionsRequest{})
if err != nil {
t.Fatalf("could not get transactions: %v", err)
}
sweepTxStr := sweepTx.TxHash().String()
for _, txn := range txResp.Transactions {
if txn.TxHash == sweepTxStr {
if txn.Label != sendCoinsLabel {
t.Fatalf("expected label: %v, got: %v",
sendCoinsLabel, txn.Label)
}
}
}
// Finally, Ainz should now have no coins at all within his wallet.
balReq := &lnrpc.WalletBalanceRequest{}
resp, err := ainz.WalletBalance(ctxt, balReq)

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
@ -433,8 +436,8 @@ func (b *BtcWallet) ListUnspentWitness(minConfs, maxConfs int32) (
// publishing the transaction fails, an error describing the reason is returned
// (currently ErrDoubleSpend). If the transaction is already published to the
// network (either in the mempool or chain) no error will be returned.
func (b *BtcWallet) PublishTransaction(tx *wire.MsgTx) error {
if err := b.wallet.PublishTransaction(tx, ""); err != nil {
func (b *BtcWallet) PublishTransaction(tx *wire.MsgTx, label string) error {
if err := b.wallet.PublishTransaction(tx, label); err != nil {
// If we failed to publish the transaction, check whether we
// got an error of known type.
@ -516,6 +519,7 @@ func minedTransactionsToDetails(
TotalFees: int64(tx.Fee),
DestAddresses: destAddresses,
RawTx: tx.Transaction,
Label: tx.Label,
}
balanceDelta, err := extractBalanceDelta(tx, wireTx)
@ -561,6 +565,7 @@ func unminedTransactionsToDetail(
Timestamp: summary.Timestamp,
DestAddresses: destAddresses,
RawTx: summary.Transaction,
Label: summary.Label,
}
balanceDelta, err := extractBalanceDelta(summary, wireTx)

View file

@ -103,6 +103,9 @@ type TransactionDetail struct {
// RawTx returns the raw serialized transaction.
RawTx []byte
// Label is an optional transaction label.
Label string
}
// TransactionSubscription is an interface which describes an object capable of
@ -174,7 +177,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
@ -224,8 +227,9 @@ type WalletController interface {
// already known transaction, ErrDoubleSpend is returned. If the
// transaction is already known (published already), no error will be
// returned. Other error returned depends on the currently active chain
// backend.
PublishTransaction(tx *wire.MsgTx) error
// backend. It takes an optional label which will save a label with the
// published transaction.
PublishTransaction(tx *wire.MsgTx, label string) error
// SubscribeTransactions returns a TransactionSubscription client which
// is capable of receiving async notifications as new transactions

View file

@ -38,6 +38,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb/kvdb"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/labels"
"github.com/lightningnetwork/lnd/lntest/wait"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/btcwallet"
@ -173,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)
}
@ -529,7 +532,8 @@ func testDualFundingReservationWorkflow(miner *rpctest.Harness,
}
// Let Alice publish the funding transaction.
if err := alice.PublishTransaction(fundingTx); err != nil {
err = alice.PublishTransaction(fundingTx, "")
if err != nil {
t.Fatalf("unable to publish funding tx: %v", err)
}
@ -1024,7 +1028,8 @@ func testSingleFunderReservationWorkflow(miner *rpctest.Harness,
}
// Let Alice publish the funding transaction.
if err := alice.PublishTransaction(fundingTx); err != nil {
err = alice.PublishTransaction(fundingTx, "")
if err != nil {
t.Fatalf("unable to publish funding tx: %v", err)
}
@ -1217,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)
}
@ -1490,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)
}
@ -1681,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)
}
@ -1721,7 +1732,8 @@ func testPublishTransaction(r *rpctest.Harness,
tx1 := newTx(t, r, keyDesc.PubKey, alice, false)
// Publish the transaction.
if err := alice.PublishTransaction(tx1); err != nil {
err = alice.PublishTransaction(tx1, labels.External)
if err != nil {
t.Fatalf("unable to publish: %v", err)
}
@ -1733,7 +1745,8 @@ func testPublishTransaction(r *rpctest.Harness,
// Publish the exact same transaction again. This should not return an
// error, even though the transaction is already in the mempool.
if err := alice.PublishTransaction(tx1); err != nil {
err = alice.PublishTransaction(tx1, labels.External)
if err != nil {
t.Fatalf("unable to publish: %v", err)
}
@ -1752,7 +1765,8 @@ func testPublishTransaction(r *rpctest.Harness,
tx2 := newTx(t, r, keyDesc.PubKey, alice, false)
// Publish this tx.
if err := alice.PublishTransaction(tx2); err != nil {
err = alice.PublishTransaction(tx2, labels.External)
if err != nil {
t.Fatalf("unable to publish: %v", err)
}
@ -1763,7 +1777,8 @@ func testPublishTransaction(r *rpctest.Harness,
// Publish the transaction again. It is already mined, and we don't
// expect this to return an error.
if err := alice.PublishTransaction(tx2); err != nil {
err = alice.PublishTransaction(tx2, labels.External)
if err != nil {
t.Fatalf("unable to publish: %v", err)
}
@ -1779,7 +1794,8 @@ func testPublishTransaction(r *rpctest.Harness,
// transaction. Create a new tx and publish it. This is the
// output we'll try to double spend.
tx3 = newTx(t, r, keyDesc.PubKey, alice, false)
if err := alice.PublishTransaction(tx3); err != nil {
err := alice.PublishTransaction(tx3, labels.External)
if err != nil {
t.Fatalf("unable to publish: %v", err)
}
@ -1799,7 +1815,8 @@ func testPublishTransaction(r *rpctest.Harness,
}
// This should be accepted into the mempool.
if err := alice.PublishTransaction(tx4); err != nil {
err = alice.PublishTransaction(tx4, labels.External)
if err != nil {
t.Fatalf("unable to publish: %v", err)
}
@ -1833,7 +1850,7 @@ func testPublishTransaction(r *rpctest.Harness,
t.Fatal(err)
}
err = alice.PublishTransaction(tx5)
err = alice.PublishTransaction(tx5, labels.External)
if err != lnwallet.ErrDoubleSpend {
t.Fatalf("expected ErrDoubleSpend, got: %v", err)
}
@ -1861,7 +1878,7 @@ func testPublishTransaction(r *rpctest.Harness,
expErr = nil
tx3Spend = tx6
}
err = alice.PublishTransaction(tx6)
err = alice.PublishTransaction(tx6, labels.External)
if err != expErr {
t.Fatalf("expected ErrDoubleSpend, got: %v", err)
}
@ -1896,7 +1913,7 @@ func testPublishTransaction(r *rpctest.Harness,
}
// Expect rejection.
err = alice.PublishTransaction(tx7)
err = alice.PublishTransaction(tx7, labels.External)
if err != lnwallet.ErrDoubleSpend {
t.Fatalf("expected ErrDoubleSpend, got: %v", err)
}
@ -1964,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)
}
@ -2086,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)
}
@ -2467,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
}
@ -322,7 +322,7 @@ func (*mockWalletController) ListTransactionDetails(_, _ int32) ([]*lnwallet.Tra
}
func (*mockWalletController) LockOutpoint(o wire.OutPoint) {}
func (*mockWalletController) UnlockOutpoint(o wire.OutPoint) {}
func (m *mockWalletController) PublishTransaction(tx *wire.MsgTx) error {
func (m *mockWalletController) PublishTransaction(tx *wire.MsgTx, _ string) error {
m.publishedTransactions <- tx
return 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,7 +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()
@ -1205,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
}
@ -1242,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))
@ -1253,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

View file

@ -75,7 +75,7 @@ func (b *mockBackend) publishTransaction(tx *wire.MsgTx) error {
return nil
}
func (b *mockBackend) PublishTransaction(tx *wire.MsgTx) error {
func (b *mockBackend) PublishTransaction(tx *wire.MsgTx, _ string) error {
log.Tracef("Publishing tx %v", tx.TxHash())
err := b.publishTransaction(tx)
select {

View file

@ -9,7 +9,7 @@ import (
type Wallet interface {
// PublishTransaction performs cursory validation (dust checks, etc) and
// broadcasts the passed transaction to the Bitcoin network.
PublishTransaction(tx *wire.MsgTx) error
PublishTransaction(tx *wire.MsgTx, label string) error
// ListUnspentWitness returns all unspent outputs which are version 0
// witness programs. The 'minconfirms' and 'maxconfirms' parameters

View file

@ -355,7 +355,7 @@ func (s *UtxoSweeper) Start() error {
// Error can be ignored. Because we are starting up, there are
// no pending inputs to update based on the publish result.
err := s.cfg.Wallet.PublishTransaction(lastTx)
err := s.cfg.Wallet.PublishTransaction(lastTx, "")
if err != nil && err != lnwallet.ErrDoubleSpend {
log.Errorf("last tx publish: %v", err)
}
@ -988,7 +988,7 @@ func (s *UtxoSweeper) sweep(inputs inputSet, feeRate chainfee.SatPerKWeight,
}),
)
err = s.cfg.Wallet.PublishTransaction(tx)
err = s.cfg.Wallet.PublishTransaction(tx, "")
// In case of an unexpected error, don't try to recover.
if err != nil && err != lnwallet.ErrDoubleSpend {

View file

@ -194,7 +194,7 @@ type NurseryConfig struct {
// PublishTransaction facilitates the process of broadcasting a signed
// transaction to the appropriate network.
PublishTransaction func(*wire.MsgTx) error
PublishTransaction func(*wire.MsgTx, string) error
// Store provides access to and modification of the persistent state
// maintained about the utxo nursery's incubating outputs.
@ -867,7 +867,7 @@ func (u *utxoNursery) sweepCribOutput(classHeight uint32, baby *babyOutput) erro
// We'll now broadcast the HTLC transaction, then wait for it to be
// confirmed before transitioning it to kindergarten.
err := u.cfg.PublishTransaction(baby.timeoutTx)
err := u.cfg.PublishTransaction(baby.timeoutTx, "")
if err != nil && err != lnwallet.ErrDoubleSpend {
utxnLog.Errorf("Unable to broadcast baby tx: "+
"%v, %v", err, spew.Sdump(baby.timeoutTx))

View file

@ -467,7 +467,7 @@ func createNurseryTestContext(t *testing.T,
Store: storeIntercepter,
ChainIO: chainIO,
SweepInput: sweeper.sweepInput,
PublishTransaction: func(tx *wire.MsgTx) error {
PublishTransaction: func(tx *wire.MsgTx, _ string) error {
return publishFunc(tx, "nursery")
},
}

View file

@ -72,7 +72,7 @@ type Config struct {
//
// TODO(conner): replace with lnwallet.WalletController interface to
// have stronger guarantees wrt. returned error types.
PublishTx func(*wire.MsgTx) error
PublishTx func(*wire.MsgTx, string) error
// ListenAddrs specifies the listening addresses of the tower.
ListenAddrs []net.Addr

View file

@ -293,7 +293,7 @@ func testJusticeDescriptor(t *testing.T, blobType blob.Type) {
// over the buffered channel.
publications := make(chan *wire.MsgTx, 1)
punisher := lookout.NewBreachPunisher(&lookout.PunisherConfig{
PublishTx: func(tx *wire.MsgTx) error {
PublishTx: func(tx *wire.MsgTx, _ string) error {
publications <- tx
return nil
},

View file

@ -8,7 +8,7 @@ import (
type PunisherConfig struct {
// PublishTx provides the ability to send a signed transaction to the
// network.
PublishTx func(*wire.MsgTx) error
PublishTx func(*wire.MsgTx, string) error
// TODO(conner) add DB tracking and spend ntfn registration to see if
// ours confirmed or not
@ -42,7 +42,7 @@ func (p *BreachPunisher) Punish(desc *JusticeDescriptor, quit <-chan struct{}) e
log.Infof("Publishing justice transaction for client=%s with txid=%s",
desc.SessionInfo.ID, justiceTxn.TxHash())
err = p.cfg.PublishTx(justiceTxn)
err = p.cfg.PublishTx(justiceTxn, "")
if err != nil {
log.Errorf("Unable to publish justice txn for client=%s"+
"with breach-txid=%s: %v",