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 +}