github.com/decred/dcrlnd@v0.7.6/chanrestore.go (about)

     1  package dcrlnd
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"net"
     7  
     8  	"github.com/decred/dcrd/chaincfg/chainhash"
     9  	"github.com/decred/dcrd/chaincfg/v3"
    10  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
    11  	"github.com/decred/dcrlnd/chanbackup"
    12  	"github.com/decred/dcrlnd/channeldb"
    13  	"github.com/decred/dcrlnd/contractcourt"
    14  	"github.com/decred/dcrlnd/keychain"
    15  	"github.com/decred/dcrlnd/lnwire"
    16  	"github.com/decred/dcrlnd/shachain"
    17  )
    18  
    19  const (
    20  	// mainnetSCBLaunchBlock is the approximate block height of the bitcoin
    21  	// mainnet chain of the date when SCBs first were released in lnd
    22  	// (v0.6.0-beta). The block date is 4/15/2019, 10:54 PM UTC.
    23  	mainnetSCBLaunchBlock = 571800
    24  
    25  	// testnetSCBLaunchBlock is the approximate block height of the bitcoin
    26  	// testnet3 chain of the date when SCBs first were released in lnd
    27  	// (v0.6.0-beta). The block date is 4/16/2019, 08:04 AM UTC.
    28  	testnetSCBLaunchBlock = 1489300
    29  )
    30  
    31  // chanDBRestorer is an implementation of the chanbackup.ChannelRestorer
    32  // interface that is able to properly map a Single backup, into a
    33  // channeldb.ChannelShell which is required to fully restore a channel. We also
    34  // need the secret key chain in order obtain the prior shachain root so we can
    35  // verify the DLP protocol as initiated by the remote node.
    36  type chanDBRestorer struct {
    37  	db *channeldb.ChannelStateDB
    38  
    39  	secretKeys keychain.SecretKeyRing
    40  
    41  	chainArb *contractcourt.ChainArbitrator
    42  }
    43  
    44  // openChannelShell maps the static channel back up into an open channel
    45  // "shell". We say shell as this doesn't include all the information required
    46  // to continue to use the channel, only the minimal amount of information to
    47  // insert this shell channel back into the database.
    48  func (c *chanDBRestorer) openChannelShell(backup chanbackup.Single) (
    49  	*channeldb.ChannelShell, error) {
    50  
    51  	var err error
    52  
    53  	// Each of the keys in our local channel config only have their
    54  	// locators populate, so we'll re-derive the raw key now as we'll need
    55  	// it in order to carry out the DLP protocol.
    56  	backup.LocalChanCfg.MultiSigKey, err = c.secretKeys.DeriveKey(
    57  		backup.LocalChanCfg.MultiSigKey.KeyLocator,
    58  	)
    59  	if err != nil {
    60  		return nil, fmt.Errorf("unable to derive multi sig key: %v", err)
    61  	}
    62  	backup.LocalChanCfg.RevocationBasePoint, err = c.secretKeys.DeriveKey(
    63  		backup.LocalChanCfg.RevocationBasePoint.KeyLocator,
    64  	)
    65  	if err != nil {
    66  		return nil, fmt.Errorf("unable to derive revocation key: %v", err)
    67  	}
    68  	backup.LocalChanCfg.PaymentBasePoint, err = c.secretKeys.DeriveKey(
    69  		backup.LocalChanCfg.PaymentBasePoint.KeyLocator,
    70  	)
    71  	if err != nil {
    72  		return nil, fmt.Errorf("unable to derive payment key: %v", err)
    73  	}
    74  	backup.LocalChanCfg.DelayBasePoint, err = c.secretKeys.DeriveKey(
    75  		backup.LocalChanCfg.DelayBasePoint.KeyLocator,
    76  	)
    77  	if err != nil {
    78  		return nil, fmt.Errorf("unable to derive delay key: %v", err)
    79  	}
    80  	backup.LocalChanCfg.HtlcBasePoint, err = c.secretKeys.DeriveKey(
    81  		backup.LocalChanCfg.HtlcBasePoint.KeyLocator,
    82  	)
    83  	if err != nil {
    84  		return nil, fmt.Errorf("unable to derive htlc key: %v", err)
    85  	}
    86  
    87  	// The shachain root that seeds RevocationProducer for this channel.
    88  	// It currently has two possible formats.
    89  	var revRoot *chainhash.Hash
    90  
    91  	// If the PubKey field is non-nil, then this shachain root is using the
    92  	// legacy non-ECDH scheme.
    93  	if backup.ShaChainRootDesc.PubKey != nil {
    94  		ltndLog.Debugf("Using legacy revocation producer format for "+
    95  			"channel point %v", backup.FundingOutpoint)
    96  
    97  		// Obtain the private key for the shachain root from the
    98  		// encoded public key.
    99  		privKey, err := c.secretKeys.DerivePrivKey(
   100  			backup.ShaChainRootDesc,
   101  		)
   102  		if err != nil {
   103  			return nil, fmt.Errorf("could not derive private key "+
   104  				"for legacy channel revocation root format: "+
   105  				"%v", err)
   106  		}
   107  
   108  		revRoot, err = chainhash.NewHash(privKey.Serialize())
   109  		if err != nil {
   110  			return nil, err
   111  		}
   112  	} else {
   113  		ltndLog.Debugf("Using new ECDH revocation producer format "+
   114  			"for channel point %v", backup.FundingOutpoint)
   115  
   116  		// This is the scheme in which the shachain root is derived via
   117  		// an ECDH operation on the private key of ShaChainRootDesc and
   118  		// our public multisig key.
   119  		ecdh, err := c.secretKeys.ECDH(
   120  			backup.ShaChainRootDesc,
   121  			backup.LocalChanCfg.MultiSigKey.PubKey,
   122  		)
   123  		if err != nil {
   124  			return nil, fmt.Errorf("unable to derive shachain "+
   125  				"root: %v", err)
   126  		}
   127  
   128  		ch := chainhash.Hash(ecdh)
   129  		revRoot = &ch
   130  	}
   131  
   132  	shaChainProducer := shachain.NewRevocationProducer(shachain.ShaHash(*revRoot))
   133  
   134  	var chanType channeldb.ChannelType
   135  	switch backup.Version {
   136  
   137  	case chanbackup.DefaultSingleVersion:
   138  		chanType = channeldb.SingleFunderBit
   139  
   140  	case chanbackup.TweaklessCommitVersion:
   141  		chanType = channeldb.SingleFunderTweaklessBit
   142  
   143  	case chanbackup.AnchorsCommitVersion:
   144  		chanType = channeldb.AnchorOutputsBit
   145  		chanType |= channeldb.SingleFunderTweaklessBit
   146  
   147  	case chanbackup.AnchorsZeroFeeHtlcTxCommitVersion:
   148  		chanType = channeldb.ZeroHtlcTxFeeBit
   149  		chanType |= channeldb.AnchorOutputsBit
   150  		chanType |= channeldb.SingleFunderTweaklessBit
   151  
   152  	case chanbackup.ScriptEnforcedLeaseVersion:
   153  		chanType = channeldb.LeaseExpirationBit
   154  		chanType |= channeldb.ZeroHtlcTxFeeBit
   155  		chanType |= channeldb.AnchorOutputsBit
   156  		chanType |= channeldb.SingleFunderTweaklessBit
   157  
   158  	default:
   159  		return nil, fmt.Errorf("unknown Single version: %v", err)
   160  	}
   161  
   162  	ltndLog.Infof("SCB Recovery: created channel shell for ChannelPoint"+
   163  		"(%v), chan_type=%v", backup.FundingOutpoint, chanType)
   164  
   165  	chanShell := channeldb.ChannelShell{
   166  		NodeAddrs: backup.Addresses,
   167  		Chan: &channeldb.OpenChannel{
   168  			ChanType:                chanType,
   169  			ChainHash:               backup.ChainHash,
   170  			IsInitiator:             backup.IsInitiator,
   171  			Capacity:                backup.Capacity,
   172  			FundingOutpoint:         backup.FundingOutpoint,
   173  			ShortChannelID:          backup.ShortChannelID,
   174  			IdentityPub:             backup.RemoteNodePub,
   175  			IsPending:               false,
   176  			LocalChanCfg:            backup.LocalChanCfg,
   177  			RemoteChanCfg:           backup.RemoteChanCfg,
   178  			RemoteCurrentRevocation: backup.RemoteNodePub,
   179  			RevocationStore:         shachain.NewRevocationStore(),
   180  			RevocationProducer:      shaChainProducer,
   181  			ThawHeight:              backup.LeaseExpiry,
   182  		},
   183  	}
   184  
   185  	return &chanShell, nil
   186  }
   187  
   188  // RestoreChansFromSingles attempts to map the set of single channel backups to
   189  // channel shells that will be stored persistently. Once these shells have been
   190  // stored on disk, we'll be able to connect to the channel peer an execute the
   191  // data loss recovery protocol.
   192  //
   193  // NOTE: Part of the chanbackup.ChannelRestorer interface.
   194  func (c *chanDBRestorer) RestoreChansFromSingles(backups ...chanbackup.Single) error {
   195  	channelShells := make([]*channeldb.ChannelShell, 0, len(backups))
   196  	firstChanHeight := uint32(math.MaxUint32)
   197  	for _, backup := range backups {
   198  		chanShell, err := c.openChannelShell(backup)
   199  		if err != nil {
   200  			return err
   201  		}
   202  
   203  		// Find the block height of the earliest channel in this backup.
   204  		chanHeight := chanShell.Chan.ShortChanID().BlockHeight
   205  		if chanHeight != 0 && chanHeight < firstChanHeight {
   206  			firstChanHeight = chanHeight
   207  		}
   208  
   209  		channelShells = append(channelShells, chanShell)
   210  	}
   211  
   212  	// In case there were only unconfirmed channels, we will have to scan
   213  	// the chain beginning from the launch date of SCBs.
   214  	if firstChanHeight == math.MaxUint32 {
   215  		chainHash := channelShells[0].Chan.ChainHash
   216  		switch {
   217  		case chainHash.IsEqual(&chaincfg.MainNetParams().GenesisHash):
   218  			firstChanHeight = mainnetSCBLaunchBlock
   219  
   220  		case chainHash.IsEqual(&chaincfg.TestNet3Params().GenesisHash):
   221  			firstChanHeight = testnetSCBLaunchBlock
   222  
   223  		default:
   224  			// Worst case: We have no height hint and start at
   225  			// block 1. Should only happen for SCBs in regtest,
   226  			// simnet and litecoin.
   227  			firstChanHeight = 1
   228  		}
   229  	}
   230  
   231  	// If there were channels in the backup that were not confirmed at the
   232  	// time of the backup creation, they won't have a block height in the
   233  	// ShortChanID which would lead to an error in the chain watcher.
   234  	// We want to at least set the funding broadcast height that the chain
   235  	// watcher can use instead. We have two possible fallback values for
   236  	// the broadcast height that we are going to try here.
   237  	for _, chanShell := range channelShells {
   238  		channel := chanShell.Chan
   239  
   240  		switch {
   241  		// Fallback case 1: This is an unconfirmed channel from an old
   242  		// backup file where we didn't have any workaround in place and
   243  		// the short channel ID is 0:0:0. Best we can do here is set the
   244  		// funding broadcast height to a reasonable value that we
   245  		// determined earlier.
   246  		case channel.ShortChanID().BlockHeight == 0:
   247  			channel.FundingBroadcastHeight = firstChanHeight
   248  
   249  		// Fallback case 2: It is extremely unlikely at this point that
   250  		// a channel we are trying to restore has a coinbase funding TX.
   251  		// Therefore we can be quite certain that if the TxIndex is
   252  		// zero but the block height wasn't, it was an unconfirmed
   253  		// channel where we used the BlockHeight to encode the funding
   254  		// TX broadcast height. To not end up with an invalid short
   255  		// channel ID that looks valid, we restore the "original"
   256  		// unconfirmed one here.
   257  		case channel.ShortChannelID.TxIndex == 0:
   258  			broadcastHeight := channel.ShortChannelID.BlockHeight
   259  			channel.FundingBroadcastHeight = broadcastHeight
   260  			channel.ShortChannelID.BlockHeight = 0
   261  		}
   262  	}
   263  
   264  	ltndLog.Infof("Inserting %v SCB channel shells into DB",
   265  		len(channelShells))
   266  
   267  	// Now that we have all the backups mapped into a series of Singles,
   268  	// we'll insert them all into the database.
   269  	if err := c.db.RestoreChannelShells(channelShells...); err != nil {
   270  		return err
   271  	}
   272  
   273  	ltndLog.Infof("Informing chain watchers of new restored channels")
   274  
   275  	// Finally, we'll need to inform the chain arbitrator of these new
   276  	// channels so we'll properly watch for their ultimate closure on chain
   277  	// and sweep them via the DLP.
   278  	for _, restoredChannel := range channelShells {
   279  		err := c.chainArb.WatchNewChannel(restoredChannel.Chan)
   280  		if err != nil {
   281  			return err
   282  		}
   283  	}
   284  
   285  	return nil
   286  }
   287  
   288  // A compile-time constraint to ensure chanDBRestorer implements
   289  // chanbackup.ChannelRestorer.
   290  var _ chanbackup.ChannelRestorer = (*chanDBRestorer)(nil)
   291  
   292  // ConnectPeer attempts to connect to the target node at the set of available
   293  // addresses. Once this method returns with a non-nil error, the connector
   294  // should attempt to persistently connect to the target peer in the background
   295  // as a persistent attempt.
   296  //
   297  // NOTE: Part of the chanbackup.PeerConnector interface.
   298  func (s *server) ConnectPeer(nodePub *secp256k1.PublicKey, addrs []net.Addr) error {
   299  	// Before we connect to the remote peer, we'll remove any connections
   300  	// to ensure the new connection is created after this new link/channel
   301  	// is known.
   302  	if err := s.DisconnectPeer(nodePub); err != nil {
   303  		ltndLog.Infof("Peer(%v) is already connected, proceeding "+
   304  			"with chan restore", nodePub.SerializeCompressed())
   305  	}
   306  
   307  	// For each of the known addresses, we'll attempt to launch a
   308  	// persistent connection to the (pub, addr) pair. In the event that any
   309  	// of them connect, all the other stale requests will be canceled.
   310  	for _, addr := range addrs {
   311  		netAddr := &lnwire.NetAddress{
   312  			IdentityKey: nodePub,
   313  			Address:     addr,
   314  		}
   315  
   316  		ltndLog.Infof("Attempting to connect to %v for SCB restore "+
   317  			"DLP", netAddr)
   318  
   319  		// Attempt to connect to the peer using this full address. If
   320  		// we're unable to connect to them, then we'll try the next
   321  		// address in place of it.
   322  		err := s.ConnectToPeer(netAddr, true, s.cfg.ConnectionTimeout)
   323  
   324  		// If we're already connected to this peer, then we don't
   325  		// consider this an error, so we'll exit here.
   326  		if _, ok := err.(*errPeerAlreadyConnected); ok {
   327  			return nil
   328  
   329  		} else if err != nil {
   330  			// Otherwise, something else happened, so we'll try the
   331  			// next address.
   332  			ltndLog.Errorf("unable to connect to %v to "+
   333  				"complete SCB restore: %v", netAddr, err)
   334  			continue
   335  		}
   336  
   337  		// If we connected no problem, then we can exit early as our
   338  		// job here is done.
   339  		return nil
   340  	}
   341  
   342  	return fmt.Errorf("unable to connect to peer %x for SCB restore",
   343  		nodePub.SerializeCompressed())
   344  }