From 1ec5dc3c2d103b4a883b39b028d969c0d5d8d4ad Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sun, 29 Apr 2018 15:26:37 -0700 Subject: [PATCH 1/7] channeldb: additionally store static channel information in CloseChannelSummary In this commit, we extend the CloseChannelSummary by also storing: the current unrevoked revocation for the remote party, the next pending unused revocation, and also the local channel config. We move to store these as the provide an extra level of defense against bugs as we'll always store information required to derive keys for any current and prior states. --- channeldb/channel.go | 139 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 115 insertions(+), 24 deletions(-) diff --git a/channeldb/channel.go b/channeldb/channel.go index 9e13e5d34..12e942be7 100644 --- a/channeldb/channel.go +++ b/channeldb/channel.go @@ -1578,9 +1578,9 @@ const ( ) // ChannelCloseSummary contains the final state of a channel at the point it -// was closed. Once a channel is closed, all the information pertaining to -// that channel within the openChannelBucket is deleted, and a compact -// summary is put in place instead. +// was closed. Once a channel is closed, all the information pertaining to that +// channel within the openChannelBucket is deleted, and a compact summary is +// put in place instead. type ChannelCloseSummary struct { // ChanPoint is the outpoint for this channel's funding transaction, // and is used as a unique identifier for the channel. @@ -1606,7 +1606,8 @@ type ChannelCloseSummary struct { // Capacity was the total capacity of the channel. Capacity btcutil.Amount - // CloseHeight is the height at which the funding transaction was spent. + // CloseHeight is the height at which the funding transaction was + // spent. CloseHeight uint32 // SettledBalance is our total balance settled balance at the time of @@ -1636,6 +1637,21 @@ type ChannelCloseSummary struct { // closed, they'll stay marked as "pending" until _all_ the pending // funds have been swept. IsPending bool + + // RemoteCurrentRevocation is the current revocation for their + // commitment transaction. However, since this the derived public key, + // we don't yet have the private key so we aren't yet able to verify + // that it's actually in the hash chain. + RemoteCurrentRevocation *btcec.PublicKey + + // RemoteNextRevocation is the revocation key to be used for the *next* + // commitment transaction we create for the local node. Within the + // specification, this value is referred to as the + // per-commitment-point. + RemoteNextRevocation *btcec.PublicKey + + // LocalChanCfg is the channel configuration for the local node. + LocalChanConfig ChannelConfig } // CloseChannel closes a previously active Lightning channel. Closing a channel @@ -1675,6 +1691,16 @@ func (c *OpenChannel) CloseChannel(summary *ChannelCloseSummary) error { return ErrNoActiveChannels } + // Before we delete the channel state, we'll read out the full + // details, as we'll also store portions of this information + // for record keeping. + chanState, err := fetchOpenChannel( + chanBucket, &c.FundingOutpoint, + ) + if err != nil { + return err + } + // Now that the index to this channel has been deleted, purge // the remaining channel metadata from the database. err = deleteOpenChannel(chanBucket, chanPointBuf.Bytes()) @@ -1703,7 +1729,9 @@ func (c *OpenChannel) CloseChannel(summary *ChannelCloseSummary) error { // Finally, create a summary of this channel in the closed // channel bucket for this node. - return putChannelCloseSummary(tx, chanPointBuf.Bytes(), summary) + return putChannelCloseSummary( + tx, chanPointBuf.Bytes(), summary, chanState, + ) }) } @@ -1818,13 +1846,17 @@ func (c *OpenChannel) RemoteRevocationStore() (shachain.Store, error) { } func putChannelCloseSummary(tx *bolt.Tx, chanID []byte, - summary *ChannelCloseSummary) error { + summary *ChannelCloseSummary, lastChanState *OpenChannel) error { closedChanBucket, err := tx.CreateBucketIfNotExists(closedChannelBucket) if err != nil { return err } + summary.RemoteCurrentRevocation = lastChanState.RemoteCurrentRevocation + summary.RemoteNextRevocation = lastChanState.RemoteNextRevocation + summary.LocalChanConfig = lastChanState.LocalChanCfg + var b bytes.Buffer if err := serializeChannelCloseSummary(&b, summary); err != nil { return err @@ -1834,11 +1866,37 @@ func putChannelCloseSummary(tx *bolt.Tx, chanID []byte, } func serializeChannelCloseSummary(w io.Writer, cs *ChannelCloseSummary) error { - return writeElements(w, + err := writeElements(w, cs.ChanPoint, cs.ShortChanID, cs.ChainHash, cs.ClosingTXID, cs.CloseHeight, cs.RemotePub, cs.Capacity, cs.SettledBalance, cs.TimeLockedBalance, cs.CloseType, cs.IsPending, ) + if err != nil { + return err + } + + // If this is a close channel summary created before the addition of + // the new fields, then we can exit here. + if cs.RemoteCurrentRevocation == nil { + return nil + } + + if err := writeElements(w, cs.RemoteCurrentRevocation); err != nil { + return err + } + + if err := writeChanConfig(w, &cs.LocalChanConfig); err != nil { + return err + } + + // We'll write this field last, as it's possible for a channel to be + // closed before we learn of the next unrevoked revocation point for + // the remote party. + if cs.RemoteNextRevocation == nil { + return nil + } + + return writeElements(w, cs.RemoteNextRevocation) } func fetchChannelCloseSummary(tx *bolt.Tx, @@ -1870,9 +1928,49 @@ func deserializeCloseChannelSummary(r io.Reader) (*ChannelCloseSummary, error) { return nil, err } + // We'll now check to see if the channel close summary was encoded with + // any of the additional optional fields. + err = readElements(r, &c.RemoteCurrentRevocation) + switch { + case err == io.EOF: + return c, nil + + // If we got a non-eof error, then we know there's an actually issue. + // Otherwise, it may have been the case that this summary didn't have + // the set of optional fields. + case err != nil: + return nil, err + } + + if err := readChanConfig(r, &c.LocalChanConfig); err != nil { + return nil, err + } + + // Finally, we'll attempt to read the next unrevoked commitment point + // for the remote party. If we closed the channel before receiving a + // funding locked message, then this can be nil. As a result, we'll use + // the same technique to read the field, only if there's still data + // left in the buffer. + err = readElements(r, &c.RemoteNextRevocation) + if err != nil && err != io.EOF { + // If we got a non-eof error, then we know there's an actually + // issue. Otherwise, it may have been the case that this + // summary didn't have the set of optional fields. + return nil, err + } + return c, nil } +func writeChanConfig(b io.Writer, c *ChannelConfig) error { + return writeElements(b, + c.DustLimit, c.MaxPendingAmount, c.ChanReserve, c.MinHTLC, + c.MaxAcceptedHtlcs, c.CsvDelay, c.MultiSigKey, + c.RevocationBasePoint, c.PaymentBasePoint, c.DelayBasePoint, + c.HtlcBasePoint, + ) +} + func putChanInfo(chanBucket *bolt.Bucket, channel *OpenChannel) error { var w bytes.Buffer if err := writeElements(&w, @@ -1893,14 +1991,6 @@ func putChanInfo(chanBucket *bolt.Bucket, channel *OpenChannel) error { } } - writeChanConfig := func(b io.Writer, c *ChannelConfig) error { - return writeElements(b, - c.DustLimit, c.MaxPendingAmount, c.ChanReserve, c.MinHTLC, - c.MaxAcceptedHtlcs, c.CsvDelay, c.MultiSigKey, - c.RevocationBasePoint, c.PaymentBasePoint, c.DelayBasePoint, - c.HtlcBasePoint, - ) - } if err := writeChanConfig(&w, &channel.LocalChanCfg); err != nil { return err } @@ -1976,6 +2066,16 @@ func putChanRevocationState(chanBucket *bolt.Bucket, channel *OpenChannel) error return chanBucket.Put(revocationStateKey, b.Bytes()) } +func readChanConfig(b io.Reader, c *ChannelConfig) error { + return readElements(b, + &c.DustLimit, &c.MaxPendingAmount, &c.ChanReserve, + &c.MinHTLC, &c.MaxAcceptedHtlcs, &c.CsvDelay, + &c.MultiSigKey, &c.RevocationBasePoint, + &c.PaymentBasePoint, &c.DelayBasePoint, + &c.HtlcBasePoint, + ) +} + func fetchChanInfo(chanBucket *bolt.Bucket, channel *OpenChannel) error { infoBytes := chanBucket.Get(chanInfoKey) if infoBytes == nil { @@ -2001,15 +2101,6 @@ func fetchChanInfo(chanBucket *bolt.Bucket, channel *OpenChannel) error { } } - readChanConfig := func(b io.Reader, c *ChannelConfig) error { - return readElements(b, - &c.DustLimit, &c.MaxPendingAmount, &c.ChanReserve, - &c.MinHTLC, &c.MaxAcceptedHtlcs, &c.CsvDelay, - &c.MultiSigKey, &c.RevocationBasePoint, - &c.PaymentBasePoint, &c.DelayBasePoint, - &c.HtlcBasePoint, - ) - } if err := readChanConfig(r, &channel.LocalChanCfg); err != nil { return err } From 90bbc4f41b076f3cb3cdc807be1d9d060cc47f28 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sun, 29 Apr 2018 15:31:32 -0700 Subject: [PATCH 2/7] lnwallet: move CreateTestChannels to new test_utils.go In this commit, we move a set of useful functions for testing channels into a new file. The old createTestChannels has been improved as it will now properly set the height hint on the first created commitments, and also no longer accepts any arguments as the revocation window no longer exists. --- lnwallet/channel_test.go | 458 +++--------------------------- lnwallet/common_test.go | 207 -------------- lnwallet/test_utils.go | 592 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 635 insertions(+), 622 deletions(-) delete mode 100644 lnwallet/common_test.go create mode 100644 lnwallet/test_utils.go diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index b36c393c7..e788532ba 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -2,122 +2,21 @@ package lnwallet import ( "bytes" - "crypto/rand" "crypto/sha256" - "encoding/binary" - "io" - "io/ioutil" - "os" "reflect" "runtime" "testing" "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/chainntnfs" - "github.com/lightningnetwork/lnd/channeldb" - "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwire" - "github.com/lightningnetwork/lnd/shachain" "github.com/roasbeef/btcd/blockchain" - "github.com/roasbeef/btcd/btcec" - "github.com/roasbeef/btcd/chaincfg/chainhash" "github.com/roasbeef/btcd/txscript" "github.com/roasbeef/btcd/wire" "github.com/roasbeef/btcutil" ) -var ( - privPass = []byte("private-test") - - // For simplicity a single priv key controls all of our test outputs. - testWalletPrivKey = []byte{ - 0x2b, 0xd8, 0x06, 0xc9, 0x7f, 0x0e, 0x00, 0xaf, - 0x1a, 0x1f, 0xc3, 0x32, 0x8f, 0xa7, 0x63, 0xa9, - 0x26, 0x97, 0x23, 0xc8, 0xdb, 0x8f, 0xac, 0x4f, - 0x93, 0xaf, 0x71, 0xdb, 0x18, 0x6d, 0x6e, 0x90, - } - - // We're alice :) - bobsPrivKey = []byte{ - 0x81, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda, - 0x63, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17, - 0xd, 0xe7, 0x95, 0xe4, 0xb7, 0x25, 0xb8, 0x4d, - 0x1e, 0xb, 0x4c, 0xfd, 0x9e, 0xc5, 0x8c, 0xe9, - } - - // Use a hard-coded HD seed. - testHdSeed = chainhash.Hash{ - 0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab, - 0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4, - 0x4f, 0x2f, 0x6f, 0x25, 0x88, 0xa3, 0xef, 0xb9, - 0x6a, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53, - } - - // The number of confirmations required to consider any created channel - // open. - numReqConfs = uint16(1) - - // A serializable txn for testing funding txn. - testTx = &wire.MsgTx{ - Version: 1, - TxIn: []*wire.TxIn{ - { - PreviousOutPoint: wire.OutPoint{ - Hash: chainhash.Hash{}, - Index: 0xffffffff, - }, - SignatureScript: []byte{0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62}, - Sequence: 0xffffffff, - }, - }, - TxOut: []*wire.TxOut{ - { - Value: 5000000000, - PkScript: []byte{ - 0x41, // OP_DATA_65 - 0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5, - 0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42, - 0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1, - 0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24, - 0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97, - 0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78, - 0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20, - 0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63, - 0xa6, // 65-byte signature - 0xac, // OP_CHECKSIG - }, - }, - }, - LockTime: 5, - } -) - -// initRevocationWindows simulates a new channel being opened within the p2p -// network by populating the initial revocation windows of the passed -// commitment state machines. -// -// TODO(roasbeef): rename! -func initRevocationWindows(chanA, chanB *LightningChannel, windowSize int) error { - aliceNextRevoke, err := chanA.NextRevocationKey() - if err != nil { - return err - } - if err := chanB.InitNextRevocation(aliceNextRevoke); err != nil { - return err - } - - bobNextRevoke, err := chanB.NextRevocationKey() - if err != nil { - return err - } - if err := chanA.InitNextRevocation(bobNextRevoke); err != nil { - return err - } - - return nil -} - // forceStateTransition executes the necessary interaction between the two // commitment state machines to transition to a new state locking in any // pending updates. @@ -157,277 +56,6 @@ func forceStateTransition(chanA, chanB *LightningChannel) error { return nil } -// createTestChannels creates two test lightning channels using the provided -// notifier. The channel itself is funded with 10 BTC, with 5 BTC allocated to -// each side. Within the channel, Alice is the initiator. -func createTestChannels(revocationWindow int) (*LightningChannel, - *LightningChannel, func(), error) { - - channelCapacity, err := btcutil.NewAmount(10) - if err != nil { - return nil, nil, nil, err - } - - channelBal := channelCapacity / 2 - aliceDustLimit := btcutil.Amount(200) - bobDustLimit := btcutil.Amount(1300) - csvTimeoutAlice := uint32(5) - csvTimeoutBob := uint32(4) - - prevOut := &wire.OutPoint{ - Hash: chainhash.Hash(testHdSeed), - Index: 0, - } - fundingTxIn := wire.NewTxIn(prevOut, nil, nil) - - // For each party, we'll create a distinct set of keys in order to - // emulate the typical set up with live channels. - var ( - aliceKeys []*btcec.PrivateKey - bobKeys []*btcec.PrivateKey - ) - for i := 0; i < 5; i++ { - key := make([]byte, len(testWalletPrivKey)) - copy(key[:], testWalletPrivKey[:]) - key[0] ^= byte(i + 1) - - aliceKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), key) - aliceKeys = append(aliceKeys, aliceKey) - - key = make([]byte, len(bobsPrivKey)) - copy(key[:], bobsPrivKey) - key[0] ^= byte(i + 1) - - bobKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), key) - bobKeys = append(bobKeys, bobKey) - } - - aliceCfg := channeldb.ChannelConfig{ - ChannelConstraints: channeldb.ChannelConstraints{ - DustLimit: aliceDustLimit, - MaxPendingAmount: lnwire.NewMSatFromSatoshis(channelCapacity), - ChanReserve: channelCapacity / 100, - MinHTLC: 0, - MaxAcceptedHtlcs: MaxHTLCNumber / 2, - }, - CsvDelay: uint16(csvTimeoutAlice), - MultiSigKey: keychain.KeyDescriptor{ - PubKey: aliceKeys[0].PubKey(), - }, - RevocationBasePoint: keychain.KeyDescriptor{ - PubKey: aliceKeys[1].PubKey(), - }, - PaymentBasePoint: keychain.KeyDescriptor{ - PubKey: aliceKeys[2].PubKey(), - }, - DelayBasePoint: keychain.KeyDescriptor{ - PubKey: aliceKeys[3].PubKey(), - }, - HtlcBasePoint: keychain.KeyDescriptor{ - PubKey: aliceKeys[4].PubKey(), - }, - } - bobCfg := channeldb.ChannelConfig{ - ChannelConstraints: channeldb.ChannelConstraints{ - DustLimit: bobDustLimit, - MaxPendingAmount: lnwire.NewMSatFromSatoshis(channelCapacity), - ChanReserve: channelCapacity / 100, - MinHTLC: 0, - MaxAcceptedHtlcs: MaxHTLCNumber / 2, - }, - CsvDelay: uint16(csvTimeoutBob), - MultiSigKey: keychain.KeyDescriptor{ - PubKey: bobKeys[0].PubKey(), - }, - RevocationBasePoint: keychain.KeyDescriptor{ - PubKey: bobKeys[1].PubKey(), - }, - PaymentBasePoint: keychain.KeyDescriptor{ - PubKey: bobKeys[2].PubKey(), - }, - DelayBasePoint: keychain.KeyDescriptor{ - PubKey: bobKeys[3].PubKey(), - }, - HtlcBasePoint: keychain.KeyDescriptor{ - PubKey: bobKeys[4].PubKey(), - }, - } - - bobRoot, err := chainhash.NewHash(bobKeys[0].Serialize()) - if err != nil { - return nil, nil, nil, err - } - bobPreimageProducer := shachain.NewRevocationProducer(*bobRoot) - bobFirstRevoke, err := bobPreimageProducer.AtIndex(0) - if err != nil { - return nil, nil, nil, err - } - bobCommitPoint := ComputeCommitmentPoint(bobFirstRevoke[:]) - - aliceRoot, err := chainhash.NewHash(aliceKeys[0].Serialize()) - if err != nil { - return nil, nil, nil, err - } - alicePreimageProducer := shachain.NewRevocationProducer(*aliceRoot) - aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0) - if err != nil { - return nil, nil, nil, err - } - aliceCommitPoint := ComputeCommitmentPoint(aliceFirstRevoke[:]) - - aliceCommitTx, bobCommitTx, err := CreateCommitmentTxns(channelBal, - channelBal, &aliceCfg, &bobCfg, aliceCommitPoint, bobCommitPoint, - *fundingTxIn) - if err != nil { - return nil, nil, nil, err - } - - alicePath, err := ioutil.TempDir("", "alicedb") - dbAlice, err := channeldb.Open(alicePath) - if err != nil { - return nil, nil, nil, err - } - - bobPath, err := ioutil.TempDir("", "bobdb") - dbBob, err := channeldb.Open(bobPath) - if err != nil { - return nil, nil, nil, err - } - - estimator := &StaticFeeEstimator{24} - feePerVSize, err := estimator.EstimateFeePerVSize(1) - if err != nil { - return nil, nil, nil, err - } - feePerKw := feePerVSize.FeePerKWeight() - commitFee := calcStaticFee(0) - - aliceCommit := channeldb.ChannelCommitment{ - CommitHeight: 0, - LocalBalance: lnwire.NewMSatFromSatoshis(channelBal - commitFee), - RemoteBalance: lnwire.NewMSatFromSatoshis(channelBal), - CommitFee: commitFee, - FeePerKw: btcutil.Amount(feePerKw), - CommitTx: aliceCommitTx, - CommitSig: bytes.Repeat([]byte{1}, 71), - } - bobCommit := channeldb.ChannelCommitment{ - CommitHeight: 0, - LocalBalance: lnwire.NewMSatFromSatoshis(channelBal), - RemoteBalance: lnwire.NewMSatFromSatoshis(channelBal - commitFee), - CommitFee: commitFee, - FeePerKw: btcutil.Amount(feePerKw), - CommitTx: bobCommitTx, - CommitSig: bytes.Repeat([]byte{1}, 71), - } - - var chanIDBytes [8]byte - if _, err := io.ReadFull(rand.Reader, chanIDBytes[:]); err != nil { - return nil, nil, nil, err - } - - shortChanID := lnwire.NewShortChanIDFromInt( - binary.BigEndian.Uint64(chanIDBytes[:]), - ) - - aliceChannelState := &channeldb.OpenChannel{ - LocalChanCfg: aliceCfg, - RemoteChanCfg: bobCfg, - IdentityPub: aliceKeys[0].PubKey(), - FundingOutpoint: *prevOut, - ShortChanID: shortChanID, - ChanType: channeldb.SingleFunder, - IsInitiator: true, - Capacity: channelCapacity, - RemoteCurrentRevocation: bobCommitPoint, - RevocationProducer: alicePreimageProducer, - RevocationStore: shachain.NewRevocationStore(), - LocalCommitment: aliceCommit, - RemoteCommitment: aliceCommit, - Db: dbAlice, - Packager: channeldb.NewChannelPackager(shortChanID), - FundingTxn: testTx, - } - bobChannelState := &channeldb.OpenChannel{ - LocalChanCfg: bobCfg, - RemoteChanCfg: aliceCfg, - IdentityPub: bobKeys[0].PubKey(), - FundingOutpoint: *prevOut, - ShortChanID: shortChanID, - ChanType: channeldb.SingleFunder, - IsInitiator: false, - Capacity: channelCapacity, - RemoteCurrentRevocation: aliceCommitPoint, - RevocationProducer: bobPreimageProducer, - RevocationStore: shachain.NewRevocationStore(), - LocalCommitment: bobCommit, - RemoteCommitment: bobCommit, - Db: dbBob, - Packager: channeldb.NewChannelPackager(shortChanID), - } - - aliceSigner := &mockSigner{privkeys: aliceKeys} - bobSigner := &mockSigner{privkeys: bobKeys} - - pCache := &mockPreimageCache{ - // hash -> preimage - preimageMap: make(map[[32]byte][]byte), - } - - // TODO(roasbeef): make mock version of pre-image store - channelAlice, err := NewLightningChannel( - aliceSigner, pCache, aliceChannelState, - ) - if err != nil { - return nil, nil, nil, err - } - channelBob, err := NewLightningChannel( - bobSigner, pCache, bobChannelState, - ) - if err != nil { - return nil, nil, nil, err - } - - if err := channelAlice.channelState.FullSync(); err != nil { - return nil, nil, nil, err - } - if err := channelBob.channelState.FullSync(); err != nil { - return nil, nil, nil, err - } - - cleanUpFunc := func() { - os.RemoveAll(bobPath) - os.RemoveAll(alicePath) - - channelAlice.Stop() - channelBob.Stop() - } - - // Now that the channel are open, simulate the start of a session by - // having Alice and Bob extend their revocation windows to each other. - err = initRevocationWindows(channelAlice, channelBob, revocationWindow) - if err != nil { - return nil, nil, nil, err - } - - return channelAlice, channelBob, cleanUpFunc, nil -} - -// calcStaticFee calculates appropriate fees for commitment transactions. This -// function provides a simple way to allow test balance assertions to take fee -// calculations into account. -// -// TODO(bvu): Refactor when dynamic fee estimation is added. -func calcStaticFee(numHTLCs int) btcutil.Amount { - const ( - commitWeight = btcutil.Amount(724) - htlcWeight = 172 - feePerKw = btcutil.Amount(24/4) * 1000 - ) - return feePerKw * (commitWeight + - btcutil.Amount(htlcWeight*numHTLCs)) / 1000 -} - // createHTLC is a utility function for generating an HTLC with a given // preimage and a given amount. func createHTLC(id int, amount lnwire.MilliSatoshi) (*lnwire.UpdateAddHTLC, [32]byte) { @@ -473,7 +101,7 @@ func TestSimpleAddSettleWorkflow(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -783,7 +411,7 @@ func TestCheckCommitTxSize(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -842,7 +470,7 @@ func TestCooperativeChannelClosure(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -909,7 +537,7 @@ func TestForceClose(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(3) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -1048,7 +676,7 @@ func TestForceClose(t *testing.T) { Value: htlcResolution.SweepSignDesc.Output.Value, }) htlcResolution.SweepSignDesc.InputIndex = 0 - sweepTx.TxIn[0].Witness, err = htlcSpendSuccess(aliceChannel.signer, + sweepTx.TxIn[0].Witness, err = htlcSpendSuccess(aliceChannel.Signer, &htlcResolution.SweepSignDesc, sweepTx, uint32(aliceChannel.channelState.LocalChanCfg.CsvDelay)) if err != nil { @@ -1109,7 +737,7 @@ func TestForceClose(t *testing.T) { Value: inHtlcResolution.SweepSignDesc.Output.Value, }) inHtlcResolution.SweepSignDesc.InputIndex = 0 - sweepTx.TxIn[0].Witness, err = htlcSpendSuccess(aliceChannel.signer, + sweepTx.TxIn[0].Witness, err = htlcSpendSuccess(aliceChannel.Signer, &inHtlcResolution.SweepSignDesc, sweepTx, uint32(aliceChannel.channelState.LocalChanCfg.CsvDelay)) if err != nil { @@ -1198,7 +826,7 @@ func TestForceCloseDustOutput(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(3) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -1316,7 +944,7 @@ func TestDustHTLCFees(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(3) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -1393,7 +1021,7 @@ func TestHTLCDustLimit(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(3) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -1478,7 +1106,7 @@ func TestHTLCSigNumber(t *testing.T) { // Create a test channel funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. Alice's dustlimit is 200 sat, while // Bob has 1300 sat. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(3) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -1649,7 +1277,7 @@ func TestChannelBalanceDustLimit(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(3) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -1717,7 +1345,7 @@ func TestStateUpdatePersistence(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -2008,7 +1636,7 @@ func TestCancelHTLC(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(5) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -2122,7 +1750,7 @@ func TestCooperativeCloseDustAdherence(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(5) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -2283,7 +1911,7 @@ func TestCooperativeCloseDustAdherence(t *testing.T) { func TestUpdateFeeAdjustments(t *testing.T) { t.Parallel() - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -2338,7 +1966,7 @@ func TestUpdateFeeAdjustments(t *testing.T) { func TestUpdateFeeFail(t *testing.T) { t.Parallel() - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -2374,7 +2002,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -2486,7 +2114,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -2624,7 +2252,7 @@ func TestUpdateFeeReceiverSendsUpdate(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -2653,7 +2281,7 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -2763,7 +2391,7 @@ func TestAddHTLCNegativeBalance(t *testing.T) { // We'll kick off the test by creating our channels which both are // loaded with 5 BTC each. - aliceChannel, _, cleanUp, err := createTestChannels(1) + aliceChannel, _, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -2844,7 +2472,7 @@ func TestChanSyncFullySynced(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -2964,7 +2592,7 @@ func TestChanSyncOweCommitment(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -3278,7 +2906,7 @@ func TestChanSyncOweRevocation(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -3468,7 +3096,7 @@ func TestChanSyncOweRevocationAndCommit(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -3637,7 +3265,7 @@ func TestChanSyncOweRevocationAndCommitForceTransition(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -3821,7 +3449,7 @@ func TestFeeUpdateRejectInsaneFee(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, _, cleanUp, err := createTestChannels(1) + aliceChannel, _, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -3847,7 +3475,7 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -4032,7 +3660,7 @@ func TestChanSyncUnableToSync(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -4069,7 +3697,7 @@ func TestChanSyncInvalidLastSecret(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -4159,7 +3787,7 @@ func TestChanAvailableBandwidth(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -4282,7 +3910,7 @@ func TestSignCommitmentFailNotLockedIn(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, _, cleanUp, err := createTestChannels(1) + aliceChannel, _, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -4307,7 +3935,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { t.Parallel() // First, we'll make a channel between Alice and Bob. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -4447,7 +4075,7 @@ func TestInvalidCommitSigError(t *testing.T) { t.Parallel() // First, we'll make a channel between Alice and Bob. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -4494,7 +4122,7 @@ func TestChannelUnilateralCloseHtlcResolution(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(3) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -4652,7 +4280,7 @@ func TestDesyncHTLCs(t *testing.T) { // We'll kick off the test by creating our channels which both are // loaded with 5 BTC each. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -4719,7 +4347,7 @@ func TestMaxAcceptedHTLCs(t *testing.T) { // We'll kick off the test by creating our channels which both are // loaded with 5 BTC each. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -4780,7 +4408,7 @@ func TestMaxPendingAmount(t *testing.T) { // We'll kick off the test by creating our channels which both are // loaded with 5 BTC each. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -4838,9 +4466,9 @@ func TestChanReserve(t *testing.T) { t.Parallel() setupChannels := func() (*LightningChannel, *LightningChannel, func()) { - // We'll kick off the test by creating our channels which both - // are loaded with 5 BTC each. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + // We'll kick off the test by creating our channels which both are + // loaded with 5 BTC each. + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -5022,7 +4650,7 @@ func TestMinHTLC(t *testing.T) { // We'll kick off the test by creating our channels which both are // loaded with 5 BTC each. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -5079,7 +4707,7 @@ func TestNewBreachRetributionSkipsDustHtlcs(t *testing.T) { // We'll kick off the test by creating our channels which both are // loaded with 5 BTC each. - aliceChannel, bobChannel, cleanUp, err := createTestChannels(1) + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() if err != nil { t.Fatalf("unable to create test channels: %v", err) } diff --git a/lnwallet/common_test.go b/lnwallet/common_test.go deleted file mode 100644 index 424d34daa..000000000 --- a/lnwallet/common_test.go +++ /dev/null @@ -1,207 +0,0 @@ -package lnwallet - -import ( - "bytes" - "crypto/sha256" - "encoding/hex" - "fmt" - "sync" - - "github.com/roasbeef/btcd/btcec" - "github.com/roasbeef/btcd/chaincfg" - "github.com/roasbeef/btcd/txscript" - "github.com/roasbeef/btcd/wire" - "github.com/roasbeef/btcutil" -) - -// mockSigner is a simple implementation of the Signer interface. Each one has -// a set of private keys in a slice and can sign messages using the appropriate -// one. -type mockSigner struct { - privkeys []*btcec.PrivateKey - netParams *chaincfg.Params -} - -func (m *mockSigner) SignOutputRaw(tx *wire.MsgTx, signDesc *SignDescriptor) ([]byte, error) { - pubkey := signDesc.KeyDesc.PubKey - switch { - case signDesc.SingleTweak != nil: - pubkey = TweakPubKeyWithTweak(pubkey, signDesc.SingleTweak) - case signDesc.DoubleTweak != nil: - pubkey = DeriveRevocationPubkey(pubkey, signDesc.DoubleTweak.PubKey()) - } - - hash160 := btcutil.Hash160(pubkey.SerializeCompressed()) - privKey := m.findKey(hash160, signDesc.SingleTweak, signDesc.DoubleTweak) - if privKey == nil { - return nil, fmt.Errorf("Mock signer does not have key") - } - - sig, err := txscript.RawTxInWitnessSignature(tx, signDesc.SigHashes, - signDesc.InputIndex, signDesc.Output.Value, signDesc.WitnessScript, - txscript.SigHashAll, privKey) - if err != nil { - return nil, err - } - - return sig[:len(sig)-1], nil -} - -func (m *mockSigner) ComputeInputScript(tx *wire.MsgTx, signDesc *SignDescriptor) (*InputScript, error) { - scriptType, addresses, _, err := txscript.ExtractPkScriptAddrs( - signDesc.Output.PkScript, m.netParams) - if err != nil { - return nil, err - } - - switch scriptType { - case txscript.PubKeyHashTy: - privKey := m.findKey(addresses[0].ScriptAddress(), signDesc.SingleTweak, - signDesc.DoubleTweak) - if privKey == nil { - return nil, fmt.Errorf("Mock signer does not have key for "+ - "address %v", addresses[0]) - } - - scriptSig, err := txscript.SignatureScript(tx, signDesc.InputIndex, - signDesc.Output.PkScript, txscript.SigHashAll, privKey, true) - if err != nil { - return nil, err - } - - return &InputScript{ScriptSig: scriptSig}, nil - - case txscript.WitnessV0PubKeyHashTy: - privKey := m.findKey(addresses[0].ScriptAddress(), signDesc.SingleTweak, - signDesc.DoubleTweak) - if privKey == nil { - return nil, fmt.Errorf("Mock signer does not have key for "+ - "address %v", addresses[0]) - } - - witnessScript, err := txscript.WitnessSignature(tx, signDesc.SigHashes, - signDesc.InputIndex, signDesc.Output.Value, - signDesc.Output.PkScript, txscript.SigHashAll, privKey, true) - if err != nil { - return nil, err - } - - return &InputScript{Witness: witnessScript}, nil - - default: - return nil, fmt.Errorf("Unexpected script type: %v", scriptType) - } -} - -// findKey searches through all stored private keys and returns one -// corresponding to the hashed pubkey if it can be found. The public key may -// either correspond directly to the private key or to the private key with a -// tweak applied. -func (m *mockSigner) findKey(needleHash160 []byte, singleTweak []byte, - doubleTweak *btcec.PrivateKey) *btcec.PrivateKey { - - for _, privkey := range m.privkeys { - // First check whether public key is directly derived from private key. - hash160 := btcutil.Hash160(privkey.PubKey().SerializeCompressed()) - if bytes.Equal(hash160, needleHash160) { - return privkey - } - - // Otherwise check if public key is derived from tweaked private key. - switch { - case singleTweak != nil: - privkey = TweakPrivKey(privkey, singleTweak) - case doubleTweak != nil: - privkey = DeriveRevocationPrivKey(privkey, doubleTweak) - default: - continue - } - hash160 = btcutil.Hash160(privkey.PubKey().SerializeCompressed()) - if bytes.Equal(hash160, needleHash160) { - return privkey - } - } - return nil -} - -type mockPreimageCache struct { - sync.Mutex - preimageMap map[[32]byte][]byte -} - -func (m *mockPreimageCache) LookupPreimage(hash []byte) ([]byte, bool) { - m.Lock() - defer m.Unlock() - - var h [32]byte - copy(h[:], hash) - - p, ok := m.preimageMap[h] - return p, ok -} - -func (m *mockPreimageCache) AddPreimage(preimage []byte) error { - m.Lock() - defer m.Unlock() - - m.preimageMap[sha256.Sum256(preimage[:])] = preimage - - return nil -} - -// pubkeyFromHex parses a Bitcoin public key from a hex encoded string. -func pubkeyFromHex(keyHex string) (*btcec.PublicKey, error) { - bytes, err := hex.DecodeString(keyHex) - if err != nil { - return nil, err - } - return btcec.ParsePubKey(bytes, btcec.S256()) -} - -// privkeyFromHex parses a Bitcoin private key from a hex encoded string. -func privkeyFromHex(keyHex string) (*btcec.PrivateKey, error) { - bytes, err := hex.DecodeString(keyHex) - if err != nil { - return nil, err - } - key, _ := btcec.PrivKeyFromBytes(btcec.S256(), bytes) - return key, nil - -} - -// pubkeyToHex serializes a Bitcoin public key to a hex encoded string. -func pubkeyToHex(key *btcec.PublicKey) string { - return hex.EncodeToString(key.SerializeCompressed()) -} - -// privkeyFromHex serializes a Bitcoin private key to a hex encoded string. -func privkeyToHex(key *btcec.PrivateKey) string { - return hex.EncodeToString(key.Serialize()) -} - -// signatureFromHex parses a Bitcoin signature from a hex encoded string. -func signatureFromHex(sigHex string) (*btcec.Signature, error) { - bytes, err := hex.DecodeString(sigHex) - if err != nil { - return nil, err - } - return btcec.ParseSignature(bytes, btcec.S256()) -} - -// blockFromHex parses a full Bitcoin block from a hex encoded string. -func blockFromHex(blockHex string) (*btcutil.Block, error) { - bytes, err := hex.DecodeString(blockHex) - if err != nil { - return nil, err - } - return btcutil.NewBlockFromBytes(bytes) -} - -// txFromHex parses a full Bitcoin transaction from a hex encoded string. -func txFromHex(txHex string) (*btcutil.Tx, error) { - bytes, err := hex.DecodeString(txHex) - if err != nil { - return nil, err - } - return btcutil.NewTxFromBytes(bytes) -} diff --git a/lnwallet/test_utils.go b/lnwallet/test_utils.go new file mode 100644 index 000000000..27ee491f3 --- /dev/null +++ b/lnwallet/test_utils.go @@ -0,0 +1,592 @@ +package lnwallet + +import ( + "bytes" + "crypto/rand" + "crypto/sha256" + "encoding/binary" + "encoding/hex" + "fmt" + "io" + "io/ioutil" + "os" + "sync" + + "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/shachain" + "github.com/roasbeef/btcd/btcec" + "github.com/roasbeef/btcd/chaincfg" + "github.com/roasbeef/btcd/chaincfg/chainhash" + "github.com/roasbeef/btcd/txscript" + "github.com/roasbeef/btcd/wire" + "github.com/roasbeef/btcutil" +) + +var ( + privPass = []byte("private-test") + + // For simplicity a single priv key controls all of our test outputs. + testWalletPrivKey = []byte{ + 0x2b, 0xd8, 0x06, 0xc9, 0x7f, 0x0e, 0x00, 0xaf, + 0x1a, 0x1f, 0xc3, 0x32, 0x8f, 0xa7, 0x63, 0xa9, + 0x26, 0x97, 0x23, 0xc8, 0xdb, 0x8f, 0xac, 0x4f, + 0x93, 0xaf, 0x71, 0xdb, 0x18, 0x6d, 0x6e, 0x90, + } + + // We're alice :) + bobsPrivKey = []byte{ + 0x81, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda, + 0x63, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17, + 0xd, 0xe7, 0x95, 0xe4, 0xb7, 0x25, 0xb8, 0x4d, + 0x1e, 0xb, 0x4c, 0xfd, 0x9e, 0xc5, 0x8c, 0xe9, + } + + // Use a hard-coded HD seed. + testHdSeed = chainhash.Hash{ + 0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab, + 0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4, + 0x4f, 0x2f, 0x6f, 0x25, 0x88, 0xa3, 0xef, 0xb9, + 0x6a, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53, + } + + // The number of confirmations required to consider any created channel + // open. + numReqConfs = uint16(1) + + // A serializable txn for testing funding txn. + testTx = &wire.MsgTx{ + Version: 1, + TxIn: []*wire.TxIn{ + { + PreviousOutPoint: wire.OutPoint{ + Hash: chainhash.Hash{}, + Index: 0xffffffff, + }, + SignatureScript: []byte{0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62}, + Sequence: 0xffffffff, + }, + }, + TxOut: []*wire.TxOut{ + { + Value: 5000000000, + PkScript: []byte{ + 0x41, // OP_DATA_65 + 0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5, + 0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42, + 0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1, + 0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24, + 0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97, + 0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78, + 0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20, + 0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63, + 0xa6, // 65-byte signature + 0xac, // OP_CHECKSIG + }, + }, + }, + LockTime: 5, + } +) + +// CreateTestChannels creates to fully populated channels to be used within +// testing fixtures. The channels will be returned as if the funding process +// has just completed. The channel itself is funded with 10 BTC, with 5 BTC +// allocated to each side. Within the channel, Alice is the initiator. The +// function also returns a "cleanup" function that is meant to be called once +// the test has been finalized. The clean up function will remote all temporary +// files created +func CreateTestChannels() (*LightningChannel, *LightningChannel, func(), error) { + channelCapacity, err := btcutil.NewAmount(10) + if err != nil { + return nil, nil, nil, err + } + + channelBal := channelCapacity / 2 + aliceDustLimit := btcutil.Amount(200) + bobDustLimit := btcutil.Amount(1300) + csvTimeoutAlice := uint32(5) + csvTimeoutBob := uint32(4) + + prevOut := &wire.OutPoint{ + Hash: chainhash.Hash(testHdSeed), + Index: 0, + } + fundingTxIn := wire.NewTxIn(prevOut, nil, nil) + + // For each party, we'll create a distinct set of keys in order to + // emulate the typical set up with live channels. + var ( + aliceKeys []*btcec.PrivateKey + bobKeys []*btcec.PrivateKey + ) + for i := 0; i < 5; i++ { + key := make([]byte, len(testWalletPrivKey)) + copy(key[:], testWalletPrivKey[:]) + key[0] ^= byte(i + 1) + + aliceKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), key) + aliceKeys = append(aliceKeys, aliceKey) + + key = make([]byte, len(bobsPrivKey)) + copy(key[:], bobsPrivKey) + key[0] ^= byte(i + 1) + + bobKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), key) + bobKeys = append(bobKeys, bobKey) + } + + aliceCfg := channeldb.ChannelConfig{ + ChannelConstraints: channeldb.ChannelConstraints{ + DustLimit: aliceDustLimit, + MaxPendingAmount: lnwire.NewMSatFromSatoshis(channelCapacity), + ChanReserve: channelCapacity / 100, + MinHTLC: 0, + MaxAcceptedHtlcs: MaxHTLCNumber / 2, + }, + CsvDelay: uint16(csvTimeoutAlice), + MultiSigKey: keychain.KeyDescriptor{ + PubKey: aliceKeys[0].PubKey(), + }, + RevocationBasePoint: keychain.KeyDescriptor{ + PubKey: aliceKeys[1].PubKey(), + }, + PaymentBasePoint: keychain.KeyDescriptor{ + PubKey: aliceKeys[2].PubKey(), + }, + DelayBasePoint: keychain.KeyDescriptor{ + PubKey: aliceKeys[3].PubKey(), + }, + HtlcBasePoint: keychain.KeyDescriptor{ + PubKey: aliceKeys[4].PubKey(), + }, + } + bobCfg := channeldb.ChannelConfig{ + ChannelConstraints: channeldb.ChannelConstraints{ + DustLimit: bobDustLimit, + MaxPendingAmount: lnwire.NewMSatFromSatoshis(channelCapacity), + ChanReserve: channelCapacity / 100, + MinHTLC: 0, + MaxAcceptedHtlcs: MaxHTLCNumber / 2, + }, + CsvDelay: uint16(csvTimeoutBob), + MultiSigKey: keychain.KeyDescriptor{ + PubKey: bobKeys[0].PubKey(), + }, + RevocationBasePoint: keychain.KeyDescriptor{ + PubKey: bobKeys[1].PubKey(), + }, + PaymentBasePoint: keychain.KeyDescriptor{ + PubKey: bobKeys[2].PubKey(), + }, + DelayBasePoint: keychain.KeyDescriptor{ + PubKey: bobKeys[3].PubKey(), + }, + HtlcBasePoint: keychain.KeyDescriptor{ + PubKey: bobKeys[4].PubKey(), + }, + } + + bobRoot, err := chainhash.NewHash(bobKeys[0].Serialize()) + if err != nil { + return nil, nil, nil, err + } + bobPreimageProducer := shachain.NewRevocationProducer(*bobRoot) + bobFirstRevoke, err := bobPreimageProducer.AtIndex(0) + if err != nil { + return nil, nil, nil, err + } + bobCommitPoint := ComputeCommitmentPoint(bobFirstRevoke[:]) + + aliceRoot, err := chainhash.NewHash(aliceKeys[0].Serialize()) + if err != nil { + return nil, nil, nil, err + } + alicePreimageProducer := shachain.NewRevocationProducer(*aliceRoot) + aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0) + if err != nil { + return nil, nil, nil, err + } + aliceCommitPoint := ComputeCommitmentPoint(aliceFirstRevoke[:]) + + aliceCommitTx, bobCommitTx, err := CreateCommitmentTxns(channelBal, + channelBal, &aliceCfg, &bobCfg, aliceCommitPoint, bobCommitPoint, + *fundingTxIn) + if err != nil { + return nil, nil, nil, err + } + + alicePath, err := ioutil.TempDir("", "alicedb") + dbAlice, err := channeldb.Open(alicePath) + if err != nil { + return nil, nil, nil, err + } + + bobPath, err := ioutil.TempDir("", "bobdb") + dbBob, err := channeldb.Open(bobPath) + if err != nil { + return nil, nil, nil, err + } + + estimator := &StaticFeeEstimator{24} + feePerVSize, err := estimator.EstimateFeePerVSize(1) + if err != nil { + return nil, nil, nil, err + } + feePerKw := feePerVSize.FeePerKWeight() + commitFee := calcStaticFee(0) + + aliceCommit := channeldb.ChannelCommitment{ + CommitHeight: 0, + LocalBalance: lnwire.NewMSatFromSatoshis(channelBal - commitFee), + RemoteBalance: lnwire.NewMSatFromSatoshis(channelBal), + CommitFee: commitFee, + FeePerKw: btcutil.Amount(feePerKw), + CommitTx: aliceCommitTx, + CommitSig: bytes.Repeat([]byte{1}, 71), + } + bobCommit := channeldb.ChannelCommitment{ + CommitHeight: 0, + LocalBalance: lnwire.NewMSatFromSatoshis(channelBal), + RemoteBalance: lnwire.NewMSatFromSatoshis(channelBal - commitFee), + CommitFee: commitFee, + FeePerKw: btcutil.Amount(feePerKw), + CommitTx: bobCommitTx, + CommitSig: bytes.Repeat([]byte{1}, 71), + } + + var chanIDBytes [8]byte + if _, err := io.ReadFull(rand.Reader, chanIDBytes[:]); err != nil { + return nil, nil, nil, err + } + + shortChanID := lnwire.NewShortChanIDFromInt( + binary.BigEndian.Uint64(chanIDBytes[:]), + ) + + aliceChannelState := &channeldb.OpenChannel{ + LocalChanCfg: aliceCfg, + RemoteChanCfg: bobCfg, + IdentityPub: aliceKeys[0].PubKey(), + FundingOutpoint: *prevOut, + ShortChanID: shortChanID, + ChanType: channeldb.SingleFunder, + IsInitiator: true, + Capacity: channelCapacity, + RemoteCurrentRevocation: bobCommitPoint, + RevocationProducer: alicePreimageProducer, + RevocationStore: shachain.NewRevocationStore(), + LocalCommitment: aliceCommit, + RemoteCommitment: aliceCommit, + Db: dbAlice, + Packager: channeldb.NewChannelPackager(shortChanID), + FundingTxn: testTx, + } + bobChannelState := &channeldb.OpenChannel{ + LocalChanCfg: bobCfg, + RemoteChanCfg: aliceCfg, + IdentityPub: bobKeys[0].PubKey(), + FundingOutpoint: *prevOut, + ShortChanID: shortChanID, + ChanType: channeldb.SingleFunder, + IsInitiator: false, + Capacity: channelCapacity, + RemoteCurrentRevocation: aliceCommitPoint, + RevocationProducer: bobPreimageProducer, + RevocationStore: shachain.NewRevocationStore(), + LocalCommitment: bobCommit, + RemoteCommitment: bobCommit, + Db: dbBob, + Packager: channeldb.NewChannelPackager(shortChanID), + } + + aliceSigner := &mockSigner{privkeys: aliceKeys} + bobSigner := &mockSigner{privkeys: bobKeys} + + pCache := &mockPreimageCache{ + // hash -> preimage + preimageMap: make(map[[32]byte][]byte), + } + + // TODO(roasbeef): make mock version of pre-image store + channelAlice, err := NewLightningChannel( + aliceSigner, pCache, aliceChannelState, + ) + if err != nil { + return nil, nil, nil, err + } + channelBob, err := NewLightningChannel( + bobSigner, pCache, bobChannelState, + ) + if err != nil { + return nil, nil, nil, err + } + + err = SetStateNumHint( + aliceCommitTx, 0, channelAlice.stateHintObfuscator, + ) + if err != nil { + return nil, nil, nil, err + } + err = SetStateNumHint( + bobCommitTx, 0, channelAlice.stateHintObfuscator, + ) + if err != nil { + return nil, nil, nil, err + } + + if err := channelAlice.channelState.FullSync(); err != nil { + return nil, nil, nil, err + } + if err := channelBob.channelState.FullSync(); err != nil { + return nil, nil, nil, err + } + + cleanUpFunc := func() { + os.RemoveAll(bobPath) + os.RemoveAll(alicePath) + + channelAlice.Stop() + channelBob.Stop() + } + + // Now that the channel are open, simulate the start of a session by + // having Alice and Bob extend their revocation windows to each other. + err = initRevocationWindows(channelAlice, channelBob) + if err != nil { + return nil, nil, nil, err + } + + return channelAlice, channelBob, cleanUpFunc, nil +} + +// initRevocationWindows simulates a new channel being opened within the p2p +// network by populating the initial revocation windows of the passed +// commitment state machines. +func initRevocationWindows(chanA, chanB *LightningChannel) error { + aliceNextRevoke, err := chanA.NextRevocationKey() + if err != nil { + return err + } + if err := chanB.InitNextRevocation(aliceNextRevoke); err != nil { + return err + } + + bobNextRevoke, err := chanB.NextRevocationKey() + if err != nil { + return err + } + if err := chanA.InitNextRevocation(bobNextRevoke); err != nil { + return err + } + + return nil +} + +// mockSigner is a simple implementation of the Signer interface. Each one has +// a set of private keys in a slice and can sign messages using the appropriate +// one. +type mockSigner struct { + privkeys []*btcec.PrivateKey + netParams *chaincfg.Params +} + +func (m *mockSigner) SignOutputRaw(tx *wire.MsgTx, signDesc *SignDescriptor) ([]byte, error) { + pubkey := signDesc.KeyDesc.PubKey + switch { + case signDesc.SingleTweak != nil: + pubkey = TweakPubKeyWithTweak(pubkey, signDesc.SingleTweak) + case signDesc.DoubleTweak != nil: + pubkey = DeriveRevocationPubkey(pubkey, signDesc.DoubleTweak.PubKey()) + } + + hash160 := btcutil.Hash160(pubkey.SerializeCompressed()) + privKey := m.findKey(hash160, signDesc.SingleTweak, signDesc.DoubleTweak) + if privKey == nil { + return nil, fmt.Errorf("Mock signer does not have key") + } + + sig, err := txscript.RawTxInWitnessSignature(tx, signDesc.SigHashes, + signDesc.InputIndex, signDesc.Output.Value, signDesc.WitnessScript, + txscript.SigHashAll, privKey) + if err != nil { + return nil, err + } + + return sig[:len(sig)-1], nil +} + +func (m *mockSigner) ComputeInputScript(tx *wire.MsgTx, signDesc *SignDescriptor) (*InputScript, error) { + scriptType, addresses, _, err := txscript.ExtractPkScriptAddrs( + signDesc.Output.PkScript, m.netParams) + if err != nil { + return nil, err + } + + switch scriptType { + case txscript.PubKeyHashTy: + privKey := m.findKey(addresses[0].ScriptAddress(), signDesc.SingleTweak, + signDesc.DoubleTweak) + if privKey == nil { + return nil, fmt.Errorf("Mock signer does not have key for "+ + "address %v", addresses[0]) + } + + scriptSig, err := txscript.SignatureScript(tx, signDesc.InputIndex, + signDesc.Output.PkScript, txscript.SigHashAll, privKey, true) + if err != nil { + return nil, err + } + + return &InputScript{ScriptSig: scriptSig}, nil + + case txscript.WitnessV0PubKeyHashTy: + privKey := m.findKey(addresses[0].ScriptAddress(), signDesc.SingleTweak, + signDesc.DoubleTweak) + if privKey == nil { + return nil, fmt.Errorf("Mock signer does not have key for "+ + "address %v", addresses[0]) + } + + witnessScript, err := txscript.WitnessSignature(tx, signDesc.SigHashes, + signDesc.InputIndex, signDesc.Output.Value, + signDesc.Output.PkScript, txscript.SigHashAll, privKey, true) + if err != nil { + return nil, err + } + + return &InputScript{Witness: witnessScript}, nil + + default: + return nil, fmt.Errorf("Unexpected script type: %v", scriptType) + } +} + +// findKey searches through all stored private keys and returns one +// corresponding to the hashed pubkey if it can be found. The public key may +// either correspond directly to the private key or to the private key with a +// tweak applied. +func (m *mockSigner) findKey(needleHash160 []byte, singleTweak []byte, + doubleTweak *btcec.PrivateKey) *btcec.PrivateKey { + + for _, privkey := range m.privkeys { + // First check whether public key is directly derived from private key. + hash160 := btcutil.Hash160(privkey.PubKey().SerializeCompressed()) + if bytes.Equal(hash160, needleHash160) { + return privkey + } + + // Otherwise check if public key is derived from tweaked private key. + switch { + case singleTweak != nil: + privkey = TweakPrivKey(privkey, singleTweak) + case doubleTweak != nil: + privkey = DeriveRevocationPrivKey(privkey, doubleTweak) + default: + continue + } + hash160 = btcutil.Hash160(privkey.PubKey().SerializeCompressed()) + if bytes.Equal(hash160, needleHash160) { + return privkey + } + } + return nil +} + +type mockPreimageCache struct { + sync.Mutex + preimageMap map[[32]byte][]byte +} + +func (m *mockPreimageCache) LookupPreimage(hash []byte) ([]byte, bool) { + m.Lock() + defer m.Unlock() + + var h [32]byte + copy(h[:], hash) + + p, ok := m.preimageMap[h] + return p, ok +} + +func (m *mockPreimageCache) AddPreimage(preimage []byte) error { + m.Lock() + defer m.Unlock() + + m.preimageMap[sha256.Sum256(preimage[:])] = preimage + + return nil +} + +// pubkeyFromHex parses a Bitcoin public key from a hex encoded string. +func pubkeyFromHex(keyHex string) (*btcec.PublicKey, error) { + bytes, err := hex.DecodeString(keyHex) + if err != nil { + return nil, err + } + return btcec.ParsePubKey(bytes, btcec.S256()) +} + +// privkeyFromHex parses a Bitcoin private key from a hex encoded string. +func privkeyFromHex(keyHex string) (*btcec.PrivateKey, error) { + bytes, err := hex.DecodeString(keyHex) + if err != nil { + return nil, err + } + key, _ := btcec.PrivKeyFromBytes(btcec.S256(), bytes) + return key, nil + +} + +// pubkeyToHex serializes a Bitcoin public key to a hex encoded string. +func pubkeyToHex(key *btcec.PublicKey) string { + return hex.EncodeToString(key.SerializeCompressed()) +} + +// privkeyFromHex serializes a Bitcoin private key to a hex encoded string. +func privkeyToHex(key *btcec.PrivateKey) string { + return hex.EncodeToString(key.Serialize()) +} + +// signatureFromHex parses a Bitcoin signature from a hex encoded string. +func signatureFromHex(sigHex string) (*btcec.Signature, error) { + bytes, err := hex.DecodeString(sigHex) + if err != nil { + return nil, err + } + return btcec.ParseSignature(bytes, btcec.S256()) +} + +// blockFromHex parses a full Bitcoin block from a hex encoded string. +func blockFromHex(blockHex string) (*btcutil.Block, error) { + bytes, err := hex.DecodeString(blockHex) + if err != nil { + return nil, err + } + return btcutil.NewBlockFromBytes(bytes) +} + +// txFromHex parses a full Bitcoin transaction from a hex encoded string. +func txFromHex(txHex string) (*btcutil.Tx, error) { + bytes, err := hex.DecodeString(txHex) + if err != nil { + return nil, err + } + return btcutil.NewTxFromBytes(bytes) +} + +// calcStaticFee calculates appropriate fees for commitment transactions. This +// function provides a simple way to allow test balance assertions to take fee +// calculations into account. +// +// TODO(bvu): Refactor when dynamic fee estimation is added. +func calcStaticFee(numHTLCs int) btcutil.Amount { + const ( + commitWeight = btcutil.Amount(724) + htlcWeight = 172 + feePerKw = btcutil.Amount(24/4) * 1000 + ) + return feePerKw * (commitWeight + + btcutil.Amount(htlcWeight*numHTLCs)) / 1000 +} From 8b068174822b1c1520003951d7faeda967205092 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sun, 29 Apr 2018 15:40:59 -0700 Subject: [PATCH 3/7] lnwallet: modify NewUnilateralCloseSummary to be aware of pending remote commits In this commit, we modify the NewUnilateralCloseSummary to be able to distinguish between a unilateral closure using the lowest+highest commitment the remote party possesses. Before this commit, if the remote party broadcast their highest commitment, when they have a lower unrevoked commitment, then this function would fail to find the proper output, leaving funds on the chain. To fix this, it's now the duty of the caller to pass remotePendingCommit with the proper value. The caller should use the lowest unrevoked commitment, and the height hint of the broadcast commitment to discern if this is a pending commitment or not. --- lnwallet/channel.go | 30 ++++++++++++++++++++---------- lnwallet/channel_test.go | 18 +++++++++--------- lnwallet/transactions_test.go | 2 +- 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 34039db0f..2cf85c1e6 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -1234,10 +1234,10 @@ func compactLogs(ourLog, theirLog *updateLog, // // See the individual comments within the above methods for further details. type LightningChannel struct { - // signer is the main signer instances that will be responsible for + // Signer is the main signer instances that will be responsible for // signing any HTLC and commitment transaction generated by the state // machine. - signer Signer + Signer Signer // signDesc is the primary sign descriptor that is capable of signing // the commitment transaction that spends the multi-sig output. @@ -1350,7 +1350,7 @@ func NewLightningChannel(signer Signer, pCache PreimageCache, lc := &LightningChannel{ // TODO(roasbeef): tune num sig workers? sigPool: newSigPool(runtime.NumCPU(), signer), - signer: signer, + Signer: signer, pCache: pCache, currentHeight: localCommit.CommitHeight, remoteCommitChain: newCommitmentChain(remoteCommit.CommitHeight), @@ -2920,7 +2920,7 @@ func (lc *LightningChannel) SignNextCommitment() (lnwire.Sig, []lnwire.Sig, erro // the new commitment transaction while we're waiting for the rest of // the HTLC signatures to be processed. lc.signDesc.SigHashes = txscript.NewTxSigHashes(newCommitView.txn) - rawSig, err := lc.signer.SignOutputRaw(newCommitView.txn, lc.signDesc) + rawSig, err := lc.Signer.SignOutputRaw(newCommitView.txn, lc.signDesc) if err != nil { close(cancelChan) return sig, htlcSigs, err @@ -4601,7 +4601,7 @@ func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) { // With this, we then generate the full witness so the caller can // broadcast a fully signed transaction. lc.signDesc.SigHashes = txscript.NewTxSigHashes(commitTx) - ourSigRaw, err := lc.signer.SignOutputRaw(commitTx, lc.signDesc) + ourSigRaw, err := lc.Signer.SignOutputRaw(commitTx, lc.signDesc) if err != nil { return nil, err } @@ -4678,14 +4678,24 @@ type UnilateralCloseSummary struct { // NewUnilateralCloseSummary creates a new summary that provides the caller // with all the information required to claim all funds on chain in the event -// that the remote party broadcasts their commitment. +// that the remote party broadcasts their commitment. If the +// remotePendingCommit value is set to true, then we'll use the next (second) +// unrevoked commitment point to construct the summary. Otherwise, we assume +// that the remote party broadcast the lower of their two possible commits. func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer Signer, pCache PreimageCache, commitSpend *chainntnfs.SpendDetail, - remoteCommit channeldb.ChannelCommitment) (*UnilateralCloseSummary, error) { + remoteCommit channeldb.ChannelCommitment, + remotePendingCommit bool) (*UnilateralCloseSummary, error) { // First, we'll generate the commitment point and the revocation point - // so we can re-construct the HTLC state and also our payment key. + // so we can re-construct the HTLC state and also our payment key. If + // this is the pending remote commitment, then we'll use the second + // unrevoked commit point in order to properly reconstruct the scripts + // we need to locate. commitPoint := chanState.RemoteCurrentRevocation + if remotePendingCommit { + commitPoint = chanState.RemoteNextRevocation + } keyRing := deriveCommitmentKeys( commitPoint, false, &chanState.LocalChanCfg, &chanState.RemoteChanCfg, @@ -5258,7 +5268,7 @@ func (lc *LightningChannel) ForceClose() (*LocalForceCloseSummary, error) { localCommitment := lc.channelState.LocalCommitment summary, err := NewLocalForceCloseSummary(lc.channelState, - lc.signer, lc.pCache, commitTx, localCommitment) + lc.Signer, lc.pCache, commitTx, localCommitment) if err != nil { return nil, err } @@ -5427,7 +5437,7 @@ func (lc *LightningChannel) CreateCloseProposal(proposedFee btcutil.Amount, // using the generated txid to be notified once the closure transaction // has been confirmed. lc.signDesc.SigHashes = txscript.NewTxSigHashes(closeTx) - sig, err := lc.signer.SignOutputRaw(closeTx, lc.signDesc) + sig, err := lc.Signer.SignOutputRaw(closeTx, lc.signDesc) if err != nil { return nil, nil, 0, err } diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index e788532ba..becdbfecd 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -1443,13 +1443,13 @@ func TestStateUpdatePersistence(t *testing.T) { t.Fatalf("unable to fetch channel: %v", err) } aliceChannelNew, err := NewLightningChannel( - aliceChannel.signer, nil, aliceChannels[0], + aliceChannel.Signer, nil, aliceChannels[0], ) if err != nil { t.Fatalf("unable to create new channel: %v", err) } bobChannelNew, err := NewLightningChannel( - bobChannel.signer, nil, bobChannels[0], + bobChannel.Signer, nil, bobChannels[0], ) if err != nil { t.Fatalf("unable to create new channel: %v", err) @@ -2543,14 +2543,14 @@ func TestChanSyncFullySynced(t *testing.T) { t.Fatalf("unable to fetch channel: %v", err) } aliceChannelNew, err := NewLightningChannel( - aliceChannel.signer, nil, aliceChannels[0], + aliceChannel.Signer, nil, aliceChannels[0], ) if err != nil { t.Fatalf("unable to create new channel: %v", err) } defer aliceChannelNew.Stop() bobChannelNew, err := NewLightningChannel( - bobChannel.signer, nil, bobChannels[0], + bobChannel.Signer, nil, bobChannels[0], ) if err != nil { t.Fatalf("unable to create new channel: %v", err) @@ -2573,7 +2573,7 @@ func restartChannel(channelOld *LightningChannel) (*LightningChannel, error) { } channelNew, err := NewLightningChannel( - channelOld.signer, channelOld.pCache, nodeChannels[0], + channelOld.Signer, channelOld.pCache, nodeChannels[0], ) if err != nil { return nil, err @@ -4173,8 +4173,8 @@ func TestChannelUnilateralCloseHtlcResolution(t *testing.T) { SpenderTxHash: &commitTxHash, } aliceCloseSummary, err := NewUnilateralCloseSummary( - aliceChannel.channelState, aliceChannel.signer, aliceChannel.pCache, - spendDetail, aliceChannel.channelState.RemoteCommitment, + aliceChannel.channelState, aliceChannel.Signer, aliceChannel.pCache, + spendDetail, aliceChannel.channelState.RemoteCommitment, false, ) if err != nil { t.Fatalf("unable to create alice close summary: %v", err) @@ -4216,7 +4216,7 @@ func TestChannelUnilateralCloseHtlcResolution(t *testing.T) { // With the transaction constructed, we'll generate a witness that // should be valid for it, and verify using an instance of Script. sweepTx.TxIn[0].Witness, err = receiverHtlcSpendTimeout( - aliceChannel.signer, &outHtlcResolution.SweepSignDesc, + aliceChannel.Signer, &outHtlcResolution.SweepSignDesc, sweepTx, int32(outHtlcResolution.Expiry), ) if err != nil { @@ -4250,7 +4250,7 @@ func TestChannelUnilateralCloseHtlcResolution(t *testing.T) { sweepTx, ) sweepTx.TxIn[0].Witness, err = SenderHtlcSpendRedeem( - aliceChannel.signer, &inHtlcResolution.SweepSignDesc, + aliceChannel.Signer, &inHtlcResolution.SweepSignDesc, sweepTx, preimageBob[:], ) if err != nil { diff --git a/lnwallet/transactions_test.go b/lnwallet/transactions_test.go index 42d5859cc..3ab47f2d9 100644 --- a/lnwallet/transactions_test.go +++ b/lnwallet/transactions_test.go @@ -419,7 +419,7 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) { // of the dependencies. channel := LightningChannel{ channelState: &channelState, - signer: signer, + Signer: signer, localChanCfg: &channelState.LocalChanCfg, remoteChanCfg: &channelState.RemoteChanCfg, } From 88ff2af931a0364cff69d2e79b3613c2a8eec545 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sun, 29 Apr 2018 15:41:16 -0700 Subject: [PATCH 4/7] lnwallet: add new test to exercise creation of unilateral close summaries for pending broadcast commitments --- lnwallet/channel_test.go | 115 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index becdbfecd..69d10d1cc 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -4273,6 +4273,121 @@ func TestChannelUnilateralCloseHtlcResolution(t *testing.T) { } } +// TestChannelUnilateralClosePendingCommit tests that if the remote party +// broadcasts their pending commit (hasn't yet revoked the lower one), then +// we'll create a proper unilateral channel clsoure that can sweep the created +// outputs. +func TestChannelUnilateralClosePendingCommit(t *testing.T) { + t.Parallel() + + // Create a test channel which will be used for the duration of this + // unittest. The channel will be funded evenly with Alice having 5 BTC, + // and Bob having 5 BTC. + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + if err != nil { + t.Fatalf("unable to create test channels: %v", err) + } + defer cleanUp() + + // First, we'll add an HTLC from Alice to Bob, just to be be able to + // create a new state transition. + htlcAmount := lnwire.NewMSatFromSatoshis(20000) + htlcAlice, _ := createHTLC(0, htlcAmount) + if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil { + t.Fatalf("alice unable to add htlc: %v", err) + } + if _, err := bobChannel.ReceiveHTLC(htlcAlice); err != nil { + t.Fatalf("bob unable to recv add htlc: %v", err) + } + + // With the HTLC added, we'll now manually initiate a state transition + // from Alice to Bob. + _, _, err = aliceChannel.SignNextCommitment() + if err != nil { + t.Fatal(err) + } + + // At this point, Alice's commitment chain should have a new pending + // commit for Bob. We'll extract it so we can simulate Bob broadcasting + // the commitment due to an issue. + bobCommit := aliceChannel.remoteCommitChain.tip().txn + bobTxHash := bobCommit.TxHash() + spendDetail := &chainntnfs.SpendDetail{ + SpenderTxHash: &bobTxHash, + SpendingTx: bobCommit, + } + + // At this point, if we attempt to create a unilateral close summary + // using this commitment, but with the wrong state, we should find that + // our output wasn't picked up. + aliceWrongCloseSummary, err := NewUnilateralCloseSummary( + aliceChannel.channelState, aliceChannel.Signer, aliceChannel.pCache, + spendDetail, aliceChannel.channelState.RemoteCommitment, false, + ) + if err != nil { + t.Fatalf("unable to create alice close summary: %v", err) + } + + if aliceWrongCloseSummary.CommitResolution != nil { + t.Fatalf("alice shouldn't have found self output") + } + + // If we create the close summary again, but this time use Alice's + // pending commit to Bob, then the unilateral close summary should be + // properly populated. + aliceRemoteChainTip, err := aliceChannel.channelState.RemoteCommitChainTip() + if err != nil { + t.Fatalf("unable to fetch remote chain tip: %v", err) + } + aliceCloseSummary, err := NewUnilateralCloseSummary( + aliceChannel.channelState, aliceChannel.Signer, aliceChannel.pCache, + spendDetail, aliceRemoteChainTip.Commitment, true, + ) + if err != nil { + t.Fatalf("unable to create alice close summary: %v", err) + } + + // With this proper version, Alice's commit resolution should have been + // properly located. + if aliceCloseSummary.CommitResolution == nil { + t.Fatalf("unable to find alice's commit resolution") + } + + aliceSignDesc := aliceCloseSummary.CommitResolution.SelfOutputSignDesc + + // Finally, we'll ensure that we're able to properly sweep our output + // from using the materials within the unilateral close summary. + sweepTx := wire.NewMsgTx(2) + sweepTx.AddTxIn(&wire.TxIn{ + PreviousOutPoint: aliceCloseSummary.CommitResolution.SelfOutPoint, + }) + sweepTx.AddTxOut(&wire.TxOut{ + PkScript: testHdSeed[:], + Value: aliceSignDesc.Output.Value, + }) + aliceSignDesc.SigHashes = txscript.NewTxSigHashes(sweepTx) + sweepTx.TxIn[0].Witness, err = CommitSpendNoDelay( + aliceChannel.Signer, &aliceSignDesc, sweepTx, + ) + if err != nil { + t.Fatalf("unable to generate sweep witness: %v", err) + } + + // If we validate the signature on the new sweep transaction, it should + // be fully valid. + vm, err := txscript.NewEngine( + aliceSignDesc.Output.PkScript, + sweepTx, 0, txscript.StandardVerifyFlags, nil, + nil, aliceSignDesc.Output.Value, + ) + if err != nil { + t.Fatalf("unable to create engine: %v", err) + } + if err := vm.Execute(); err != nil { + t.Fatalf("htlc timeout spend is invalid: %v", err) + } +} + // TestDesyncHTLCs checks that we cannot add HTLCs that would make the // balance negative, when the remote and local update logs are desynced. func TestDesyncHTLCs(t *testing.T) { From c8b15719f217d577b6528e1eaf4d657ac594bb5d Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sun, 29 Apr 2018 15:42:01 -0700 Subject: [PATCH 5/7] contractcourt: ensure the chainWatcher is able to play all remote commitments --- contractcourt/chain_watcher.go | 69 +++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/contractcourt/chain_watcher.go b/contractcourt/chain_watcher.go index cf35d5588..897f7b6fd 100644 --- a/contractcourt/chain_watcher.go +++ b/contractcourt/chain_watcher.go @@ -340,40 +340,71 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) { ) remoteStateNum := remoteCommit.CommitHeight + remoteChainTip, err := c.cfg.chanState.RemoteCommitChainTip() + if err != nil && err != channeldb.ErrNoPendingCommit { + log.Errorf("unable to obtain chain tip for "+ + "ChannelPoint(%v): %v", + c.cfg.chanState.FundingOutpoint, err) + return + } + switch { // If state number spending transaction matches the // current latest state, then they've initiated a // unilateral close. So we'll trigger the unilateral // close signal so subscribers can clean up the state // as necessary. - // + case broadcastStateNum == remoteStateNum: + err := c.dispatchRemoteForceClose( + commitSpend, *remoteCommit, false, + ) + if err != nil { + log.Errorf("unable to handle remote "+ + "close for chan_point=%v: %v", + c.cfg.chanState.FundingOutpoint, err) + } + // We'll also handle the case of the remote party // broadcasting their commitment transaction which is // one height above ours. This case can arise when we // initiate a state transition, but the remote party // has a fail crash _after_ accepting the new state, // but _before_ sending their signature to us. - case broadcastStateNum >= remoteStateNum: - if err := c.dispatchRemoteForceClose( - commitSpend, *remoteCommit, - ); err != nil { + case broadcastStateNum == remoteStateNum+1 && + remoteChainTip != nil: + + err := c.dispatchRemoteForceClose( + commitSpend, remoteChainTip.Commitment, + true, + ) + if err != nil { log.Errorf("unable to handle remote "+ "close for chan_point=%v: %v", c.cfg.chanState.FundingOutpoint, err) } + // This is the case that somehow the commitment + // broadcast is actually greater than even one beyond + // our best known state number. This should NEVER + // happen, but we'll log it in any case. + case broadcastStateNum > remoteStateNum+1: + log.Errorf("Remote node broadcast state #%v, "+ + "which is more than 1 beyond best known "+ + "state #%v!!!", broadcastStateNum, + remoteStateNum) + // If the state number broadcast is lower than the // remote node's current un-revoked height, then // THEY'RE ATTEMPTING TO VIOLATE THE CONTRACT LAID OUT // WITHIN THE PAYMENT CHANNEL. Therefore we close the // signal indicating a revoked broadcast to allow - // subscribers to - // swiftly dispatch justice!!! + // subscribers to swiftly dispatch justice!!! case broadcastStateNum < remoteStateNum: - if err := c.dispatchContractBreach( + err := c.dispatchContractBreach( commitSpend, remoteCommit, broadcastStateNum, - ); err != nil { + ) + if err != nil { log.Errorf("unable to handle channel "+ "breach for chan_point=%v: %v", c.cfg.chanState.FundingOutpoint, err) @@ -570,13 +601,16 @@ func (c *chainWatcher) dispatchLocalForceClose( return nil } -// dispatchRemoteForceClose processes a detected unilateral channel closure by the -// remote party. This function will prepare a UnilateralCloseSummary which will -// then be sent to any subscribers allowing them to resolve all our funds in -// the channel on chain. Once this close summary is prepared, all registered -// subscribers will receive a notification of this event. +// dispatchRemoteForceClose processes a detected unilateral channel closure by +// the remote party. This function will prepare a UnilateralCloseSummary which +// will then be sent to any subscribers allowing them to resolve all our funds +// in the channel on chain. Once this close summary is prepared, all registered +// subscribers will receive a notification of this event. The +// isRemotePendingCommit argument should be set to true if the remote node +// broadcast their pending commitment (w/o revoking their current settled +// commitment). func (c *chainWatcher) dispatchRemoteForceClose(commitSpend *chainntnfs.SpendDetail, - remoteCommit channeldb.ChannelCommitment) error { + remoteCommit channeldb.ChannelCommitment, isRemotePendingCommit bool) error { log.Infof("Unilateral close of ChannelPoint(%v) "+ "detected", c.cfg.chanState.FundingOutpoint) @@ -584,8 +618,9 @@ func (c *chainWatcher) dispatchRemoteForceClose(commitSpend *chainntnfs.SpendDet // First, we'll create a closure summary that contains all the // materials required to let each subscriber sweep the funds in the // channel on-chain. - uniClose, err := lnwallet.NewUnilateralCloseSummary(c.cfg.chanState, - c.cfg.signer, c.cfg.pCache, commitSpend, remoteCommit, + uniClose, err := lnwallet.NewUnilateralCloseSummary( + c.cfg.chanState, c.cfg.signer, c.cfg.pCache, commitSpend, + remoteCommit, isRemotePendingCommit, ) if err != nil { return err From a3227ba147107f663faaca8a6384a26ce7be49f7 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sun, 29 Apr 2018 15:42:22 -0700 Subject: [PATCH 6/7] contractcourt: add two new tests to ensure the chainWatcher is able to play all remote commitments --- contractcourt/chain_watcher_test.go | 205 ++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 contractcourt/chain_watcher_test.go diff --git a/contractcourt/chain_watcher_test.go b/contractcourt/chain_watcher_test.go new file mode 100644 index 000000000..7e65c5fa7 --- /dev/null +++ b/contractcourt/chain_watcher_test.go @@ -0,0 +1,205 @@ +package contractcourt + +import ( + "bytes" + "crypto/sha256" + "testing" + "time" + + "github.com/lightningnetwork/lnd/chainntnfs" + "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwire" + "github.com/roasbeef/btcd/chaincfg/chainhash" + "github.com/roasbeef/btcd/wire" +) + +type mockNotifier struct { + spendChan chan *chainntnfs.SpendDetail +} + +func (m *mockNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash, numConfs, + heightHint uint32) (*chainntnfs.ConfirmationEvent, error) { + return nil, nil +} +func (m *mockNotifier) RegisterBlockEpochNtfn() (*chainntnfs.BlockEpochEvent, error) { + return &chainntnfs.BlockEpochEvent{ + Epochs: make(chan *chainntnfs.BlockEpoch), + Cancel: func() {}, + }, nil +} + +func (m *mockNotifier) Start() error { + return nil +} + +func (m *mockNotifier) Stop() error { + return nil +} +func (m *mockNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, + heightHint uint32, _ bool) (*chainntnfs.SpendEvent, error) { + return &chainntnfs.SpendEvent{ + Spend: m.spendChan, + Cancel: func() {}, + }, nil +} + +// TestChainWatcherRemoteUnilateralClose tests that the chain watcher is able +// to properly detect a normal unilateral close by the remote node using their +// lowest commitment. +func TestChainWatcherRemoteUnilateralClose(t *testing.T) { + t.Parallel() + + // First, we'll create two channels which already have established a + // commitment contract between themselves. + aliceChannel, bobChannel, cleanUp, err := lnwallet.CreateTestChannels() + if err != nil { + t.Fatalf("unable to create test channels: %v", err) + } + defer cleanUp() + + // With the channels created, we'll now create a chain watcher instance + // which will be watching for any closes of Alice's channel. + aliceNotifier := &mockNotifier{ + spendChan: make(chan *chainntnfs.SpendDetail), + } + aliceChainWatcher, err := newChainWatcher( + aliceChannel.State(), aliceNotifier, nil, aliceChannel.Signer, + nil, nil, + ) + if err != nil { + t.Fatalf("unable to create chain watcher: %v", err) + } + err = aliceChainWatcher.Start() + if err != nil { + t.Fatalf("unable to start chain watcher: %v", err) + } + defer aliceChainWatcher.Stop() + + // We'll request a new channel event subscription from Alice's chain + // watcher. + chanEvents := aliceChainWatcher.SubscribeChannelEvents(false) + + // If we simulate an immediate broadcast of the current commitment by + // Bob, then the chain watcher should detect this case. + bobCommit := bobChannel.State().LocalCommitment.CommitTx + bobTxHash := bobCommit.TxHash() + bobSpend := &chainntnfs.SpendDetail{ + SpenderTxHash: &bobTxHash, + SpendingTx: bobCommit, + } + aliceNotifier.spendChan <- bobSpend + + // We should get a new spend event over the remote unilateral close + // event channel. + var uniClose *lnwallet.UnilateralCloseSummary + select { + case uniClose = <-chanEvents.RemoteUnilateralClosure: + case <-time.After(time.Second * 15): + t.Fatalf("didn't receive unilateral close event") + } + + // The unilateral close should have properly located Alice's output in + // the commitment transaction. + if uniClose.CommitResolution == nil { + t.Fatalf("unable to find alice's commit resolution") + } +} + +// TestChainWatcherRemoteUnilateralClosePendingCommit tests that the chain +// watcher is able to properly detect a unilateral close wherein the remote +// node broadcasts their newly received commitment, without first revoking the +// old one. +func TestChainWatcherRemoteUnilateralClosePendingCommit(t *testing.T) { + t.Parallel() + + // First, we'll create two channels which already have established a + // commitment contract between themselves. + aliceChannel, bobChannel, cleanUp, err := lnwallet.CreateTestChannels() + if err != nil { + t.Fatalf("unable to create test channels: %v", err) + } + defer cleanUp() + + // With the channels created, we'll now create a chain watcher instance + // which will be watching for any closes of Alice's channel. + aliceNotifier := &mockNotifier{ + spendChan: make(chan *chainntnfs.SpendDetail), + } + aliceChainWatcher, err := newChainWatcher( + aliceChannel.State(), aliceNotifier, nil, aliceChannel.Signer, + nil, nil, + ) + if err != nil { + t.Fatalf("unable to create chain watcher: %v", err) + } + if err := aliceChainWatcher.Start(); err != nil { + t.Fatalf("unable to start chain watcher: %v", err) + } + defer aliceChainWatcher.Stop() + + // We'll request a new channel event subscription from Alice's chain + // watcher. + chanEvents := aliceChainWatcher.SubscribeChannelEvents(false) + + // Next, we'll create a fake HTLC just so we can advance Alice's + // channel state to a new pending commitment on her remote commit chain + // for Bob. + htlcAmount := lnwire.NewMSatFromSatoshis(20000) + preimage := bytes.Repeat([]byte{byte(1)}, 32) + paymentHash := sha256.Sum256(preimage) + var returnPreimage [32]byte + copy(returnPreimage[:], preimage) + htlc := &lnwire.UpdateAddHTLC{ + ID: uint64(0), + PaymentHash: paymentHash, + Amount: htlcAmount, + Expiry: uint32(5), + } + + if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { + t.Fatalf("alice unable to add htlc: %v", err) + } + if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { + t.Fatalf("bob unable to recv add htlc: %v", err) + } + + // With the HTLC added, we'll now manually initiate a state transition + // from Alice to Bob. + _, _, err = aliceChannel.SignNextCommitment() + if err != nil { + t.Fatal(err) + } + + // At this point, we'll now Bob broadcasting this new pending unrevoked + // commitment. + bobPendingCommit, err := aliceChannel.State().RemoteCommitChainTip() + if err != nil { + t.Fatal(err) + } + + // We'll craft a fake spend notification with Bob's actual commitment. + // The chain watcher should be able to detect that this is a pending + // commit broadcast based on the state hints in the commitment. + bobCommit := bobPendingCommit.Commitment.CommitTx + bobTxHash := bobCommit.TxHash() + bobSpend := &chainntnfs.SpendDetail{ + SpenderTxHash: &bobTxHash, + SpendingTx: bobCommit, + } + aliceNotifier.spendChan <- bobSpend + + // We should get a new spend event over the remote unilateral close + // event channel. + var uniClose *lnwallet.UnilateralCloseSummary + select { + case uniClose = <-chanEvents.RemoteUnilateralClosure: + case <-time.After(time.Second * 15): + t.Fatalf("didn't receive unilateral close event") + } + + // The unilateral close should have properly located Alice's output in + // the commitment transaction. + if uniClose.CommitResolution == nil { + t.Fatalf("unable to find alice's commit resolution") + } +} From 7af699f3a7d5600bf7f2745b87f1962772b2f126 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Thu, 3 May 2018 18:36:55 -0700 Subject: [PATCH 7/7] contractcourt: update chain watcher tests to latest API --- contractcourt/chain_watcher_test.go | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/contractcourt/chain_watcher_test.go b/contractcourt/chain_watcher_test.go index 7e65c5fa7..d5d4a4879 100644 --- a/contractcourt/chain_watcher_test.go +++ b/contractcourt/chain_watcher_test.go @@ -62,10 +62,11 @@ func TestChainWatcherRemoteUnilateralClose(t *testing.T) { aliceNotifier := &mockNotifier{ spendChan: make(chan *chainntnfs.SpendDetail), } - aliceChainWatcher, err := newChainWatcher( - aliceChannel.State(), aliceNotifier, nil, aliceChannel.Signer, - nil, nil, - ) + aliceChainWatcher, err := newChainWatcher(chainWatcherConfig{ + chanState: aliceChannel.State(), + notifier: aliceNotifier, + signer: aliceChannel.Signer, + }) if err != nil { t.Fatalf("unable to create chain watcher: %v", err) } @@ -77,7 +78,7 @@ func TestChainWatcherRemoteUnilateralClose(t *testing.T) { // We'll request a new channel event subscription from Alice's chain // watcher. - chanEvents := aliceChainWatcher.SubscribeChannelEvents(false) + chanEvents := aliceChainWatcher.SubscribeChannelEvents() // If we simulate an immediate broadcast of the current commitment by // Bob, then the chain watcher should detect this case. @@ -125,10 +126,11 @@ func TestChainWatcherRemoteUnilateralClosePendingCommit(t *testing.T) { aliceNotifier := &mockNotifier{ spendChan: make(chan *chainntnfs.SpendDetail), } - aliceChainWatcher, err := newChainWatcher( - aliceChannel.State(), aliceNotifier, nil, aliceChannel.Signer, - nil, nil, - ) + aliceChainWatcher, err := newChainWatcher(chainWatcherConfig{ + chanState: aliceChannel.State(), + notifier: aliceNotifier, + signer: aliceChannel.Signer, + }) if err != nil { t.Fatalf("unable to create chain watcher: %v", err) } @@ -139,7 +141,7 @@ func TestChainWatcherRemoteUnilateralClosePendingCommit(t *testing.T) { // We'll request a new channel event subscription from Alice's chain // watcher. - chanEvents := aliceChainWatcher.SubscribeChannelEvents(false) + chanEvents := aliceChainWatcher.SubscribeChannelEvents() // Next, we'll create a fake HTLC just so we can advance Alice's // channel state to a new pending commitment on her remote commit chain