Skip to content

Commit

Permalink
multi: add labels to lnd native transactions
Browse files Browse the repository at this point in the history
Follow up labelling of external transactions with labels for the
transaction types we create within lnd. Since these labels will live
a life of string matching, a version number and rigid format is added
so that string matching is less painful. We start out with channel ID,
where available, and a transaction "type". External labels, added in a
previous PR, are not updated to this new versioned label because they
are not lnd-initiated transactions. Label matching can check this case,
then check for a version number.
  • Loading branch information
carlaKC committed Jul 29, 2020
1 parent a39c91f commit 2a614cc
Show file tree
Hide file tree
Showing 13 changed files with 225 additions and 20 deletions.
4 changes: 3 additions & 1 deletion breacharbiter.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb/kvdb"
"github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/labels"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
)
Expand Down Expand Up @@ -566,7 +567,8 @@ 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, "")
label := labels.MakeLabel(labels.LabelTypeJusticeTransaction, nil)
err = b.cfg.PublishTransaction(finalTx, label)
if err != nil {
brarLog.Errorf("Unable to broadcast justice tx: %v", err)

Expand Down
6 changes: 5 additions & 1 deletion contractcourt/chain_arbitrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb/kvdb"
"github.com/lightningnetwork/lnd/clock"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/labels"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire"
Expand Down Expand Up @@ -715,7 +716,10 @@ 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, "")
label := labels.MakeLabel(
labels.LabelTypeChannelClose, &channel.ShortChannelID,
)
err = c.cfg.PublishTx(closeTx, label)
if err != nil && err != lnwallet.ErrDoubleSpend {
log.Warnf("Unable to broadcast %s close tx(%v): %v",
kind, closeTx.TxHash(), err)
Expand Down
7 changes: 6 additions & 1 deletion contractcourt/channel_arbitrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/channeldb/kvdb"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/labels"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire"
Expand Down Expand Up @@ -874,7 +875,11 @@ func (c *ChannelArbitrator) stateStep(

// At this point, we'll now broadcast the commitment
// transaction itself.
if err := c.cfg.PublishTx(closeTx, ""); err != nil {
label := labels.MakeLabel(
labels.LabelTypeChannelClose, &c.cfg.ShortChanID,
)

if err := c.cfg.PublishTx(closeTx, label); err != nil {
log.Errorf("ChannelArbitrator(%v): unable to broadcast "+
"close tx: %v", c.cfg.ChanPoint, err)
if err != lnwallet.ErrDoubleSpend {
Expand Down
11 changes: 9 additions & 2 deletions contractcourt/htlc_success_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/labels"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/sweep"
)
Expand Down Expand Up @@ -157,7 +158,10 @@ 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, "")
label := labels.MakeLabel(
labels.LabelTypeChannelClose, &h.ShortChanID,
)
err := h.PublishTx(h.sweepTx, label)
if err != nil {
log.Infof("%T(%x): unable to publish tx: %v",
h, h.htlc.RHash[:], err)
Expand Down Expand Up @@ -206,7 +210,10 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) {
// the claiming process.
//
// TODO(roasbeef): after changing sighashes send to tx bundler
err := h.PublishTx(h.htlcResolution.SignedSuccessTx, "")
label := labels.MakeLabel(
labels.LabelTypeChannelClose, &h.ShortChanID,
)
err := h.PublishTx(h.htlcResolution.SignedSuccessTx, label)
if err != nil {
return nil, err
}
Expand Down
41 changes: 39 additions & 2 deletions fundingmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/labels"
"github.com/lightningnetwork/lnd/lnpeer"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnwallet"
Expand Down Expand Up @@ -243,6 +244,10 @@ type fundingConfig struct {
// transaction to the network.
PublishTransaction func(*wire.MsgTx, string) error

// UpdateLabel updates the label that a transaction has in our wallet,
// overwriting any existing labels.
UpdateLabel func(chainhash.Hash, string) error

// FeeEstimator calculates appropriate fee rates based on historical
// transaction information.
FeeEstimator chainfee.Estimator
Expand Down Expand Up @@ -576,8 +581,15 @@ func (f *fundingManager) start() error {
channel.FundingOutpoint,
fundingTxBuf.Bytes())

// Set a nil short channel ID at this stage
// because we do not know it until our funding
// tx confirms.
label := labels.MakeLabel(
labels.LabelTypeChannelOpen, nil,
)

err = f.cfg.PublishTransaction(
channel.FundingTxn, "",
channel.FundingTxn, label,
)
if err != nil {
fndgLog.Errorf("Unable to rebroadcast "+
Expand Down Expand Up @@ -2032,7 +2044,13 @@ func (f *fundingManager) handleFundingSigned(fmsg *fundingSignedMsg) {
fndgLog.Infof("Broadcasting funding tx for ChannelPoint(%v): %x",
completeChan.FundingOutpoint, fundingTxBuf.Bytes())

err = f.cfg.PublishTransaction(fundingTx, "")
// Set a nil short channel ID at this stage because we do not
// know it until our funding tx confirms.
label := labels.MakeLabel(
labels.LabelTypeChannelOpen, nil,
)

err = f.cfg.PublishTransaction(fundingTx, label)
if err != nil {
fndgLog.Errorf("Unable to broadcast funding tx %x for "+
"ChannelPoint(%v): %v", fundingTxBuf.Bytes(),
Expand Down Expand Up @@ -2372,6 +2390,25 @@ func (f *fundingManager) handleFundingConfirmation(
fndgLog.Errorf("unable to report short chan id: %v", err)
}

// If we opened the channel, and lnd's wallet published our funding tx
// (which is not the case for some channels) then we update our
// transaction label with our short channel ID, which is known now that
// our funding transaction has confirmed. We do not label transactions
// we did not publish, because our wallet has no knowledge of them.
if completeChan.IsInitiator && completeChan.ChanType.HasFundingTx() {
shortChanID := completeChan.ShortChanID()
label := labels.MakeLabel(
labels.LabelTypeChannelOpen, &shortChanID,
)

err = f.cfg.UpdateLabel(
completeChan.FundingOutpoint.Hash, label,
)
if err != nil {
fndgLog.Errorf("unable to update label: %v", err)
}
}

// Close the discoverySignal channel, indicating to a separate
// goroutine that the channel now is marked as open in the database
// and that it is acceptable to process funding locked messages
Expand Down
6 changes: 6 additions & 0 deletions fundingmanager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,9 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey,
publTxChan <- txn
return nil
},
UpdateLabel: func(chainhash.Hash, string) error {
return nil
},
ZombieSweeperInterval: 1 * time.Hour,
ReservationTimeout: 1 * time.Nanosecond,
MaxPendingChannels: lncfg.DefaultMaxPendingChannels,
Expand Down Expand Up @@ -524,6 +527,9 @@ func recreateAliceFundingManager(t *testing.T, alice *testNode) {
publishChan <- txn
return nil
},
UpdateLabel: func(chainhash.Hash, string) error {
return nil
},
ZombieSweeperInterval: oldCfg.ZombieSweeperInterval,
ReservationTimeout: oldCfg.ReservationTimeout,
OpenChannelPredicate: chainedAcceptor,
Expand Down
59 changes: 59 additions & 0 deletions labels/labels.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
// 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.
//
// Labels for transactions broadcast by lnd have two set fields followed by an
// optional set labelled data values, all separated by colons.
// - Label version: an integer that indicates the version lnd used
// - Label type: the type of transaction we are labelling
// - {field name}-{value}: a named field followed by its value, these items are
// optional, and there may be more than field present.
//
// For version 0 we have the following optional data fields defined:
// - shortchanid: the short channel ID that a transaction is associated with,
// with its value set to the uint64 short channel id.
package labels

import (
"fmt"

"github.com/btcsuite/btcwallet/wtxmgr"
"github.com/lightningnetwork/lnd/lnwire"
)

// External labels a transaction as user initiated via the api. This
Expand All @@ -31,3 +43,50 @@ func ValidateAPI(label string) (string, error) {

return label, nil
}

// LabelVersion versions our labels so they can be easily update to contain
// new data while still easily string matched.
type LabelVersion uint8

// LabelVersionZero is the label version for labels that contain label type and
// channel ID (where available).
const LabelVersionZero LabelVersion = iota

// LabelType indicates the type of label we are creating. It is a string rather
// than an int for easy string matching and human-readability.
type LabelType string

const (
// LabelTypeChannelOpen is used to label channel opens.
LabelTypeChannelOpen LabelType = "openchannel"

// LabelTypeChannelClose is used to label channel closes.
LabelTypeChannelClose LabelType = "closechannel"

// LabelTypeJusticeTransaction is used to label justice transactions.
LabelTypeJusticeTransaction LabelType = "justicetx"

// LabelTypeSweepTransaction is used to label sweeps.
LabelTypeSweepTransaction LabelType = "sweep"
)

// LabelField is used to tag a value within a label.
type LabelField string

const (
// ShortChanID is used to tag short channel id values in our labels.
ShortChanID LabelField = "shortchanid"
)

// MakeLabel creates a label with the provided type and short channel id. If
// our short channel ID is not known, we simply return version:label_type. If
// we do have a short channel ID set, the label will also contain its value:
// shortchanid-{int64 chan ID}.
func MakeLabel(labelType LabelType, channelID *lnwire.ShortChannelID) string {
if channelID == nil {
return fmt.Sprintf("%v:%v", LabelVersionZero, labelType)
}

return fmt.Sprintf("%v:%v:%v-%v", LabelVersionZero, labelType,
ShortChanID, channelID.ToUint64())
}
25 changes: 19 additions & 6 deletions lntest/harness.go
Original file line number Diff line number Diff line change
Expand Up @@ -1204,9 +1204,14 @@ func (n *NetworkHarness) WaitForChannelClose(ctx context.Context,
}

// AssertChannelExists asserts that an active channel identified by the
// specified channel point exists from the point-of-view of the node.
// specified channel point exists from the point-of-view of the node. It takes
// an optional set of check functions which can be used to make further
// assertions using channel's values. These functions are responsible for
// failing the test themselves if they do not pass.
// nolint: interfacer
func (n *NetworkHarness) AssertChannelExists(ctx context.Context,
node *HarnessNode, chanPoint *wire.OutPoint) error {
node *HarnessNode, chanPoint *wire.OutPoint,
checks ...func(*lnrpc.Channel)) error {

req := &lnrpc.ListChannelsRequest{}

Expand All @@ -1218,12 +1223,20 @@ func (n *NetworkHarness) AssertChannelExists(ctx context.Context,

for _, channel := range resp.Channels {
if channel.ChannelPoint == chanPoint.String() {
if channel.Active {
return nil
// First check whether our channel is active,
// failing early if it is not.
if !channel.Active {
return fmt.Errorf("channel %s inactive",
chanPoint)
}

return fmt.Errorf("channel %s inactive",
chanPoint)
// Apply any additional checks that we would
// like to verify.
for _, check := range checks {
check(channel)
}

return nil
}
}

Expand Down
Loading

0 comments on commit 2a614cc

Please sign in to comment.