2017-07-14 22:05:55 +03:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2018-03-05 19:20:36 +03:00
|
|
|
crand "crypto/rand"
|
|
|
|
"encoding/binary"
|
|
|
|
"io"
|
2017-07-14 22:05:55 +03:00
|
|
|
"io/ioutil"
|
|
|
|
"math/rand"
|
|
|
|
"net"
|
|
|
|
"os"
|
|
|
|
|
|
|
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
|
|
|
"github.com/lightningnetwork/lnd/channeldb"
|
2018-01-17 07:56:51 +03:00
|
|
|
"github.com/lightningnetwork/lnd/contractcourt"
|
2017-07-14 22:05:55 +03:00
|
|
|
"github.com/lightningnetwork/lnd/htlcswitch"
|
2018-02-18 02:29:01 +03:00
|
|
|
"github.com/lightningnetwork/lnd/keychain"
|
2017-07-14 22:05:55 +03:00
|
|
|
"github.com/lightningnetwork/lnd/lnwallet"
|
|
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
|
|
"github.com/lightningnetwork/lnd/shachain"
|
|
|
|
"github.com/roasbeef/btcd/btcec"
|
|
|
|
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
|
|
|
"github.com/roasbeef/btcd/wire"
|
|
|
|
"github.com/roasbeef/btcutil"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
alicesPrivKey = []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,
|
|
|
|
}
|
|
|
|
|
|
|
|
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 = [32]byte{
|
|
|
|
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,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Just use some arbitrary bytes as delivery script.
|
|
|
|
dummyDeliveryScript = alicesPrivKey[:]
|
|
|
|
)
|
|
|
|
|
|
|
|
// createTestPeer creates a channel between two nodes, and returns a peer for
|
|
|
|
// one of the nodes, together with the channel seen from both nodes.
|
|
|
|
func createTestPeer(notifier chainntnfs.ChainNotifier,
|
|
|
|
publTx chan *wire.MsgTx) (*peer, *lnwallet.LightningChannel,
|
|
|
|
*lnwallet.LightningChannel, func(), error) {
|
|
|
|
|
|
|
|
aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(btcec.S256(),
|
|
|
|
alicesPrivKey)
|
|
|
|
bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes(btcec.S256(),
|
|
|
|
bobsPrivKey)
|
|
|
|
|
|
|
|
channelCapacity := btcutil.Amount(10 * 1e8)
|
|
|
|
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)
|
|
|
|
|
|
|
|
aliceCfg := channeldb.ChannelConfig{
|
|
|
|
ChannelConstraints: channeldb.ChannelConstraints{
|
|
|
|
DustLimit: aliceDustLimit,
|
2017-08-22 09:25:41 +03:00
|
|
|
MaxPendingAmount: lnwire.MilliSatoshi(rand.Int63()),
|
2017-07-14 22:05:55 +03:00
|
|
|
ChanReserve: btcutil.Amount(rand.Int63()),
|
2017-08-22 09:25:41 +03:00
|
|
|
MinHTLC: lnwire.MilliSatoshi(rand.Int63()),
|
2017-07-14 22:05:55 +03:00
|
|
|
MaxAcceptedHtlcs: uint16(rand.Int31()),
|
|
|
|
},
|
2018-02-18 02:29:01 +03:00
|
|
|
CsvDelay: uint16(csvTimeoutAlice),
|
|
|
|
MultiSigKey: keychain.KeyDescriptor{
|
|
|
|
PubKey: aliceKeyPub,
|
|
|
|
},
|
|
|
|
RevocationBasePoint: keychain.KeyDescriptor{
|
|
|
|
PubKey: aliceKeyPub,
|
|
|
|
},
|
|
|
|
PaymentBasePoint: keychain.KeyDescriptor{
|
|
|
|
PubKey: aliceKeyPub,
|
|
|
|
},
|
|
|
|
DelayBasePoint: keychain.KeyDescriptor{
|
|
|
|
PubKey: aliceKeyPub,
|
|
|
|
},
|
|
|
|
HtlcBasePoint: keychain.KeyDescriptor{
|
|
|
|
PubKey: aliceKeyPub,
|
|
|
|
},
|
2017-07-14 22:05:55 +03:00
|
|
|
}
|
|
|
|
bobCfg := channeldb.ChannelConfig{
|
|
|
|
ChannelConstraints: channeldb.ChannelConstraints{
|
|
|
|
DustLimit: bobDustLimit,
|
2017-08-22 09:25:41 +03:00
|
|
|
MaxPendingAmount: lnwire.MilliSatoshi(rand.Int63()),
|
2017-07-14 22:05:55 +03:00
|
|
|
ChanReserve: btcutil.Amount(rand.Int63()),
|
2017-08-22 09:25:41 +03:00
|
|
|
MinHTLC: lnwire.MilliSatoshi(rand.Int63()),
|
2017-07-14 22:05:55 +03:00
|
|
|
MaxAcceptedHtlcs: uint16(rand.Int31()),
|
|
|
|
},
|
2018-02-18 02:29:01 +03:00
|
|
|
CsvDelay: uint16(csvTimeoutBob),
|
|
|
|
MultiSigKey: keychain.KeyDescriptor{
|
|
|
|
PubKey: bobKeyPub,
|
|
|
|
},
|
|
|
|
RevocationBasePoint: keychain.KeyDescriptor{
|
|
|
|
PubKey: bobKeyPub,
|
|
|
|
},
|
|
|
|
PaymentBasePoint: keychain.KeyDescriptor{
|
|
|
|
PubKey: bobKeyPub,
|
|
|
|
},
|
|
|
|
DelayBasePoint: keychain.KeyDescriptor{
|
|
|
|
PubKey: bobKeyPub,
|
|
|
|
},
|
|
|
|
HtlcBasePoint: keychain.KeyDescriptor{
|
|
|
|
PubKey: bobKeyPub,
|
|
|
|
},
|
2017-07-14 22:05:55 +03:00
|
|
|
}
|
|
|
|
|
2018-02-18 02:29:01 +03:00
|
|
|
bobRoot, err := chainhash.NewHash(bobKeyPriv.Serialize())
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, nil, err
|
|
|
|
}
|
|
|
|
bobPreimageProducer := shachain.NewRevocationProducer(*bobRoot)
|
2017-07-14 22:05:55 +03:00
|
|
|
bobFirstRevoke, err := bobPreimageProducer.AtIndex(0)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, nil, err
|
|
|
|
}
|
|
|
|
bobCommitPoint := lnwallet.ComputeCommitmentPoint(bobFirstRevoke[:])
|
|
|
|
|
2018-02-18 02:29:01 +03:00
|
|
|
aliceRoot, err := chainhash.NewHash(aliceKeyPriv.Serialize())
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, nil, err
|
|
|
|
}
|
|
|
|
alicePreimageProducer := shachain.NewRevocationProducer(*aliceRoot)
|
2017-07-14 22:05:55 +03:00
|
|
|
aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, nil, err
|
|
|
|
}
|
|
|
|
aliceCommitPoint := lnwallet.ComputeCommitmentPoint(aliceFirstRevoke[:])
|
|
|
|
|
|
|
|
aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns(channelBal,
|
|
|
|
channelBal, &aliceCfg, &bobCfg, aliceCommitPoint, bobCommitPoint,
|
2017-12-22 21:26:16 +03:00
|
|
|
*fundingTxIn)
|
2017-07-14 22:05:55 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
alicePath, err := ioutil.TempDir("", "alicedb")
|
|
|
|
dbAlice, err := channeldb.Open(alicePath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
bobPath, err := ioutil.TempDir("", "bobdb")
|
|
|
|
dbBob, err := channeldb.Open(bobPath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
estimator := &lnwallet.StaticFeeEstimator{FeeRate: 50}
|
2018-02-13 17:15:14 +03:00
|
|
|
feePerVSize, err := estimator.EstimateFeePerVSize(1)
|
2017-11-23 22:41:20 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, nil, err
|
|
|
|
}
|
2018-02-13 17:15:14 +03:00
|
|
|
feePerKw := feePerVSize.FeePerKWeight()
|
2017-11-23 22:41:20 +03:00
|
|
|
|
2017-11-11 02:24:29 +03:00
|
|
|
// TODO(roasbeef): need to factor in commit fee?
|
|
|
|
aliceCommit := channeldb.ChannelCommitment{
|
|
|
|
CommitHeight: 0,
|
|
|
|
LocalBalance: lnwire.NewMSatFromSatoshis(channelBal),
|
|
|
|
RemoteBalance: lnwire.NewMSatFromSatoshis(channelBal),
|
2018-02-13 17:15:14 +03:00
|
|
|
FeePerKw: btcutil.Amount(feePerKw),
|
|
|
|
CommitFee: feePerKw.FeeForWeight(lnwallet.CommitWeight),
|
2017-11-11 02:24:29 +03:00
|
|
|
CommitTx: aliceCommitTx,
|
|
|
|
CommitSig: bytes.Repeat([]byte{1}, 71),
|
|
|
|
}
|
|
|
|
bobCommit := channeldb.ChannelCommitment{
|
|
|
|
CommitHeight: 0,
|
|
|
|
LocalBalance: lnwire.NewMSatFromSatoshis(channelBal),
|
|
|
|
RemoteBalance: lnwire.NewMSatFromSatoshis(channelBal),
|
2018-02-13 17:15:14 +03:00
|
|
|
FeePerKw: btcutil.Amount(feePerKw),
|
|
|
|
CommitFee: feePerKw.FeeForWeight(lnwallet.CommitWeight),
|
2017-11-11 02:24:29 +03:00
|
|
|
CommitTx: bobCommitTx,
|
|
|
|
CommitSig: bytes.Repeat([]byte{1}, 71),
|
|
|
|
}
|
|
|
|
|
2018-03-05 19:20:36 +03:00
|
|
|
var chanIDBytes [8]byte
|
|
|
|
if _, err := io.ReadFull(crand.Reader, chanIDBytes[:]); err != nil {
|
|
|
|
return nil, nil, nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
shortChanID := lnwire.NewShortChanIDFromInt(
|
|
|
|
binary.BigEndian.Uint64(chanIDBytes[:]),
|
|
|
|
)
|
|
|
|
|
2017-07-14 22:05:55 +03:00
|
|
|
aliceChannelState := &channeldb.OpenChannel{
|
|
|
|
LocalChanCfg: aliceCfg,
|
|
|
|
RemoteChanCfg: bobCfg,
|
|
|
|
IdentityPub: aliceKeyPub,
|
|
|
|
FundingOutpoint: *prevOut,
|
2018-03-05 19:20:36 +03:00
|
|
|
ShortChanID: shortChanID,
|
2017-07-14 22:05:55 +03:00
|
|
|
ChanType: channeldb.SingleFunder,
|
|
|
|
IsInitiator: true,
|
|
|
|
Capacity: channelCapacity,
|
|
|
|
RemoteCurrentRevocation: bobCommitPoint,
|
|
|
|
RevocationProducer: alicePreimageProducer,
|
|
|
|
RevocationStore: shachain.NewRevocationStore(),
|
2017-11-11 02:24:29 +03:00
|
|
|
LocalCommitment: aliceCommit,
|
|
|
|
RemoteCommitment: aliceCommit,
|
2017-07-14 22:05:55 +03:00
|
|
|
Db: dbAlice,
|
2018-03-05 19:20:36 +03:00
|
|
|
Packager: channeldb.NewChannelPackager(shortChanID),
|
2017-07-14 22:05:55 +03:00
|
|
|
}
|
|
|
|
bobChannelState := &channeldb.OpenChannel{
|
|
|
|
LocalChanCfg: bobCfg,
|
|
|
|
RemoteChanCfg: aliceCfg,
|
|
|
|
IdentityPub: bobKeyPub,
|
|
|
|
FundingOutpoint: *prevOut,
|
|
|
|
ChanType: channeldb.SingleFunder,
|
|
|
|
IsInitiator: false,
|
|
|
|
Capacity: channelCapacity,
|
|
|
|
RemoteCurrentRevocation: aliceCommitPoint,
|
|
|
|
RevocationProducer: bobPreimageProducer,
|
|
|
|
RevocationStore: shachain.NewRevocationStore(),
|
2017-11-11 02:24:29 +03:00
|
|
|
LocalCommitment: bobCommit,
|
|
|
|
RemoteCommitment: bobCommit,
|
2017-07-14 22:05:55 +03:00
|
|
|
Db: dbBob,
|
2018-03-05 19:20:36 +03:00
|
|
|
Packager: channeldb.NewChannelPackager(shortChanID),
|
2017-07-14 22:05:55 +03:00
|
|
|
}
|
|
|
|
|
2017-11-11 02:24:29 +03:00
|
|
|
addr := &net.TCPAddr{
|
|
|
|
IP: net.ParseIP("127.0.0.1"),
|
|
|
|
Port: 18555,
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := aliceChannelState.SyncPending(addr, 0); err != nil {
|
|
|
|
return nil, nil, nil, nil, err
|
|
|
|
}
|
|
|
|
|
2017-07-14 22:05:55 +03:00
|
|
|
addr = &net.TCPAddr{
|
|
|
|
IP: net.ParseIP("127.0.0.1"),
|
|
|
|
Port: 18556,
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := bobChannelState.SyncPending(addr, 0); err != nil {
|
|
|
|
return nil, nil, nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanUpFunc := func() {
|
|
|
|
os.RemoveAll(bobPath)
|
|
|
|
os.RemoveAll(alicePath)
|
|
|
|
}
|
|
|
|
|
|
|
|
aliceSigner := &mockSigner{aliceKeyPriv}
|
|
|
|
bobSigner := &mockSigner{bobKeyPriv}
|
|
|
|
|
2018-01-17 07:56:51 +03:00
|
|
|
channelAlice, err := lnwallet.NewLightningChannel(
|
2018-01-19 01:15:42 +03:00
|
|
|
aliceSigner, nil, aliceChannelState,
|
2018-01-17 07:56:51 +03:00
|
|
|
)
|
2017-07-14 22:05:55 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, nil, err
|
|
|
|
}
|
2018-01-17 07:56:51 +03:00
|
|
|
channelBob, err := lnwallet.NewLightningChannel(
|
2018-01-19 01:15:42 +03:00
|
|
|
bobSigner, nil, bobChannelState,
|
2018-01-17 07:56:51 +03:00
|
|
|
)
|
2017-07-14 22:05:55 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
chainIO := &mockChainIO{}
|
|
|
|
wallet := &lnwallet.LightningWallet{
|
|
|
|
WalletController: &mockWalletController{
|
|
|
|
rootKey: aliceKeyPriv,
|
|
|
|
publishedTransactions: publTx,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
cc := &chainControl{
|
|
|
|
feeEstimator: estimator,
|
|
|
|
chainIO: chainIO,
|
|
|
|
chainNotifier: notifier,
|
|
|
|
wallet: wallet,
|
|
|
|
}
|
|
|
|
|
2018-01-21 07:27:25 +03:00
|
|
|
breachArbiter := &breachArbiter{}
|
2017-07-14 22:05:55 +03:00
|
|
|
|
2018-01-17 07:56:51 +03:00
|
|
|
chainArb := contractcourt.NewChainArbitrator(
|
breacharbiter: properly account for second-level spends during breach remedy
In this commit, we address an un accounted for case during the breach
remedy process. If the remote node actually went directly to the second
layer during a channel breach attempt, then we wouldn’t properly be
able to sweep with out justice transaction, as some HTLC inputs may
actually be spent at that point.
In order to address this case, we’ll now catch the transaction
rejection, then check to see which input was spent, promote that to a
second level spend, and repeat as necessary. At the end of this loop,
any inputs which have been spent to the second level will have had the
prevouts and witnesses updated.
In order to perform this transition, we now also store the second level
witness script in the database. This allow us to modify the sign desc
with the proper input value, as well as witness script.
2018-01-23 04:11:02 +03:00
|
|
|
contractcourt.ChainArbitratorConfig{
|
|
|
|
Notifier: notifier,
|
|
|
|
ChainIO: chainIO,
|
|
|
|
}, dbAlice,
|
2018-01-17 07:56:51 +03:00
|
|
|
)
|
breacharbiter: properly account for second-level spends during breach remedy
In this commit, we address an un accounted for case during the breach
remedy process. If the remote node actually went directly to the second
layer during a channel breach attempt, then we wouldn’t properly be
able to sweep with out justice transaction, as some HTLC inputs may
actually be spent at that point.
In order to address this case, we’ll now catch the transaction
rejection, then check to see which input was spent, promote that to a
second level spend, and repeat as necessary. At the end of this loop,
any inputs which have been spent to the second level will have had the
prevouts and witnesses updated.
In order to perform this transition, we now also store the second level
witness script in the database. This allow us to modify the sign desc
with the proper input value, as well as witness script.
2018-01-23 04:11:02 +03:00
|
|
|
chainArb.WatchNewChannel(aliceChannelState)
|
2018-01-17 07:56:51 +03:00
|
|
|
|
2017-07-14 22:05:55 +03:00
|
|
|
s := &server{
|
|
|
|
chanDB: dbAlice,
|
|
|
|
cc: cc,
|
|
|
|
breachArbiter: breachArbiter,
|
2018-01-17 07:56:51 +03:00
|
|
|
chainArb: chainArb,
|
2017-07-14 22:05:55 +03:00
|
|
|
}
|
2018-01-18 04:15:41 +03:00
|
|
|
htlcSwitch, err := htlcswitch.New(htlcswitch.Config{
|
2018-03-05 19:20:36 +03:00
|
|
|
DB: dbAlice,
|
|
|
|
SwitchPackager: channeldb.NewSwitchPackager(),
|
2018-01-18 04:15:41 +03:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, nil, err
|
|
|
|
}
|
|
|
|
s.htlcSwitch = htlcSwitch
|
2017-07-14 22:05:55 +03:00
|
|
|
s.htlcSwitch.Start()
|
|
|
|
|
|
|
|
alicePeer := &peer{
|
|
|
|
server: s,
|
2018-02-07 06:11:11 +03:00
|
|
|
sendQueue: make(chan outgoingMsg, 1),
|
|
|
|
outgoingQueue: make(chan outgoingMsg, outgoingQueueLen),
|
2017-07-14 22:05:55 +03:00
|
|
|
|
|
|
|
activeChannels: make(map[lnwire.ChannelID]*lnwallet.LightningChannel),
|
|
|
|
newChannels: make(chan *newChannelMsg, 1),
|
|
|
|
|
2017-11-23 22:41:20 +03:00
|
|
|
activeChanCloses: make(map[lnwire.ChannelID]*channelCloser),
|
|
|
|
localCloseChanReqs: make(chan *htlcswitch.ChanClose),
|
|
|
|
chanCloseMsgs: make(chan *closeMsg),
|
2017-07-14 22:05:55 +03:00
|
|
|
|
|
|
|
queueQuit: make(chan struct{}),
|
|
|
|
quit: make(chan struct{}),
|
|
|
|
}
|
|
|
|
|
|
|
|
chanID := lnwire.NewChanIDFromOutPoint(channelAlice.ChannelPoint())
|
|
|
|
alicePeer.activeChannels[chanID] = channelAlice
|
|
|
|
|
|
|
|
go alicePeer.channelManager()
|
|
|
|
|
|
|
|
return alicePeer, channelAlice, channelBob, cleanUpFunc, nil
|
|
|
|
}
|