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

     1  package lnwallet
     2  
     3  import (
     4  	"crypto/rand"
     5  	"encoding/binary"
     6  	"encoding/hex"
     7  	"io"
     8  	"io/ioutil"
     9  	prand "math/rand"
    10  	"net"
    11  	"os"
    12  
    13  	"github.com/decred/dcrd/chaincfg/chainhash"
    14  	"github.com/decred/dcrd/chaincfg/v3"
    15  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
    16  	"github.com/decred/dcrd/dcrutil/v4"
    17  	"github.com/decred/dcrd/wire"
    18  	"github.com/decred/dcrlnd/channeldb"
    19  	"github.com/decred/dcrlnd/input"
    20  	"github.com/decred/dcrlnd/keychain"
    21  	"github.com/decred/dcrlnd/lnwallet/chainfee"
    22  	"github.com/decred/dcrlnd/lnwire"
    23  	"github.com/decred/dcrlnd/shachain"
    24  )
    25  
    26  var (
    27  	// For simplicity a single priv key controls all of our test outputs.
    28  	testWalletPrivKey = []byte{
    29  		0x2b, 0xd8, 0x06, 0xc9, 0x7f, 0x0e, 0x00, 0xaf,
    30  		0x1a, 0x1f, 0xc3, 0x32, 0x8f, 0xa7, 0x63, 0xa9,
    31  		0x26, 0x97, 0x23, 0xc8, 0xdb, 0x8f, 0xac, 0x4f,
    32  		0x93, 0xaf, 0x71, 0xdb, 0x18, 0x6d, 0x6e, 0x90,
    33  	}
    34  
    35  	// We're alice :)
    36  	bobsPrivKey = []byte{
    37  		0x81, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda,
    38  		0x63, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17,
    39  		0xd, 0xe7, 0x95, 0xe4, 0xb7, 0x25, 0xb8, 0x4d,
    40  		0x1e, 0xb, 0x4c, 0xfd, 0x9e, 0xc5, 0x8c, 0xe9,
    41  	}
    42  
    43  	// Use a hard-coded HD seed.
    44  	testHdSeed = chainhash.Hash{
    45  		0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab,
    46  		0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4,
    47  		0x4f, 0x2f, 0x6f, 0x25, 0x88, 0xa3, 0xef, 0xb9,
    48  		0x6a, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53,
    49  	}
    50  
    51  	alicePkScript = []byte{
    52  		0x76, 0xa9, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
    53  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    54  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xac}
    55  
    56  	bobPkScript = []byte{
    57  		0x76, 0xa9, 0x14, 0x11, 0x00, 0x00, 0x00, 0x00,
    58  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    59  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xac}
    60  
    61  	// A serializable txn for testing funding txn.
    62  	testTx = &wire.MsgTx{
    63  		Version: 1,
    64  		TxIn: []*wire.TxIn{
    65  			{
    66  				PreviousOutPoint: wire.OutPoint{
    67  					Hash:  chainhash.Hash{},
    68  					Index: 0xffffffff,
    69  				},
    70  				SignatureScript: []byte{0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62},
    71  				Sequence:        0xffffffff,
    72  			},
    73  		},
    74  		TxOut: []*wire.TxOut{
    75  			{
    76  				Value: 5000000000,
    77  				PkScript: []byte{
    78  					0x41, // OP_DATA_65
    79  					0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5,
    80  					0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42,
    81  					0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1,
    82  					0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24,
    83  					0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97,
    84  					0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78,
    85  					0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20,
    86  					0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63,
    87  					0xa6, // 65-byte signature
    88  					0xac, // OP_CHECKSIG
    89  				},
    90  			},
    91  		},
    92  		LockTime: 5,
    93  	}
    94  
    95  	// A valid, DER-encoded signature (taken from secp256k1 unit tests).
    96  	testSigBytes = []byte{
    97  		0x30, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69,
    98  		0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3,
    99  		0xa1, 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32,
   100  		0xe9, 0xd6, 0x24, 0xc6, 0xc6, 0x15, 0x48, 0xab,
   101  		0x5f, 0xb8, 0xcd, 0x41, 0x02, 0x20, 0x18, 0x15,
   102  		0x22, 0xec, 0x8e, 0xca, 0x07, 0xde, 0x48, 0x60,
   103  		0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, 0x83, 0x1c,
   104  		0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, 0x08, 0x22,
   105  		0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09,
   106  	}
   107  
   108  	aliceDustLimit = dcrutil.Amount(6030)
   109  	bobDustLimit   = dcrutil.Amount(12060)
   110  
   111  	testChannelCapacity float64 = 10
   112  )
   113  
   114  // CreateTestChannels creates to fully populated channels to be used within
   115  // testing fixtures. The channels will be returned as if the funding process
   116  // has just completed.  The channel itself is funded with 10 DCR, with 5 DCR
   117  // allocated to each side. Within the channel, Alice is the initiator. The
   118  // function also returns a "cleanup" function that is meant to be called once
   119  // the test has been finalized. The clean up function will remote all temporary
   120  // files created. If tweaklessCommits is true, then the commits within the
   121  // channels will use the new format, otherwise the legacy format.
   122  func CreateTestChannels(chanType channeldb.ChannelType) (
   123  	*LightningChannel, *LightningChannel, func(), error) {
   124  
   125  	channelCapacity, err := dcrutil.NewAmount(testChannelCapacity)
   126  	if err != nil {
   127  		return nil, nil, nil, err
   128  	}
   129  
   130  	channelBal := channelCapacity / 2
   131  	csvTimeoutAlice := uint32(5)
   132  	csvTimeoutBob := uint32(4)
   133  	isAliceInitiator := true
   134  
   135  	prevOut := &wire.OutPoint{
   136  		Hash:  testHdSeed,
   137  		Index: prand.Uint32(),
   138  	}
   139  	fundingTxIn := wire.NewTxIn(prevOut, 0, nil) // TODO(decred): Need correct input value
   140  
   141  	// For each party, we'll create a distinct set of keys in order to
   142  	// emulate the typical set up with live channels.
   143  	var (
   144  		aliceKeys []*secp256k1.PrivateKey
   145  		bobKeys   []*secp256k1.PrivateKey
   146  	)
   147  	for i := 0; i < 5; i++ {
   148  		key := make([]byte, len(testWalletPrivKey))
   149  		copy(key, testWalletPrivKey)
   150  		key[0] ^= byte(i + 1)
   151  
   152  		aliceKey := secp256k1.PrivKeyFromBytes(key)
   153  		aliceKeys = append(aliceKeys, aliceKey)
   154  
   155  		key = make([]byte, len(bobsPrivKey))
   156  		copy(key, bobsPrivKey)
   157  		key[0] ^= byte(i + 1)
   158  
   159  		bobKey := secp256k1.PrivKeyFromBytes(key)
   160  		bobKeys = append(bobKeys, bobKey)
   161  	}
   162  
   163  	aliceCfg := channeldb.ChannelConfig{
   164  		ChannelConstraints: channeldb.ChannelConstraints{
   165  			DustLimit:        aliceDustLimit,
   166  			MaxPendingAmount: lnwire.NewMAtomsFromAtoms(channelCapacity),
   167  			ChanReserve:      channelCapacity / 100,
   168  			MinHTLC:          0,
   169  			MaxAcceptedHtlcs: input.MaxHTLCNumber / 2,
   170  			CsvDelay:         uint16(csvTimeoutAlice),
   171  		},
   172  		MultiSigKey: keychain.KeyDescriptor{
   173  			PubKey: aliceKeys[0].PubKey(),
   174  		},
   175  		RevocationBasePoint: keychain.KeyDescriptor{
   176  			PubKey: aliceKeys[1].PubKey(),
   177  		},
   178  		PaymentBasePoint: keychain.KeyDescriptor{
   179  			PubKey: aliceKeys[2].PubKey(),
   180  		},
   181  		DelayBasePoint: keychain.KeyDescriptor{
   182  			PubKey: aliceKeys[3].PubKey(),
   183  		},
   184  		HtlcBasePoint: keychain.KeyDescriptor{
   185  			PubKey: aliceKeys[4].PubKey(),
   186  		},
   187  	}
   188  	bobCfg := channeldb.ChannelConfig{
   189  		ChannelConstraints: channeldb.ChannelConstraints{
   190  			DustLimit:        bobDustLimit,
   191  			MaxPendingAmount: lnwire.NewMAtomsFromAtoms(channelCapacity),
   192  			ChanReserve:      channelCapacity / 100,
   193  			MinHTLC:          0,
   194  			MaxAcceptedHtlcs: input.MaxHTLCNumber / 2,
   195  			CsvDelay:         uint16(csvTimeoutBob),
   196  		},
   197  		MultiSigKey: keychain.KeyDescriptor{
   198  			PubKey: bobKeys[0].PubKey(),
   199  		},
   200  		RevocationBasePoint: keychain.KeyDescriptor{
   201  			PubKey: bobKeys[1].PubKey(),
   202  		},
   203  		PaymentBasePoint: keychain.KeyDescriptor{
   204  			PubKey: bobKeys[2].PubKey(),
   205  		},
   206  		DelayBasePoint: keychain.KeyDescriptor{
   207  			PubKey: bobKeys[3].PubKey(),
   208  		},
   209  		HtlcBasePoint: keychain.KeyDescriptor{
   210  			PubKey: bobKeys[4].PubKey(),
   211  		},
   212  	}
   213  
   214  	bobRoot, err := shachain.NewHash(bobKeys[0].Serialize())
   215  	if err != nil {
   216  		return nil, nil, nil, err
   217  	}
   218  	bobPreimageProducer := shachain.NewRevocationProducer(*bobRoot)
   219  	bobFirstRevoke, err := bobPreimageProducer.AtIndex(0)
   220  	if err != nil {
   221  		return nil, nil, nil, err
   222  	}
   223  	bobCommitPoint := input.ComputeCommitmentPoint(bobFirstRevoke[:])
   224  
   225  	aliceRoot, err := shachain.NewHash(aliceKeys[0].Serialize())
   226  	if err != nil {
   227  		return nil, nil, nil, err
   228  	}
   229  	alicePreimageProducer := shachain.NewRevocationProducer(*aliceRoot)
   230  	aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0)
   231  	if err != nil {
   232  		return nil, nil, nil, err
   233  	}
   234  	aliceCommitPoint := input.ComputeCommitmentPoint(aliceFirstRevoke[:])
   235  
   236  	netParams := chaincfg.RegNetParams()
   237  
   238  	aliceCommitTx, bobCommitTx, err := CreateCommitmentTxns(
   239  		channelBal, channelBal, &aliceCfg, &bobCfg, aliceCommitPoint,
   240  		bobCommitPoint, *fundingTxIn, chanType, isAliceInitiator, 0, netParams,
   241  	)
   242  	if err != nil {
   243  		return nil, nil, nil, err
   244  	}
   245  
   246  	alicePath, err := ioutil.TempDir("", "alicedb")
   247  	if err != nil {
   248  		return nil, nil, nil, err
   249  	}
   250  	dbAlice, err := channeldb.Open(alicePath)
   251  	if err != nil {
   252  		return nil, nil, nil, err
   253  	}
   254  
   255  	bobPath, err := ioutil.TempDir("", "bobdb")
   256  	if err != nil {
   257  		return nil, nil, nil, err
   258  	}
   259  	dbBob, err := channeldb.Open(bobPath)
   260  	if err != nil {
   261  		return nil, nil, nil, err
   262  	}
   263  
   264  	// The rate for this estimator must be the same as what is returned by
   265  	// calcStaticFee().
   266  	//
   267  	// Note: This is purposefully higher than the feeKBFloor (that is, the
   268  	// network standard relay fee) so that tests can try both lowering and
   269  	// increasing the fee rate.
   270  	estimator := chainfee.NewStaticEstimator(1e5, 0)
   271  	feePerKB, err := estimator.EstimateFeePerKB(1)
   272  	if err != nil {
   273  		return nil, nil, nil, err
   274  	}
   275  	commitFee := calcStaticFee(chanType, 0)
   276  	var anchorAmt dcrutil.Amount
   277  	if chanType.HasAnchors() {
   278  		anchorAmt += 2 * anchorSize
   279  	}
   280  
   281  	aliceBalance := lnwire.NewMAtomsFromAtoms(
   282  		channelBal - commitFee - anchorAmt,
   283  	)
   284  	bobBalance := lnwire.NewMAtomsFromAtoms(channelBal)
   285  
   286  	aliceLocalCommit := channeldb.ChannelCommitment{
   287  		CommitHeight:  0,
   288  		LocalBalance:  aliceBalance,
   289  		RemoteBalance: bobBalance,
   290  		CommitFee:     commitFee,
   291  		FeePerKB:      dcrutil.Amount(feePerKB),
   292  		CommitTx:      aliceCommitTx,
   293  		CommitSig:     testSigBytes,
   294  	}
   295  	aliceRemoteCommit := channeldb.ChannelCommitment{
   296  		CommitHeight:  0,
   297  		LocalBalance:  aliceBalance,
   298  		RemoteBalance: bobBalance,
   299  		CommitFee:     commitFee,
   300  		FeePerKB:      dcrutil.Amount(feePerKB),
   301  		CommitTx:      bobCommitTx,
   302  		CommitSig:     testSigBytes,
   303  	}
   304  	bobLocalCommit := channeldb.ChannelCommitment{
   305  		CommitHeight:  0,
   306  		LocalBalance:  bobBalance,
   307  		RemoteBalance: aliceBalance,
   308  		CommitFee:     commitFee,
   309  		FeePerKB:      dcrutil.Amount(feePerKB),
   310  		CommitTx:      bobCommitTx,
   311  		CommitSig:     testSigBytes,
   312  	}
   313  	bobRemoteCommit := channeldb.ChannelCommitment{
   314  		CommitHeight:  0,
   315  		LocalBalance:  bobBalance,
   316  		RemoteBalance: aliceBalance,
   317  		CommitFee:     commitFee,
   318  		FeePerKB:      dcrutil.Amount(feePerKB),
   319  		CommitTx:      aliceCommitTx,
   320  		CommitSig:     testSigBytes,
   321  	}
   322  
   323  	var chanIDBytes [8]byte
   324  	if _, err := io.ReadFull(rand.Reader, chanIDBytes[:]); err != nil {
   325  		return nil, nil, nil, err
   326  	}
   327  
   328  	shortChanID := lnwire.NewShortChanIDFromInt(
   329  		binary.BigEndian.Uint64(chanIDBytes[:]),
   330  	)
   331  
   332  	aliceChannelState := &channeldb.OpenChannel{
   333  		LocalChanCfg:            aliceCfg,
   334  		RemoteChanCfg:           bobCfg,
   335  		IdentityPub:             aliceKeys[0].PubKey(),
   336  		FundingOutpoint:         *prevOut,
   337  		ShortChannelID:          shortChanID,
   338  		ChanType:                chanType,
   339  		IsInitiator:             isAliceInitiator,
   340  		Capacity:                channelCapacity,
   341  		RemoteCurrentRevocation: bobCommitPoint,
   342  		RevocationProducer:      alicePreimageProducer,
   343  		RevocationStore:         shachain.NewRevocationStore(),
   344  		LocalCommitment:         aliceLocalCommit,
   345  		RemoteCommitment:        aliceRemoteCommit,
   346  		Db:                      dbAlice.ChannelStateDB(),
   347  		Packager:                channeldb.NewChannelPackager(shortChanID),
   348  		FundingTxn:              testTx,
   349  	}
   350  	bobChannelState := &channeldb.OpenChannel{
   351  		LocalChanCfg:            bobCfg,
   352  		RemoteChanCfg:           aliceCfg,
   353  		IdentityPub:             bobKeys[0].PubKey(),
   354  		FundingOutpoint:         *prevOut,
   355  		ShortChannelID:          shortChanID,
   356  		ChanType:                chanType,
   357  		IsInitiator:             !isAliceInitiator,
   358  		Capacity:                channelCapacity,
   359  		RemoteCurrentRevocation: aliceCommitPoint,
   360  		RevocationProducer:      bobPreimageProducer,
   361  		RevocationStore:         shachain.NewRevocationStore(),
   362  		LocalCommitment:         bobLocalCommit,
   363  		RemoteCommitment:        bobRemoteCommit,
   364  		Db:                      dbBob.ChannelStateDB(),
   365  		Packager:                channeldb.NewChannelPackager(shortChanID),
   366  	}
   367  
   368  	aliceSigner := &input.MockSigner{Privkeys: aliceKeys}
   369  	bobSigner := &input.MockSigner{Privkeys: bobKeys}
   370  
   371  	// TODO(roasbeef): make mock version of pre-image store
   372  
   373  	alicePool := NewSigPool(1, aliceSigner)
   374  	channelAlice, err := NewLightningChannel(
   375  		aliceSigner, aliceChannelState, alicePool, netParams,
   376  	)
   377  	if err != nil {
   378  		return nil, nil, nil, err
   379  	}
   380  	alicePool.Start()
   381  
   382  	obfuscator := createStateHintObfuscator(aliceChannelState)
   383  
   384  	bobPool := NewSigPool(1, bobSigner)
   385  	channelBob, err := NewLightningChannel(
   386  		bobSigner, bobChannelState, bobPool, netParams,
   387  	)
   388  	if err != nil {
   389  		return nil, nil, nil, err
   390  	}
   391  	if err = bobPool.Start(); err != nil {
   392  		return nil, nil, nil, err
   393  	}
   394  	err = SetStateNumHint(
   395  		aliceCommitTx, 0, obfuscator,
   396  	)
   397  	if err != nil {
   398  		return nil, nil, nil, err
   399  	}
   400  	err = SetStateNumHint(
   401  		bobCommitTx, 0, obfuscator,
   402  	)
   403  	if err != nil {
   404  		return nil, nil, nil, err
   405  	}
   406  
   407  	addr := &net.TCPAddr{
   408  		IP:   net.ParseIP("127.0.0.1"),
   409  		Port: 18556,
   410  	}
   411  	if err := channelAlice.channelState.SyncPending(addr, 101); err != nil {
   412  		return nil, nil, nil, err
   413  	}
   414  
   415  	addr = &net.TCPAddr{
   416  		IP:   net.ParseIP("127.0.0.1"),
   417  		Port: 18555,
   418  	}
   419  
   420  	if err := channelBob.channelState.SyncPending(addr, 101); err != nil {
   421  		return nil, nil, nil, err
   422  	}
   423  
   424  	cleanUpFunc := func() {
   425  		os.RemoveAll(bobPath)
   426  		os.RemoveAll(alicePath)
   427  
   428  		alicePool.Stop()
   429  		bobPool.Stop()
   430  	}
   431  
   432  	// Now that the channel are open, simulate the start of a session by
   433  	// having Alice and Bob extend their revocation windows to each other.
   434  	err = initRevocationWindows(channelAlice, channelBob)
   435  	if err != nil {
   436  		return nil, nil, nil, err
   437  	}
   438  
   439  	return channelAlice, channelBob, cleanUpFunc, nil
   440  }
   441  
   442  // initRevocationWindows simulates a new channel being opened within the p2p
   443  // network by populating the initial revocation windows of the passed
   444  // commitment state machines.
   445  func initRevocationWindows(chanA, chanB *LightningChannel) error {
   446  	aliceNextRevoke, err := chanA.NextRevocationKey()
   447  	if err != nil {
   448  		return err
   449  	}
   450  	if err := chanB.InitNextRevocation(aliceNextRevoke); err != nil {
   451  		return err
   452  	}
   453  
   454  	bobNextRevoke, err := chanB.NextRevocationKey()
   455  	if err != nil {
   456  		return err
   457  	}
   458  	if err := chanA.InitNextRevocation(bobNextRevoke); err != nil {
   459  		return err
   460  	}
   461  
   462  	return nil
   463  }
   464  
   465  // privkeyFromHex parses a Decred private key from a hex encoded string.
   466  func privkeyFromHex(keyHex string) (*secp256k1.PrivateKey, error) {
   467  	bytes, err := hex.DecodeString(keyHex)
   468  	if err != nil {
   469  		return nil, err
   470  	}
   471  	key := secp256k1.PrivKeyFromBytes(bytes)
   472  	return key, nil
   473  
   474  }
   475  
   476  func privKeyFromBytes(b []byte) (*secp256k1.PrivateKey, *secp256k1.PublicKey) {
   477  	key := secp256k1.PrivKeyFromBytes(b)
   478  	return key, key.PubKey()
   479  }
   480  
   481  // txFromHex parses a full Decred transaction from a hex encoded string.
   482  func txFromHex(txHex string) (*dcrutil.Tx, error) {
   483  	bytes, err := hex.DecodeString(txHex)
   484  	if err != nil {
   485  		return nil, err
   486  	}
   487  	return dcrutil.NewTxFromBytes(bytes)
   488  }
   489  
   490  // calcStaticFee calculates appropriate fees for commitment transactions.  This
   491  // function provides a simple way to allow test balance assertions to take fee
   492  // calculations into account.
   493  //
   494  // This uses a fixed, hard-coded value of 6000 Atoms/kB as fee.
   495  //
   496  // TODO(bvu): Refactor when dynamic fee estimation is added.
   497  func calcStaticFee(chanType channeldb.ChannelType, numHTLCs int) dcrutil.Amount {
   498  	const (
   499  		feePerKB = dcrutil.Amount(1e5)
   500  	)
   501  
   502  	commitSize := CommitSize(chanType) + input.HTLCOutputSize*int64(numHTLCs)
   503  	return feePerKB * dcrutil.Amount(commitSize) / 1000
   504  }
   505  
   506  // ForceStateTransition executes the necessary interaction between the two
   507  // commitment state machines to transition to a new state locking in any
   508  // pending updates. This method is useful when testing interactions between two
   509  // live state machines.
   510  func ForceStateTransition(chanA, chanB *LightningChannel) error {
   511  	aliceSig, aliceHtlcSigs, _, err := chanA.SignNextCommitment()
   512  	if err != nil {
   513  		return err
   514  	}
   515  	if err = chanB.ReceiveNewCommitment(aliceSig, aliceHtlcSigs); err != nil {
   516  		return err
   517  	}
   518  
   519  	bobRevocation, _, err := chanB.RevokeCurrentCommitment()
   520  	if err != nil {
   521  		return err
   522  	}
   523  	bobSig, bobHtlcSigs, _, err := chanB.SignNextCommitment()
   524  	if err != nil {
   525  		return err
   526  	}
   527  
   528  	if _, _, _, _, err := chanA.ReceiveRevocation(bobRevocation); err != nil {
   529  		return err
   530  	}
   531  	if err := chanA.ReceiveNewCommitment(bobSig, bobHtlcSigs); err != nil {
   532  		return err
   533  	}
   534  
   535  	aliceRevocation, _, err := chanA.RevokeCurrentCommitment()
   536  	if err != nil {
   537  		return err
   538  	}
   539  	if _, _, _, _, err := chanB.ReceiveRevocation(aliceRevocation); err != nil {
   540  		return err
   541  	}
   542  
   543  	return nil
   544  }