2016-07-06 03:01:55 +03:00
|
|
|
package lnwallet
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2017-03-16 04:56:25 +03:00
|
|
|
"crypto/sha256"
|
2017-07-30 23:26:31 +03:00
|
|
|
"fmt"
|
2016-07-06 03:01:55 +03:00
|
|
|
"io/ioutil"
|
2017-07-30 23:26:31 +03:00
|
|
|
"math/rand"
|
2016-07-06 03:01:55 +03:00
|
|
|
"os"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/davecgh/go-spew/spew"
|
2016-09-13 05:05:56 +03:00
|
|
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
2016-07-06 03:01:55 +03:00
|
|
|
"github.com/lightningnetwork/lnd/channeldb"
|
|
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
2016-12-14 17:01:48 +03:00
|
|
|
"github.com/lightningnetwork/lnd/shachain"
|
2016-11-23 11:29:05 +03:00
|
|
|
"github.com/roasbeef/btcd/blockchain"
|
2016-07-06 03:01:55 +03:00
|
|
|
"github.com/roasbeef/btcd/btcec"
|
2017-01-06 00:56:27 +03:00
|
|
|
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
2016-08-13 01:50:47 +03:00
|
|
|
"github.com/roasbeef/btcd/txscript"
|
2016-07-06 03:01:55 +03:00
|
|
|
"github.com/roasbeef/btcd/wire"
|
|
|
|
"github.com/roasbeef/btcutil"
|
|
|
|
)
|
|
|
|
|
2016-08-13 01:50:47 +03:00
|
|
|
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.
|
2017-07-30 23:26:31 +03:00
|
|
|
testHdSeed = chainhash.Hash{
|
2016-08-13 01:50:47 +03:00
|
|
|
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,
|
|
|
|
}
|
2016-07-06 03:01:55 +03:00
|
|
|
|
2016-08-13 01:50:47 +03:00
|
|
|
// The number of confirmations required to consider any created channel
|
|
|
|
// open.
|
|
|
|
numReqConfs = uint16(1)
|
|
|
|
)
|
|
|
|
|
|
|
|
type mockSigner struct {
|
|
|
|
key *btcec.PrivateKey
|
2016-07-06 03:01:55 +03:00
|
|
|
}
|
|
|
|
|
2016-08-13 01:50:47 +03:00
|
|
|
func (m *mockSigner) SignOutputRaw(tx *wire.MsgTx, signDesc *SignDescriptor) ([]byte, error) {
|
|
|
|
amt := signDesc.Output.Value
|
2016-10-16 02:02:09 +03:00
|
|
|
witnessScript := signDesc.WitnessScript
|
2016-08-13 01:50:47 +03:00
|
|
|
privKey := m.key
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
if !privKey.PubKey().IsEqual(signDesc.PubKey) {
|
|
|
|
return nil, fmt.Errorf("incorrect key passed")
|
|
|
|
}
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case signDesc.SingleTweak != nil:
|
|
|
|
privKey = TweakPrivKey(privKey,
|
|
|
|
signDesc.SingleTweak)
|
|
|
|
case signDesc.DoubleTweak != nil:
|
|
|
|
privKey = DeriveRevocationPrivKey(privKey,
|
|
|
|
signDesc.DoubleTweak)
|
|
|
|
}
|
|
|
|
|
2016-08-13 01:50:47 +03:00
|
|
|
sig, err := txscript.RawTxInWitnessSignature(tx, signDesc.SigHashes,
|
2017-07-30 23:26:31 +03:00
|
|
|
signDesc.InputIndex, amt, witnessScript, txscript.SigHashAll,
|
|
|
|
privKey)
|
2016-08-13 01:50:47 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return sig[:len(sig)-1], nil
|
2016-07-06 03:01:55 +03:00
|
|
|
}
|
2016-08-13 01:50:47 +03:00
|
|
|
func (m *mockSigner) ComputeInputScript(tx *wire.MsgTx, signDesc *SignDescriptor) (*InputScript, error) {
|
2016-11-19 02:23:26 +03:00
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
// TODO(roasbeef): expose tweaked signer from lnwallet so don't need to
|
|
|
|
// duplicate this code?
|
|
|
|
|
|
|
|
privKey := m.key
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case signDesc.SingleTweak != nil:
|
|
|
|
privKey = TweakPrivKey(privKey,
|
|
|
|
signDesc.SingleTweak)
|
|
|
|
case signDesc.DoubleTweak != nil:
|
|
|
|
privKey = DeriveRevocationPrivKey(privKey,
|
|
|
|
signDesc.DoubleTweak)
|
|
|
|
}
|
|
|
|
|
2016-11-19 02:23:26 +03:00
|
|
|
witnessScript, err := txscript.WitnessScript(tx, signDesc.SigHashes,
|
|
|
|
signDesc.InputIndex, signDesc.Output.Value, signDesc.Output.PkScript,
|
2017-07-30 23:26:31 +03:00
|
|
|
txscript.SigHashAll, privKey, true)
|
2016-11-19 02:23:26 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &InputScript{
|
|
|
|
Witness: witnessScript,
|
|
|
|
}, nil
|
2016-07-06 03:01:55 +03:00
|
|
|
}
|
|
|
|
|
2016-09-13 05:05:56 +03:00
|
|
|
type mockNotfier struct {
|
|
|
|
}
|
|
|
|
|
2017-05-12 01:58:41 +03:00
|
|
|
func (m *mockNotfier) RegisterConfirmationsNtfn(txid *chainhash.Hash, numConfs, heightHint uint32) (*chainntnfs.ConfirmationEvent, error) {
|
2016-09-13 05:05:56 +03:00
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
func (m *mockNotfier) RegisterBlockEpochNtfn() (*chainntnfs.BlockEpochEvent, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockNotfier) Start() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockNotfier) Stop() error {
|
|
|
|
return nil
|
|
|
|
}
|
2017-05-12 01:58:41 +03:00
|
|
|
func (m *mockNotfier) RegisterSpendNtfn(outpoint *wire.OutPoint, heightHint uint32) (*chainntnfs.SpendEvent, error) {
|
2016-09-13 05:05:56 +03:00
|
|
|
return &chainntnfs.SpendEvent{
|
|
|
|
Spend: make(chan *chainntnfs.SpendDetail),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2016-09-07 20:45:27 +03:00
|
|
|
// initRevocationWindows simulates a new channel being opened within the p2p
|
|
|
|
// network by populating the initial revocation windows of the passed
|
|
|
|
// commitment state machines.
|
2017-07-30 23:26:31 +03:00
|
|
|
//
|
|
|
|
// TODO(roasbeef): rename!
|
2016-09-07 20:45:27 +03:00
|
|
|
func initRevocationWindows(chanA, chanB *LightningChannel, windowSize int) error {
|
2017-07-31 04:22:38 +03:00
|
|
|
aliceNextRevoke, err := chanA.NextRevocationKey()
|
2017-07-30 23:26:31 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := chanB.InitNextRevocation(aliceNextRevoke); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-09-07 20:45:27 +03:00
|
|
|
|
2017-07-31 04:22:38 +03:00
|
|
|
bobNextRevoke, err := chanB.NextRevocationKey()
|
2017-07-30 23:26:31 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := chanA.InitNextRevocation(bobNextRevoke); err != nil {
|
|
|
|
return err
|
2016-09-07 20:45:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-10-26 15:25:42 +03:00
|
|
|
// forceStateTransition executes the necessary interaction between the two
|
2016-09-07 20:45:27 +03:00
|
|
|
// commitment state machines to transition to a new state locking in any
|
|
|
|
// pending updates.
|
|
|
|
func forceStateTransition(chanA, chanB *LightningChannel) error {
|
2017-07-30 23:26:31 +03:00
|
|
|
aliceSig, aliceHtlcSigs, err := chanA.SignNextCommitment()
|
2016-09-07 20:45:27 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-07-30 23:26:31 +03:00
|
|
|
if err = chanB.ReceiveNewCommitment(aliceSig, aliceHtlcSigs); err != nil {
|
2016-09-07 20:45:27 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-02-21 05:01:39 +03:00
|
|
|
bobRevocation, err := chanB.RevokeCurrentCommitment()
|
2016-09-07 20:45:27 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-07-30 23:26:31 +03:00
|
|
|
bobSig, bobHtlcSigs, err := chanB.SignNextCommitment()
|
2016-09-07 20:45:27 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-02-21 05:01:39 +03:00
|
|
|
if _, err := chanA.ReceiveRevocation(bobRevocation); err != nil {
|
2016-09-07 20:45:27 +03:00
|
|
|
return err
|
|
|
|
}
|
2017-07-30 23:26:31 +03:00
|
|
|
if err := chanA.ReceiveNewCommitment(bobSig, bobHtlcSigs); err != nil {
|
2016-09-07 20:45:27 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-02-21 05:01:39 +03:00
|
|
|
aliceRevocation, err := chanA.RevokeCurrentCommitment()
|
|
|
|
if err != nil {
|
2016-09-07 20:45:27 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
if _, err := chanB.ReceiveRevocation(aliceRevocation); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-11-15 02:06:12 +03:00
|
|
|
// createTestChannels creates two test channels funded with 10 BTC, with 5 BTC
|
2016-11-16 23:49:37 +03:00
|
|
|
// allocated to each side. Within the channel, Alice is the initiator.
|
2016-09-07 20:45:27 +03:00
|
|
|
func createTestChannels(revocationWindow int) (*LightningChannel, *LightningChannel, func(), error) {
|
2016-07-06 03:01:55 +03:00
|
|
|
aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(btcec.S256(),
|
|
|
|
testWalletPrivKey)
|
|
|
|
bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes(btcec.S256(),
|
|
|
|
bobsPrivKey)
|
|
|
|
|
|
|
|
channelCapacity := btcutil.Amount(10 * 1e8)
|
|
|
|
channelBal := channelCapacity / 2
|
2016-12-06 17:05:46 +03:00
|
|
|
aliceDustLimit := btcutil.Amount(200)
|
2017-05-17 05:05:12 +03:00
|
|
|
bobDustLimit := btcutil.Amount(1300)
|
2016-07-06 03:01:55 +03:00
|
|
|
csvTimeoutAlice := uint32(5)
|
|
|
|
csvTimeoutBob := uint32(4)
|
|
|
|
|
|
|
|
prevOut := &wire.OutPoint{
|
2017-01-06 00:56:27 +03:00
|
|
|
Hash: chainhash.Hash(testHdSeed),
|
2016-07-06 03:01:55 +03:00
|
|
|
Index: 0,
|
|
|
|
}
|
|
|
|
fundingTxIn := wire.NewTxIn(prevOut, nil, nil)
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
aliceCfg := channeldb.ChannelConfig{
|
|
|
|
ChannelConstraints: channeldb.ChannelConstraints{
|
|
|
|
DustLimit: aliceDustLimit,
|
|
|
|
MaxPendingAmount: btcutil.Amount(rand.Int63()),
|
|
|
|
ChanReserve: btcutil.Amount(rand.Int63()),
|
|
|
|
MinHTLC: btcutil.Amount(rand.Int63()),
|
|
|
|
MaxAcceptedHtlcs: uint16(rand.Int31()),
|
|
|
|
},
|
|
|
|
CsvDelay: uint16(csvTimeoutAlice),
|
|
|
|
MultiSigKey: aliceKeyPub,
|
|
|
|
RevocationBasePoint: aliceKeyPub,
|
|
|
|
PaymentBasePoint: aliceKeyPub,
|
|
|
|
DelayBasePoint: aliceKeyPub,
|
|
|
|
}
|
|
|
|
bobCfg := channeldb.ChannelConfig{
|
|
|
|
ChannelConstraints: channeldb.ChannelConstraints{
|
|
|
|
DustLimit: bobDustLimit,
|
|
|
|
MaxPendingAmount: btcutil.Amount(rand.Int63()),
|
|
|
|
ChanReserve: btcutil.Amount(rand.Int63()),
|
|
|
|
MinHTLC: btcutil.Amount(rand.Int63()),
|
|
|
|
MaxAcceptedHtlcs: uint16(rand.Int31()),
|
|
|
|
},
|
|
|
|
CsvDelay: uint16(csvTimeoutBob),
|
|
|
|
MultiSigKey: bobKeyPub,
|
|
|
|
RevocationBasePoint: bobKeyPub,
|
|
|
|
PaymentBasePoint: bobKeyPub,
|
|
|
|
DelayBasePoint: bobKeyPub,
|
|
|
|
}
|
|
|
|
|
|
|
|
bobRoot := DeriveRevocationRoot(bobKeyPriv, testHdSeed, aliceKeyPub)
|
|
|
|
bobPreimageProducer := shachain.NewRevocationProducer(bobRoot)
|
2016-12-14 17:01:48 +03:00
|
|
|
bobFirstRevoke, err := bobPreimageProducer.AtIndex(0)
|
2016-07-06 03:01:55 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, err
|
|
|
|
}
|
2017-07-30 23:26:31 +03:00
|
|
|
bobCommitPoint := ComputeCommitmentPoint(bobFirstRevoke[:])
|
2016-07-06 03:01:55 +03:00
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
aliceRoot := DeriveRevocationRoot(aliceKeyPriv, testHdSeed, bobKeyPub)
|
|
|
|
alicePreimageProducer := shachain.NewRevocationProducer(aliceRoot)
|
2016-12-14 17:01:48 +03:00
|
|
|
aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0)
|
2016-07-06 03:01:55 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, err
|
|
|
|
}
|
2017-07-30 23:26:31 +03:00
|
|
|
aliceCommitPoint := ComputeCommitmentPoint(aliceFirstRevoke[:])
|
2016-07-06 03:01:55 +03:00
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
aliceCommitTx, bobCommitTx, err := CreateCommitmentTxns(channelBal,
|
|
|
|
channelBal, &aliceCfg, &bobCfg, aliceCommitPoint, bobCommitPoint,
|
|
|
|
fundingTxIn)
|
2016-07-06 03:01:55 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
alicePath, err := ioutil.TempDir("", "alicedb")
|
2016-12-22 23:09:19 +03:00
|
|
|
dbAlice, err := channeldb.Open(alicePath)
|
2016-07-06 03:01:55 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, err
|
|
|
|
}
|
2016-08-13 01:50:47 +03:00
|
|
|
|
2016-07-06 03:01:55 +03:00
|
|
|
bobPath, err := ioutil.TempDir("", "bobdb")
|
2016-12-22 23:09:19 +03:00
|
|
|
dbBob, err := channeldb.Open(bobPath)
|
2016-07-06 03:01:55 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, err
|
|
|
|
}
|
|
|
|
|
2016-11-16 23:49:37 +03:00
|
|
|
var obsfucator [StateHintSize]byte
|
|
|
|
copy(obsfucator[:], aliceFirstRevoke[:])
|
|
|
|
|
2017-05-18 02:51:10 +03:00
|
|
|
estimator := &StaticFeeEstimator{24, 6}
|
|
|
|
feePerKw := btcutil.Amount(estimator.EstimateFeePerWeight(1) * 1000)
|
2017-08-01 06:43:30 +03:00
|
|
|
commitFee := calcStaticFee(0)
|
2016-07-06 03:01:55 +03:00
|
|
|
aliceChannelState := &channeldb.OpenChannel{
|
2017-07-30 23:26:31 +03:00
|
|
|
LocalChanCfg: aliceCfg,
|
|
|
|
RemoteChanCfg: bobCfg,
|
|
|
|
IdentityPub: aliceKeyPub,
|
2017-08-01 06:43:30 +03:00
|
|
|
CommitFee: commitFee,
|
2017-07-30 23:26:31 +03:00
|
|
|
FundingOutpoint: *prevOut,
|
|
|
|
ChanType: channeldb.SingleFunder,
|
|
|
|
FeePerKw: feePerKw,
|
|
|
|
IsInitiator: true,
|
|
|
|
Capacity: channelCapacity,
|
2017-08-01 06:43:30 +03:00
|
|
|
LocalBalance: channelBal - commitFee,
|
2017-07-30 23:26:31 +03:00
|
|
|
RemoteBalance: channelBal,
|
|
|
|
CommitTx: *aliceCommitTx,
|
|
|
|
CommitSig: bytes.Repeat([]byte{1}, 71),
|
|
|
|
RemoteCurrentRevocation: bobCommitPoint,
|
|
|
|
RevocationProducer: alicePreimageProducer,
|
|
|
|
RevocationStore: shachain.NewRevocationStore(),
|
|
|
|
Db: dbAlice,
|
2016-07-06 03:01:55 +03:00
|
|
|
}
|
|
|
|
bobChannelState := &channeldb.OpenChannel{
|
2017-07-30 23:26:31 +03:00
|
|
|
LocalChanCfg: bobCfg,
|
|
|
|
RemoteChanCfg: aliceCfg,
|
|
|
|
IdentityPub: bobKeyPub,
|
|
|
|
FeePerKw: feePerKw,
|
2017-08-01 06:43:30 +03:00
|
|
|
CommitFee: commitFee,
|
2017-07-30 23:26:31 +03:00
|
|
|
FundingOutpoint: *prevOut,
|
|
|
|
ChanType: channeldb.SingleFunder,
|
|
|
|
IsInitiator: false,
|
|
|
|
Capacity: channelCapacity,
|
|
|
|
LocalBalance: channelBal,
|
2017-08-01 06:43:30 +03:00
|
|
|
RemoteBalance: channelBal - commitFee,
|
2017-07-30 23:26:31 +03:00
|
|
|
CommitTx: *bobCommitTx,
|
|
|
|
CommitSig: bytes.Repeat([]byte{1}, 71),
|
|
|
|
RemoteCurrentRevocation: aliceCommitPoint,
|
|
|
|
RevocationProducer: bobPreimageProducer,
|
|
|
|
RevocationStore: shachain.NewRevocationStore(),
|
|
|
|
Db: dbBob,
|
2016-07-06 03:01:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
cleanUpFunc := func() {
|
|
|
|
os.RemoveAll(bobPath)
|
|
|
|
os.RemoveAll(alicePath)
|
|
|
|
}
|
|
|
|
|
2016-08-13 01:50:47 +03:00
|
|
|
aliceSigner := &mockSigner{aliceKeyPriv}
|
|
|
|
bobSigner := &mockSigner{bobKeyPriv}
|
|
|
|
|
2016-09-13 05:05:56 +03:00
|
|
|
notifier := &mockNotfier{}
|
|
|
|
|
2017-05-01 10:27:12 +03:00
|
|
|
channelAlice, err := NewLightningChannel(aliceSigner, notifier,
|
|
|
|
estimator, aliceChannelState)
|
2016-07-06 03:01:55 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, err
|
|
|
|
}
|
2017-05-01 10:27:12 +03:00
|
|
|
channelBob, err := NewLightningChannel(bobSigner, notifier,
|
|
|
|
estimator, bobChannelState)
|
2016-07-06 03:01:55 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, nil, err
|
|
|
|
}
|
|
|
|
|
2016-09-07 20:45:27 +03:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2016-07-06 03:01:55 +03:00
|
|
|
return channelAlice, channelBob, cleanUpFunc, nil
|
|
|
|
}
|
|
|
|
|
2017-05-01 21:45:02 +03:00
|
|
|
// calcStaticFee calculates appropriate fees for commitment transactions. This
|
|
|
|
// function provides a simple way to allow test balance assertions to take fee
|
|
|
|
// calculations into account.
|
2017-08-01 06:43:30 +03:00
|
|
|
//
|
2017-05-01 21:45:02 +03:00
|
|
|
// TODO(bvu): Refactor when dynamic fee estimation is added.
|
|
|
|
func calcStaticFee(numHTLCs int) btcutil.Amount {
|
|
|
|
const (
|
|
|
|
commitWeight = btcutil.Amount(724)
|
|
|
|
htlcWeight = 172
|
2017-05-17 04:56:39 +03:00
|
|
|
feePerKw = btcutil.Amount(24/4) * 1000
|
2017-05-01 21:45:02 +03:00
|
|
|
)
|
2017-05-17 04:56:39 +03:00
|
|
|
return feePerKw * (commitWeight +
|
2017-05-01 21:45:02 +03:00
|
|
|
btcutil.Amount(htlcWeight*numHTLCs)) / 1000
|
|
|
|
}
|
|
|
|
|
2017-08-01 06:44:15 +03:00
|
|
|
// createHTLC is a utility function for generating an HTLC with a given
|
|
|
|
// preimage and a given amount.
|
2017-06-01 09:13:20 +03:00
|
|
|
func createHTLC(data int, amount btcutil.Amount) (*lnwire.UpdateAddHTLC, [32]byte) {
|
|
|
|
preimage := bytes.Repeat([]byte{byte(data)}, 32)
|
|
|
|
paymentHash := sha256.Sum256(preimage)
|
|
|
|
|
|
|
|
var returnPreimage [32]byte
|
|
|
|
copy(returnPreimage[:], preimage)
|
|
|
|
|
|
|
|
return &lnwire.UpdateAddHTLC{
|
|
|
|
PaymentHash: paymentHash,
|
|
|
|
Amount: amount,
|
|
|
|
Expiry: uint32(5),
|
|
|
|
}, returnPreimage
|
|
|
|
}
|
|
|
|
|
2016-07-06 03:01:55 +03:00
|
|
|
// TestSimpleAddSettleWorkflow tests a simple channel scenario wherein the
|
|
|
|
// local node (Alice in this case) creates a new outgoing HTLC to bob, commits
|
|
|
|
// this change, then bob immediately commits a settlement of the HTLC after the
|
2016-10-26 15:25:42 +03:00
|
|
|
// initial add is fully committed in both commit chains.
|
2017-02-21 05:01:39 +03:00
|
|
|
//
|
2016-10-15 16:18:38 +03:00
|
|
|
// TODO(roasbeef): write higher level framework to exercise various states of
|
2016-07-06 03:01:55 +03:00
|
|
|
// the state machine
|
|
|
|
// * DSL language perhaps?
|
|
|
|
// * constructed via input/output files
|
|
|
|
func TestSimpleAddSettleWorkflow(t *testing.T) {
|
2017-06-17 01:59:20 +03:00
|
|
|
t.Parallel()
|
|
|
|
|
2016-07-06 03:01:55 +03:00
|
|
|
// 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.
|
2017-02-21 05:01:39 +03:00
|
|
|
aliceChannel, bobChannel, cleanUp, err := createTestChannels(1)
|
2016-07-06 03:01:55 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create test channels: %v", err)
|
|
|
|
}
|
|
|
|
defer cleanUp()
|
|
|
|
|
|
|
|
paymentPreimage := bytes.Repeat([]byte{1}, 32)
|
2017-03-16 04:56:25 +03:00
|
|
|
paymentHash := sha256.Sum256(paymentPreimage)
|
2017-02-21 05:01:39 +03:00
|
|
|
htlc := &lnwire.UpdateAddHTLC{
|
|
|
|
PaymentHash: paymentHash,
|
|
|
|
Amount: btcutil.SatoshiPerBitcoin,
|
|
|
|
Expiry: uint32(5),
|
2016-07-06 03:01:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// First Alice adds the outgoing HTLC to her local channel's state
|
2017-07-30 23:26:31 +03:00
|
|
|
// update log. Then Alice sends this wire message over to Bob who adds
|
|
|
|
// this htlc to his remote state update log.
|
2017-02-21 05:01:39 +03:00
|
|
|
if _, err := aliceChannel.AddHTLC(htlc); err != nil {
|
|
|
|
t.Fatalf("unable to add htlc: %v", err)
|
|
|
|
}
|
|
|
|
if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
|
|
|
|
t.Fatalf("unable to recv htlc: %v", err)
|
|
|
|
}
|
2016-07-06 03:01:55 +03:00
|
|
|
|
2017-07-14 21:38:35 +03:00
|
|
|
// Next alice commits this change by sending a signature message. Since
|
|
|
|
// we expect the messages to be ordered, Bob will receive the HTLC we
|
|
|
|
// just sent before he receives this signature, so the signature will
|
|
|
|
// cover the HTLC.
|
2017-07-30 23:26:31 +03:00
|
|
|
aliceSig, aliceHtlcSigs, err := aliceChannel.SignNextCommitment()
|
2016-07-06 03:01:55 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("alice unable to sign commitment: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-07-14 21:38:35 +03:00
|
|
|
// Bob receives this signature message, and checks that this covers the
|
|
|
|
// state he has in his remote log. This includes the HTLC just sent
|
|
|
|
// from Alice.
|
2017-07-30 23:26:31 +03:00
|
|
|
err = bobChannel.ReceiveNewCommitment(aliceSig, aliceHtlcSigs)
|
|
|
|
if err != nil {
|
2016-07-06 03:01:55 +03:00
|
|
|
t.Fatalf("bob unable to process alice's new commitment: %v", err)
|
|
|
|
}
|
2017-07-30 23:26:31 +03:00
|
|
|
|
2017-07-14 21:38:35 +03:00
|
|
|
// Bob revokes his prior commitment given to him by Alice, since he now
|
|
|
|
// has a valid signature for a newer commitment.
|
2016-07-06 03:01:55 +03:00
|
|
|
bobRevocation, err := bobChannel.RevokeCurrentCommitment()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to generate bob revocation: %v", err)
|
|
|
|
}
|
2017-07-30 23:26:31 +03:00
|
|
|
|
2017-07-14 21:38:35 +03:00
|
|
|
// Bob finally send a signature for Alice's commitment transaction.
|
|
|
|
// This signature will cover the HTLC, since Bob will first send the
|
|
|
|
// revocation just created. The revocation also acks every received
|
|
|
|
// HTLC up to the point where Alice sent here signature.
|
2017-07-30 23:26:31 +03:00
|
|
|
bobSig, bobHtlcSigs, err := bobChannel.SignNextCommitment()
|
2017-02-21 05:01:39 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bob unable to sign alice's commitment: %v", err)
|
2016-07-06 03:01:55 +03:00
|
|
|
}
|
2017-02-21 05:01:39 +03:00
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
// Alice then processes this revocation, sending her own revocation for
|
2017-01-13 08:01:50 +03:00
|
|
|
// her prior commitment transaction. Alice shouldn't have any HTLCs to
|
2016-10-26 15:25:42 +03:00
|
|
|
// forward since she's sending an outgoing HTLC.
|
2016-07-06 03:01:55 +03:00
|
|
|
if htlcs, err := aliceChannel.ReceiveRevocation(bobRevocation); err != nil {
|
2017-07-30 23:26:31 +03:00
|
|
|
t.Fatalf("alice unable to process bob's revocation: %v", err)
|
2016-07-06 03:01:55 +03:00
|
|
|
} else if len(htlcs) != 0 {
|
|
|
|
t.Fatalf("alice forwards %v htlcs, should forward none: ", len(htlcs))
|
|
|
|
}
|
2017-02-21 05:01:39 +03:00
|
|
|
|
2017-07-14 21:38:35 +03:00
|
|
|
// Alice then processes bob's signature, and since she just received
|
|
|
|
// the revocation, she expect this signature to cover everything up to
|
|
|
|
// the point where she sent her signature, including the HTLC.
|
2017-07-30 23:26:31 +03:00
|
|
|
err = aliceChannel.ReceiveNewCommitment(bobSig, bobHtlcSigs)
|
|
|
|
if err != nil {
|
2017-02-21 05:01:39 +03:00
|
|
|
t.Fatalf("alice unable to process bob's new commitment: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-07-14 21:38:35 +03:00
|
|
|
// Alice then generates a revocation for bob.
|
2016-07-06 03:01:55 +03:00
|
|
|
aliceRevocation, err := aliceChannel.RevokeCurrentCommitment()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to revoke alice channel: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finally Bob processes Alice's revocation, at this point the new HTLC
|
|
|
|
// is fully locked in within both commitment transactions. Bob should
|
|
|
|
// also be able to forward an HTLC now that the HTLC has been locked
|
|
|
|
// into both commitment transactions.
|
|
|
|
if htlcs, err := bobChannel.ReceiveRevocation(aliceRevocation); err != nil {
|
|
|
|
t.Fatalf("bob unable to process alive's revocation: %v", err)
|
|
|
|
} else if len(htlcs) != 1 {
|
|
|
|
t.Fatalf("bob should be able to forward an HTLC, instead can "+
|
|
|
|
"forward %v", len(htlcs))
|
|
|
|
}
|
|
|
|
|
2017-05-01 21:45:02 +03:00
|
|
|
// At this point, both sides should have the proper number of satoshis
|
|
|
|
// sent, and commitment height updated within their local channel
|
|
|
|
// state.
|
|
|
|
aliceSent := uint64(0)
|
|
|
|
bobSent := uint64(0)
|
|
|
|
|
|
|
|
if aliceChannel.channelState.TotalSatoshisSent != aliceSent {
|
|
|
|
t.Fatalf("alice has incorrect satoshis sent: %v vs %v",
|
|
|
|
aliceChannel.channelState.TotalSatoshisSent, aliceSent)
|
2016-07-06 03:01:55 +03:00
|
|
|
}
|
2017-05-01 21:45:02 +03:00
|
|
|
if aliceChannel.channelState.TotalSatoshisReceived != bobSent {
|
|
|
|
t.Fatalf("alice has incorrect satoshis received %v vs %v",
|
|
|
|
aliceChannel.channelState.TotalSatoshisReceived, bobSent)
|
2016-07-06 03:01:55 +03:00
|
|
|
}
|
2017-05-01 21:45:02 +03:00
|
|
|
if bobChannel.channelState.TotalSatoshisSent != bobSent {
|
|
|
|
t.Fatalf("bob has incorrect satoshis sent %v vs %v",
|
|
|
|
bobChannel.channelState.TotalSatoshisSent, bobSent)
|
2016-07-06 03:01:55 +03:00
|
|
|
}
|
2017-05-01 21:45:02 +03:00
|
|
|
if bobChannel.channelState.TotalSatoshisReceived != aliceSent {
|
|
|
|
t.Fatalf("bob has incorrect satoshis received %v vs %v",
|
|
|
|
bobChannel.channelState.TotalSatoshisReceived, aliceSent)
|
2016-07-06 03:01:55 +03:00
|
|
|
}
|
|
|
|
if bobChannel.currentHeight != 1 {
|
|
|
|
t.Fatalf("bob has incorrect commitment height, %v vs %v",
|
|
|
|
bobChannel.currentHeight, 1)
|
|
|
|
}
|
|
|
|
if aliceChannel.currentHeight != 1 {
|
|
|
|
t.Fatalf("alice has incorrect commitment height, %v vs %v",
|
|
|
|
aliceChannel.currentHeight, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now we'll repeat a similar exchange, this time with Bob settling the
|
|
|
|
// HTLC once he learns of the preimage.
|
|
|
|
var preimage [32]byte
|
|
|
|
copy(preimage[:], paymentPreimage)
|
2016-07-22 02:50:20 +03:00
|
|
|
settleIndex, err := bobChannel.SettleHTLC(preimage)
|
|
|
|
if err != nil {
|
2016-07-06 03:01:55 +03:00
|
|
|
t.Fatalf("bob unable to settle inbound htlc: %v", err)
|
|
|
|
}
|
2016-07-22 02:50:20 +03:00
|
|
|
if err := aliceChannel.ReceiveHTLCSettle(preimage, settleIndex); err != nil {
|
2016-07-06 03:01:55 +03:00
|
|
|
t.Fatalf("alice unable to accept settle of outbound htlc: %v", err)
|
|
|
|
}
|
2017-02-21 05:01:39 +03:00
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
bobSig2, bobHtlcSigs2, err := bobChannel.SignNextCommitment()
|
2016-07-06 03:01:55 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bob unable to sign settle commitment: %v", err)
|
|
|
|
}
|
2017-07-30 23:26:31 +03:00
|
|
|
err = aliceChannel.ReceiveNewCommitment(bobSig2, bobHtlcSigs2)
|
|
|
|
if err != nil {
|
2016-07-06 03:01:55 +03:00
|
|
|
t.Fatalf("alice unable to process bob's new commitment: %v", err)
|
|
|
|
}
|
2017-02-21 05:01:39 +03:00
|
|
|
|
2016-07-06 03:01:55 +03:00
|
|
|
aliceRevocation2, err := aliceChannel.RevokeCurrentCommitment()
|
|
|
|
if err != nil {
|
2016-11-18 01:39:38 +03:00
|
|
|
t.Fatalf("alice unable to generate revocation: %v", err)
|
2016-07-06 03:01:55 +03:00
|
|
|
}
|
2017-07-30 23:26:31 +03:00
|
|
|
aliceSig2, aliceHtlcSigs2, err := aliceChannel.SignNextCommitment()
|
2016-07-06 03:01:55 +03:00
|
|
|
if err != nil {
|
2017-02-21 05:01:39 +03:00
|
|
|
t.Fatalf("alice unable to sign new commitment: %v", err)
|
2016-07-06 03:01:55 +03:00
|
|
|
}
|
2017-02-21 05:01:39 +03:00
|
|
|
|
2016-07-06 03:01:55 +03:00
|
|
|
if htlcs, err := bobChannel.ReceiveRevocation(aliceRevocation2); err != nil {
|
|
|
|
t.Fatalf("bob unable to process alice's revocation: %v", err)
|
2016-07-17 04:12:36 +03:00
|
|
|
} else if len(htlcs) != 0 {
|
2017-01-13 08:01:50 +03:00
|
|
|
t.Fatalf("bob shouldn't forward any HTLCs after outgoing settle, "+
|
2016-07-17 04:12:36 +03:00
|
|
|
"instead can forward: %v", spew.Sdump(htlcs))
|
2016-07-06 03:01:55 +03:00
|
|
|
}
|
2017-07-30 23:26:31 +03:00
|
|
|
err = bobChannel.ReceiveNewCommitment(aliceSig2, aliceHtlcSigs2)
|
|
|
|
if err != nil {
|
2017-02-21 05:01:39 +03:00
|
|
|
t.Fatalf("bob unable to process alice's new commitment: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
bobRevocation2, err := bobChannel.RevokeCurrentCommitment()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bob unable to revoke commitment: %v", err)
|
|
|
|
}
|
|
|
|
|
2016-07-06 03:01:55 +03:00
|
|
|
if htlcs, err := aliceChannel.ReceiveRevocation(bobRevocation2); err != nil {
|
|
|
|
t.Fatalf("alice unable to process bob's revocation: %v", err)
|
2016-07-17 04:12:36 +03:00
|
|
|
} else if len(htlcs) != 1 {
|
|
|
|
// Alice should now be able to forward the settlement HTLC to
|
|
|
|
// any down stream peers.
|
|
|
|
t.Fatalf("alice should be able to forward a single HTLC, "+
|
|
|
|
"instead can forward %v: %v", len(htlcs), spew.Sdump(htlcs))
|
2016-07-06 03:01:55 +03:00
|
|
|
}
|
|
|
|
|
2016-11-18 01:39:38 +03:00
|
|
|
// At this point, Bob should have 6 BTC settled, with Alice still having
|
|
|
|
// 4 BTC. Alice's channel should show 1 BTC sent and Bob's channel should
|
|
|
|
// show 1 BTC received. They should also be at commitment height two,
|
|
|
|
// with the revocation window extended by by 1 (5).
|
|
|
|
satoshisTransferred := uint64(100000000)
|
|
|
|
if aliceChannel.channelState.TotalSatoshisSent != satoshisTransferred {
|
|
|
|
t.Fatalf("alice satoshis sent incorrect %v vs %v expected",
|
2017-04-10 10:25:32 +03:00
|
|
|
aliceChannel.channelState.TotalSatoshisSent,
|
|
|
|
satoshisTransferred)
|
2016-11-18 01:39:38 +03:00
|
|
|
}
|
|
|
|
if aliceChannel.channelState.TotalSatoshisReceived != 0 {
|
|
|
|
t.Fatalf("alice satoshis received incorrect %v vs %v expected",
|
|
|
|
aliceChannel.channelState.TotalSatoshisSent, 0)
|
|
|
|
}
|
|
|
|
if bobChannel.channelState.TotalSatoshisReceived != satoshisTransferred {
|
|
|
|
t.Fatalf("bob satoshis received incorrect %v vs %v expected",
|
2017-04-10 10:25:32 +03:00
|
|
|
bobChannel.channelState.TotalSatoshisReceived,
|
|
|
|
satoshisTransferred)
|
2016-11-18 01:39:38 +03:00
|
|
|
}
|
|
|
|
if bobChannel.channelState.TotalSatoshisSent != 0 {
|
|
|
|
t.Fatalf("bob satoshis sent incorrect %v vs %v expected",
|
|
|
|
bobChannel.channelState.TotalSatoshisReceived, 0)
|
|
|
|
}
|
2016-07-06 03:01:55 +03:00
|
|
|
if bobChannel.currentHeight != 2 {
|
|
|
|
t.Fatalf("bob has incorrect commitment height, %v vs %v",
|
|
|
|
bobChannel.currentHeight, 2)
|
|
|
|
}
|
|
|
|
if aliceChannel.currentHeight != 2 {
|
|
|
|
t.Fatalf("alice has incorrect commitment height, %v vs %v",
|
|
|
|
aliceChannel.currentHeight, 2)
|
|
|
|
}
|
|
|
|
|
|
|
|
// The logs of both sides should now be cleared since the entry adding
|
2017-01-13 08:01:50 +03:00
|
|
|
// the HTLC should have been removed once both sides receive the
|
2016-07-06 03:01:55 +03:00
|
|
|
// revocation.
|
2017-02-21 05:01:39 +03:00
|
|
|
if aliceChannel.localUpdateLog.Len() != 0 {
|
2017-04-10 10:25:32 +03:00
|
|
|
t.Fatalf("alice's local not updated, should be empty, has %v "+
|
|
|
|
"entries instead", aliceChannel.localUpdateLog.Len())
|
2016-07-22 02:50:20 +03:00
|
|
|
}
|
2017-02-21 05:01:39 +03:00
|
|
|
if aliceChannel.remoteUpdateLog.Len() != 0 {
|
2017-04-10 10:25:32 +03:00
|
|
|
t.Fatalf("alice's remote not updated, should be empty, has %v "+
|
|
|
|
"entries instead", aliceChannel.remoteUpdateLog.Len())
|
2016-07-22 02:50:20 +03:00
|
|
|
}
|
2017-02-21 05:01:39 +03:00
|
|
|
if len(aliceChannel.localUpdateLog.updateIndex) != 0 {
|
2016-07-22 02:50:20 +03:00
|
|
|
t.Fatalf("alice's local log index not cleared, should be empty but "+
|
2017-02-21 05:01:39 +03:00
|
|
|
"has %v entries", len(aliceChannel.localUpdateLog.updateIndex))
|
2016-07-22 02:50:20 +03:00
|
|
|
}
|
2017-02-21 05:01:39 +03:00
|
|
|
if len(aliceChannel.remoteUpdateLog.updateIndex) != 0 {
|
2016-07-22 02:50:20 +03:00
|
|
|
t.Fatalf("alice's remote log index not cleared, should be empty but "+
|
2017-02-21 05:01:39 +03:00
|
|
|
"has %v entries", len(aliceChannel.remoteUpdateLog.updateIndex))
|
2016-07-06 03:01:55 +03:00
|
|
|
}
|
|
|
|
}
|
2016-07-17 04:12:36 +03:00
|
|
|
|
2016-11-23 11:29:05 +03:00
|
|
|
// TestCheckCommitTxSize checks that estimation size of commitment
|
|
|
|
// transaction with some degree of error corresponds to the actual size.
|
|
|
|
func TestCheckCommitTxSize(t *testing.T) {
|
2017-06-17 01:59:20 +03:00
|
|
|
t.Parallel()
|
|
|
|
|
2016-11-23 11:29:05 +03:00
|
|
|
checkSize := func(channel *LightningChannel, count int) {
|
2016-12-14 17:01:48 +03:00
|
|
|
// Due to variable size of the signatures (70-73) in
|
|
|
|
// witness script actual size of commitment transaction might
|
|
|
|
// be lower on 6 weight.
|
|
|
|
BaseCommitmentTxSizeEstimationError := 6
|
2016-11-23 11:29:05 +03:00
|
|
|
|
|
|
|
commitTx, err := channel.getSignedCommitTx()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to initiate alice force close: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-01-06 00:56:27 +03:00
|
|
|
actualCost := blockchain.GetTransactionWeight(btcutil.NewTx(commitTx))
|
2017-05-17 05:05:12 +03:00
|
|
|
estimatedCost := estimateCommitTxWeight(count, false)
|
2016-11-23 11:29:05 +03:00
|
|
|
|
|
|
|
diff := int(estimatedCost - actualCost)
|
|
|
|
if 0 > diff || BaseCommitmentTxSizeEstimationError < diff {
|
2017-02-21 05:01:39 +03:00
|
|
|
t.Fatalf("estimation is wrong, diff: %v", diff)
|
2016-11-23 11:29:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2017-02-21 05:01:39 +03:00
|
|
|
aliceChannel, bobChannel, cleanUp, err := createTestChannels(1)
|
2016-11-23 11:29:05 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create test channels: %v", err)
|
|
|
|
}
|
|
|
|
defer cleanUp()
|
|
|
|
|
|
|
|
// Check that weight estimation of the commitment transaction without
|
|
|
|
// HTLCs is right.
|
|
|
|
checkSize(aliceChannel, 0)
|
|
|
|
checkSize(bobChannel, 0)
|
|
|
|
|
|
|
|
// Adding HTLCs and check that size stays in allowable estimation
|
|
|
|
// error window.
|
|
|
|
for i := 1; i <= 10; i++ {
|
2017-06-01 09:13:20 +03:00
|
|
|
htlc, _ := createHTLC(i, btcutil.Amount(1e7))
|
2016-11-23 11:29:05 +03:00
|
|
|
|
|
|
|
if _, err := aliceChannel.AddHTLC(htlc); err != nil {
|
|
|
|
t.Fatalf("alice unable to add htlc: %v", err)
|
|
|
|
}
|
|
|
|
if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
|
|
|
|
t.Fatalf("bob unable to receive htlc: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-02-21 05:01:39 +03:00
|
|
|
if err := forceStateTransition(aliceChannel, bobChannel); err != nil {
|
|
|
|
t.Fatalf("unable to complete state update: %v", err)
|
|
|
|
}
|
2016-11-23 11:29:05 +03:00
|
|
|
checkSize(aliceChannel, i)
|
|
|
|
checkSize(bobChannel, i)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Settle HTLCs and check that estimation is counting cost of settle
|
|
|
|
// HTLCs properly.
|
|
|
|
for i := 10; i >= 1; i-- {
|
2017-06-01 09:13:20 +03:00
|
|
|
_, preimage := createHTLC(i, btcutil.Amount(1e7))
|
2016-11-23 11:29:05 +03:00
|
|
|
|
|
|
|
settleIndex, err := bobChannel.SettleHTLC(preimage)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bob unable to settle inbound htlc: %v", err)
|
|
|
|
}
|
|
|
|
err = aliceChannel.ReceiveHTLCSettle(preimage, settleIndex)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("alice unable to accept settle of outbound htlc: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-02-21 05:01:39 +03:00
|
|
|
if err := forceStateTransition(bobChannel, aliceChannel); err != nil {
|
|
|
|
t.Fatalf("unable to complete state update: %v", err)
|
|
|
|
}
|
2016-11-23 11:29:05 +03:00
|
|
|
checkSize(aliceChannel, i-1)
|
|
|
|
checkSize(bobChannel, i-1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-17 04:12:36 +03:00
|
|
|
func TestCooperativeChannelClosure(t *testing.T) {
|
2017-06-17 01:59:20 +03:00
|
|
|
t.Parallel()
|
|
|
|
|
2016-07-27 21:32:27 +03:00
|
|
|
// 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.
|
2017-02-21 05:01:39 +03:00
|
|
|
aliceChannel, bobChannel, cleanUp, err := createTestChannels(1)
|
2016-07-27 21:32:27 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create test channels: %v", err)
|
|
|
|
}
|
|
|
|
defer cleanUp()
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
aliceDeliveryScript := bobsPrivKey[:]
|
|
|
|
bobDeliveryScript := testHdSeed[:]
|
|
|
|
|
2017-05-18 02:51:10 +03:00
|
|
|
aliceFeeRate := uint64(aliceChannel.channelState.FeePerKw)
|
|
|
|
bobFeeRate := uint64(bobChannel.channelState.FeePerKw)
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
// We'll store with both Alice and Bob creating a new close proposal
|
|
|
|
// with the same fee rate.
|
2017-05-18 02:51:10 +03:00
|
|
|
aliceSig, _, err := aliceChannel.CreateCloseProposal(
|
2017-07-30 23:26:31 +03:00
|
|
|
aliceFeeRate, aliceDeliveryScript, bobDeliveryScript,
|
|
|
|
)
|
2016-07-27 21:32:27 +03:00
|
|
|
if err != nil {
|
2017-03-25 04:26:09 +03:00
|
|
|
t.Fatalf("unable to create alice coop close proposal: %v", err)
|
2016-07-27 21:32:27 +03:00
|
|
|
}
|
2017-03-25 04:26:09 +03:00
|
|
|
aliceCloseSig := append(aliceSig, byte(txscript.SigHashAll))
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
bobSig, _, err := bobChannel.CreateCloseProposal(
|
|
|
|
bobFeeRate, bobDeliveryScript, aliceDeliveryScript,
|
|
|
|
)
|
2016-07-27 21:32:27 +03:00
|
|
|
if err != nil {
|
2017-03-25 04:26:09 +03:00
|
|
|
t.Fatalf("unable to create bob coop close proposal: %v", err)
|
2016-07-27 21:32:27 +03:00
|
|
|
}
|
2017-03-25 04:26:09 +03:00
|
|
|
bobCloseSig := append(bobSig, byte(txscript.SigHashAll))
|
2016-07-27 21:32:27 +03:00
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
// With the proposals created, both sides should be able to properly
|
|
|
|
// process the other party's signature. This indicates that the
|
|
|
|
// transaction is well formed, and the signatures verify.
|
|
|
|
aliceCloseTx, err := bobChannel.CompleteCooperativeClose(
|
|
|
|
bobCloseSig, aliceCloseSig, bobDeliveryScript,
|
|
|
|
aliceDeliveryScript, bobFeeRate)
|
2016-07-27 21:32:27 +03:00
|
|
|
if err != nil {
|
2017-03-25 04:26:09 +03:00
|
|
|
t.Fatalf("unable to complete alice cooperative close: %v", err)
|
2016-07-27 21:32:27 +03:00
|
|
|
}
|
2017-03-25 04:26:09 +03:00
|
|
|
bobCloseSha := aliceCloseTx.TxHash()
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
bobCloseTx, err := aliceChannel.CompleteCooperativeClose(
|
|
|
|
aliceCloseSig, bobCloseSig, aliceDeliveryScript,
|
|
|
|
bobDeliveryScript, aliceFeeRate)
|
2016-07-27 21:32:27 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to complete bob cooperative close: %v", err)
|
|
|
|
}
|
2017-03-25 04:26:09 +03:00
|
|
|
aliceCloseSha := bobCloseTx.TxHash()
|
|
|
|
|
|
|
|
if bobCloseSha != aliceCloseSha {
|
|
|
|
t.Fatalf("alice and bob close transactions don't match: %v", err)
|
2016-07-27 21:32:27 +03:00
|
|
|
}
|
2016-07-17 04:12:36 +03:00
|
|
|
}
|
2016-09-07 20:45:27 +03:00
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
// TestForceClose checks that the resulting ForceCloseSummary is correct when a
|
|
|
|
// peer is ForceClosing the channel. Will check outputs both above and below
|
2017-01-24 15:04:02 +03:00
|
|
|
// the dust limit.
|
|
|
|
func TestForceClose(t *testing.T) {
|
2017-06-17 01:59:20 +03:00
|
|
|
t.Parallel()
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
// TODO(roasbeef): modify to add some HTLC's before closing?
|
|
|
|
|
2017-01-24 15:04:02 +03:00
|
|
|
// 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)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create test channels: %v", err)
|
|
|
|
}
|
|
|
|
defer cleanUp()
|
|
|
|
|
|
|
|
htlcAmount := btcutil.Amount(500)
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
aliceAmount := aliceChannel.channelState.LocalBalance
|
2017-08-01 06:44:15 +03:00
|
|
|
bobAmount := bobChannel.channelState.LocalBalance
|
2017-01-24 15:04:02 +03:00
|
|
|
|
|
|
|
closeSummary, err := aliceChannel.ForceClose()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to force close channel: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
// The SelfOutputSignDesc should be non-nil since the outout to-self is
|
|
|
|
// non-dust.
|
2017-01-24 15:04:02 +03:00
|
|
|
if closeSummary.SelfOutputSignDesc == nil {
|
|
|
|
t.Fatalf("alice fails to include to-self output in ForceCloseSummary")
|
|
|
|
}
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
// The rest of the close summary should have been populated properly.
|
|
|
|
aliceDelayPoint := aliceChannel.channelState.LocalChanCfg.DelayBasePoint
|
|
|
|
if !closeSummary.SelfOutputSignDesc.PubKey.IsEqual(aliceDelayPoint) {
|
|
|
|
t.Fatalf("alice incorrect pubkey in SelfOutputSignDesc")
|
|
|
|
}
|
|
|
|
if closeSummary.SelfOutputSignDesc.Output.Value != int64(aliceAmount) {
|
|
|
|
t.Fatalf("alice incorrect output value in SelfOutputSignDesc, "+
|
|
|
|
"expected %v, got %v",
|
|
|
|
aliceChannel.channelState.LocalBalance,
|
|
|
|
closeSummary.SelfOutputSignDesc.Output.Value)
|
|
|
|
}
|
|
|
|
if closeSummary.SelfOutputMaturity !=
|
|
|
|
uint32(aliceChannel.localChanCfg.CsvDelay) {
|
2017-01-24 15:04:02 +03:00
|
|
|
t.Fatalf("alice: incorrect local CSV delay in ForceCloseSummary, "+
|
2017-07-30 23:26:31 +03:00
|
|
|
"expected %v, got %v",
|
|
|
|
aliceChannel.channelState.LocalChanCfg.CsvDelay,
|
2017-01-24 15:04:02 +03:00
|
|
|
closeSummary.SelfOutputMaturity)
|
|
|
|
}
|
|
|
|
|
|
|
|
closeTxHash := closeSummary.CloseTx.TxHash()
|
2017-07-30 23:26:31 +03:00
|
|
|
commitTxHash := aliceChannel.channelState.CommitTx.TxHash()
|
2017-01-24 15:04:02 +03:00
|
|
|
if !bytes.Equal(closeTxHash[:], commitTxHash[:]) {
|
|
|
|
t.Fatalf("alice: incorrect close transaction txid")
|
|
|
|
}
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
// Check the same for Bob's ForceCloseSummary.
|
2017-01-24 15:04:02 +03:00
|
|
|
closeSummary, err = bobChannel.ForceClose()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to force close channel: %v", err)
|
|
|
|
}
|
|
|
|
if closeSummary.SelfOutputSignDesc == nil {
|
|
|
|
t.Fatalf("bob fails to include to-self output in ForceCloseSummary")
|
|
|
|
}
|
2017-07-30 23:26:31 +03:00
|
|
|
bobDelayPoint := bobChannel.channelState.LocalChanCfg.DelayBasePoint
|
|
|
|
if !closeSummary.SelfOutputSignDesc.PubKey.IsEqual(bobDelayPoint) {
|
|
|
|
t.Fatalf("bob incorrect pubkey in SelfOutputSignDesc")
|
|
|
|
}
|
|
|
|
if closeSummary.SelfOutputSignDesc.Output.Value != int64(bobAmount) {
|
|
|
|
t.Fatalf("bob incorrect output value in SelfOutputSignDesc, "+
|
|
|
|
"expected %v, got %v",
|
2017-08-01 06:44:15 +03:00
|
|
|
int64(bobAmount),
|
|
|
|
int64(closeSummary.SelfOutputSignDesc.Output.Value))
|
2017-07-30 23:26:31 +03:00
|
|
|
}
|
|
|
|
if closeSummary.SelfOutputMaturity !=
|
|
|
|
uint32(bobChannel.channelState.LocalChanCfg.CsvDelay) {
|
2017-01-24 15:04:02 +03:00
|
|
|
t.Fatalf("bob: incorrect local CSV delay in ForceCloseSummary, "+
|
2017-07-30 23:26:31 +03:00
|
|
|
"expected %v, got %v",
|
|
|
|
bobChannel.channelState.LocalChanCfg.CsvDelay,
|
2017-01-24 15:04:02 +03:00
|
|
|
closeSummary.SelfOutputMaturity)
|
|
|
|
}
|
|
|
|
|
|
|
|
closeTxHash = closeSummary.CloseTx.TxHash()
|
2017-07-30 23:26:31 +03:00
|
|
|
commitTxHash = bobChannel.channelState.CommitTx.TxHash()
|
2017-01-24 15:04:02 +03:00
|
|
|
if !bytes.Equal(closeTxHash[:], commitTxHash[:]) {
|
|
|
|
t.Fatalf("bob: incorrect close transaction txid")
|
|
|
|
}
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
// Re-open the channels by clearing the channel's status back to "open,
|
|
|
|
// and also resetting some internal state.
|
2017-01-24 15:04:02 +03:00
|
|
|
aliceChannel.status = channelOpen
|
|
|
|
bobChannel.status = channelOpen
|
|
|
|
aliceChannel.ForceCloseSignal = make(chan struct{})
|
|
|
|
bobChannel.ForceCloseSignal = make(chan struct{})
|
|
|
|
|
2017-04-10 10:25:32 +03:00
|
|
|
// Have Bobs' to-self output be below her dust limit and check
|
|
|
|
// ForceCloseSummary again on both peers.
|
2017-01-24 15:04:02 +03:00
|
|
|
htlc, preimage := createHTLC(0, bobAmount-htlcAmount)
|
|
|
|
if _, err := bobChannel.AddHTLC(htlc); err != nil {
|
|
|
|
t.Fatalf("alice unable to add htlc: %v", err)
|
|
|
|
}
|
|
|
|
if _, err := aliceChannel.ReceiveHTLC(htlc); err != nil {
|
|
|
|
t.Fatalf("bob unable to receive htlc: %v", err)
|
|
|
|
}
|
|
|
|
if err := forceStateTransition(bobChannel, aliceChannel); err != nil {
|
|
|
|
t.Fatalf("Can't update the channel state: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Settle HTLC and sign new commitment.
|
|
|
|
settleIndex, err := aliceChannel.SettleHTLC(preimage)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bob unable to settle inbound htlc: %v", err)
|
|
|
|
}
|
|
|
|
err = bobChannel.ReceiveHTLCSettle(preimage, settleIndex)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("alice unable to accept settle of outbound htlc: %v", err)
|
|
|
|
}
|
|
|
|
if err := forceStateTransition(aliceChannel, bobChannel); err != nil {
|
|
|
|
t.Fatalf("Can't update the channel state: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
aliceAmount = aliceChannel.channelState.LocalBalance
|
|
|
|
bobAmount = bobChannel.channelState.RemoteBalance
|
2017-01-24 15:04:02 +03:00
|
|
|
|
|
|
|
closeSummary, err = aliceChannel.ForceClose()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to force close channel: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
// Alice's to-self output should still be in the commitment
|
|
|
|
// transaction.
|
2017-01-24 15:04:02 +03:00
|
|
|
if closeSummary.SelfOutputSignDesc == nil {
|
|
|
|
t.Fatalf("alice fails to include to-self output in ForceCloseSummary")
|
2017-07-30 23:26:31 +03:00
|
|
|
}
|
|
|
|
if !closeSummary.SelfOutputSignDesc.PubKey.IsEqual(
|
|
|
|
aliceChannel.channelState.LocalChanCfg.DelayBasePoint,
|
|
|
|
) {
|
|
|
|
t.Fatalf("alice incorrect pubkey in SelfOutputSignDesc")
|
|
|
|
}
|
|
|
|
if closeSummary.SelfOutputSignDesc.Output.Value !=
|
|
|
|
int64(aliceAmount) {
|
|
|
|
t.Fatalf("alice incorrect output value in SelfOutputSignDesc, "+
|
|
|
|
"expected %v, got %v",
|
|
|
|
aliceChannel.channelState.LocalBalance,
|
|
|
|
closeSummary.SelfOutputSignDesc.Output.Value)
|
2017-01-24 15:04:02 +03:00
|
|
|
}
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
if closeSummary.SelfOutputMaturity !=
|
|
|
|
uint32(aliceChannel.channelState.LocalChanCfg.CsvDelay) {
|
2017-01-24 15:04:02 +03:00
|
|
|
t.Fatalf("alice: incorrect local CSV delay in ForceCloseSummary, "+
|
2017-07-30 23:26:31 +03:00
|
|
|
"expected %v, got %v",
|
|
|
|
aliceChannel.channelState.LocalChanCfg.CsvDelay,
|
2017-01-24 15:04:02 +03:00
|
|
|
closeSummary.SelfOutputMaturity)
|
|
|
|
}
|
|
|
|
|
|
|
|
closeTxHash = closeSummary.CloseTx.TxHash()
|
2017-07-30 23:26:31 +03:00
|
|
|
commitTxHash = aliceChannel.channelState.CommitTx.TxHash()
|
2017-01-24 15:04:02 +03:00
|
|
|
if !bytes.Equal(closeTxHash[:], commitTxHash[:]) {
|
|
|
|
t.Fatalf("alice: incorrect close transaction txid")
|
|
|
|
}
|
|
|
|
|
|
|
|
closeSummary, err = bobChannel.ForceClose()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to force close channel: %v", err)
|
|
|
|
}
|
2017-07-30 23:26:31 +03:00
|
|
|
|
2017-04-10 10:25:32 +03:00
|
|
|
// Bob's to-self output is below Bob's dust value and should be
|
|
|
|
// reflected in the ForceCloseSummary.
|
2017-01-24 15:04:02 +03:00
|
|
|
if closeSummary.SelfOutputSignDesc != nil {
|
2017-07-30 23:26:31 +03:00
|
|
|
t.Fatalf("bob incorrectly includes to-self output in " +
|
|
|
|
"ForceCloseSummary")
|
2017-01-24 15:04:02 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
closeTxHash = closeSummary.CloseTx.TxHash()
|
2017-07-30 23:26:31 +03:00
|
|
|
commitTxHash = bobChannel.channelState.CommitTx.TxHash()
|
2017-01-24 15:04:02 +03:00
|
|
|
if !bytes.Equal(closeTxHash[:], commitTxHash[:]) {
|
|
|
|
t.Fatalf("bob: incorrect close transaction txid")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-01 08:09:03 +03:00
|
|
|
// TestDustHTLCFees checks that fees are calculated correctly when HTLCs fall
|
|
|
|
// below the nodes' dust limit. In these cases, the amount of the dust HTLCs
|
|
|
|
// should be applied to the commitment transaction fee.
|
|
|
|
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)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create test channels: %v", err)
|
|
|
|
}
|
|
|
|
defer cleanUp()
|
|
|
|
|
2017-08-01 06:49:07 +03:00
|
|
|
aliceStartingBalance := aliceChannel.channelState.LocalBalance
|
|
|
|
|
2017-06-01 08:09:03 +03:00
|
|
|
// This HTLC amount should be lower than the dust limits of both nodes.
|
|
|
|
htlcAmount := btcutil.Amount(100)
|
|
|
|
htlc, _ := createHTLC(0, htlcAmount)
|
|
|
|
if _, err := aliceChannel.AddHTLC(htlc); err != nil {
|
|
|
|
t.Fatalf("alice unable to add htlc: %v", err)
|
|
|
|
}
|
|
|
|
if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
|
|
|
|
t.Fatalf("bob unable to receive htlc: %v", err)
|
|
|
|
}
|
|
|
|
if err := forceStateTransition(aliceChannel, bobChannel); err != nil {
|
|
|
|
t.Fatalf("Can't update the channel state: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-08-01 06:49:07 +03:00
|
|
|
// After the transition, we'll ensure that we performed fee accounting
|
|
|
|
// properly. Namely, the local+remote+commitfee values should add up to
|
|
|
|
// the total capacity of the channel. This same should hold for both
|
|
|
|
// sides.
|
|
|
|
totalSatoshisAlice := (aliceChannel.channelState.LocalBalance +
|
|
|
|
aliceChannel.channelState.RemoteBalance +
|
|
|
|
aliceChannel.channelState.CommitFee)
|
|
|
|
if totalSatoshisAlice+htlcAmount != aliceChannel.Capacity {
|
|
|
|
t.Fatalf("alice's funds leaked: total satoshis are %v, but channel "+
|
|
|
|
"capacity is %v", int64(totalSatoshisAlice),
|
|
|
|
int64(aliceChannel.Capacity))
|
|
|
|
}
|
|
|
|
totalSatoshisBob := (bobChannel.channelState.LocalBalance +
|
|
|
|
bobChannel.channelState.RemoteBalance +
|
|
|
|
bobChannel.channelState.CommitFee)
|
|
|
|
if totalSatoshisBob+htlcAmount != bobChannel.Capacity {
|
|
|
|
t.Fatalf("bob's funds leaked: total satoshis are %v, but channel "+
|
|
|
|
"capacity is %v", int64(totalSatoshisBob),
|
|
|
|
int64(bobChannel.Capacity))
|
|
|
|
}
|
|
|
|
|
|
|
|
// The commitment fee paid should be the same, as there have been no
|
|
|
|
// new material outputs added.
|
2017-06-01 08:09:03 +03:00
|
|
|
defaultFee := calcStaticFee(0)
|
2017-08-01 06:49:07 +03:00
|
|
|
if aliceChannel.channelState.CommitFee != defaultFee {
|
2017-06-01 08:09:03 +03:00
|
|
|
t.Fatalf("dust htlc amounts not subtracted from commitment fee "+
|
2017-08-01 06:49:07 +03:00
|
|
|
"expected %v, got %v", defaultFee,
|
2017-06-01 08:09:03 +03:00
|
|
|
aliceChannel.channelState.CommitFee)
|
|
|
|
}
|
2017-08-01 06:49:07 +03:00
|
|
|
if bobChannel.channelState.CommitFee != defaultFee {
|
2017-06-01 08:09:03 +03:00
|
|
|
t.Fatalf("dust htlc amounts not subtracted from commitment fee "+
|
2017-08-01 06:49:07 +03:00
|
|
|
"expected %v, got %v", defaultFee,
|
2017-06-01 08:09:03 +03:00
|
|
|
bobChannel.channelState.CommitFee)
|
|
|
|
}
|
2017-08-01 06:49:07 +03:00
|
|
|
|
|
|
|
// Alice's final balance should reflect the HTLC deficit even though
|
|
|
|
// the HTLC was paid to fees as it was trimmed.
|
|
|
|
aliceEndBalance := aliceChannel.channelState.LocalBalance
|
|
|
|
aliceExpectedBalance := aliceStartingBalance - htlcAmount
|
|
|
|
if aliceEndBalance != aliceExpectedBalance {
|
|
|
|
t.Fatalf("alice not credited for dust: expected %v, got %v",
|
|
|
|
aliceExpectedBalance, aliceEndBalance)
|
|
|
|
}
|
2017-06-01 08:09:03 +03:00
|
|
|
}
|
|
|
|
|
2017-06-03 23:22:52 +03:00
|
|
|
// TestHTLCDustLimit checks the situation in which an HTLC is larger than one
|
|
|
|
// channel participant's dust limit, but smaller than the other participant's
|
|
|
|
// dust limit. In this case, the participants' commitment chains will diverge.
|
|
|
|
// In one commitment chain, the HTLC will be added as normal, in the other
|
|
|
|
// chain, the amount of the HTLC will contribute to the fees to be paid.
|
|
|
|
func TestHTLCDustLimit(t *testing.T) {
|
2017-06-17 01:59:20 +03:00
|
|
|
t.Parallel()
|
2017-08-01 06:50:06 +03:00
|
|
|
|
2016-12-06 17:05:46 +03:00
|
|
|
// 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)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create test channels: %v", err)
|
|
|
|
}
|
|
|
|
defer cleanUp()
|
|
|
|
|
2017-06-03 23:22:52 +03:00
|
|
|
// The amount of the HTLC should be above Alice's dust limit and below
|
|
|
|
// Bob's dust limit.
|
2017-07-30 23:26:31 +03:00
|
|
|
htlcAmount := (btcutil.Amount(500) +
|
|
|
|
htlcTimeoutFee(aliceChannel.channelState.FeePerKw))
|
2016-12-06 17:05:46 +03:00
|
|
|
|
|
|
|
htlc, preimage := createHTLC(0, htlcAmount)
|
|
|
|
if _, err := aliceChannel.AddHTLC(htlc); err != nil {
|
|
|
|
t.Fatalf("alice unable to add htlc: %v", err)
|
|
|
|
}
|
|
|
|
if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
|
|
|
|
t.Fatalf("bob unable to receive htlc: %v", err)
|
|
|
|
}
|
|
|
|
if err := forceStateTransition(aliceChannel, bobChannel); err != nil {
|
|
|
|
t.Fatalf("Can't update the channel state: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-06-03 23:22:52 +03:00
|
|
|
// At this point, Alice's commitment transaction should have an HTLC,
|
|
|
|
// while Bob's should not, because the value falls beneath his dust
|
|
|
|
// limit. The amount of the HTLC should be applied to fees in Bob's
|
|
|
|
// commitment transaction.
|
2017-08-01 06:50:06 +03:00
|
|
|
aliceCommitment := aliceChannel.localCommitChain.tip()
|
|
|
|
if len(aliceCommitment.txn.TxOut) != 3 {
|
|
|
|
t.Fatalf("incorrect # of outputs: expected %v, got %v",
|
|
|
|
3, len(aliceCommitment.txn.TxOut))
|
|
|
|
}
|
|
|
|
bobCommitment := bobChannel.localCommitChain.tip()
|
|
|
|
if len(bobCommitment.txn.TxOut) != 2 {
|
2017-06-03 23:22:52 +03:00
|
|
|
t.Fatalf("incorrect # of outputs: expected %v, got %v",
|
2017-08-01 06:50:06 +03:00
|
|
|
2, len(bobCommitment.txn.TxOut))
|
2017-06-03 23:22:52 +03:00
|
|
|
}
|
|
|
|
defaultFee := calcStaticFee(0)
|
2017-08-01 06:50:06 +03:00
|
|
|
if bobChannel.channelState.CommitFee != defaultFee {
|
|
|
|
t.Fatalf("dust htlc amount was subtracted from commitment fee "+
|
|
|
|
"expected %v, got %v", defaultFee,
|
2017-06-03 23:22:52 +03:00
|
|
|
bobChannel.channelState.CommitFee)
|
|
|
|
}
|
2016-12-06 17:05:46 +03:00
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
// Settle HTLC and create a new commitment state.
|
2016-12-06 17:05:46 +03:00
|
|
|
settleIndex, err := bobChannel.SettleHTLC(preimage)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bob unable to settle inbound htlc: %v", err)
|
|
|
|
}
|
|
|
|
err = aliceChannel.ReceiveHTLCSettle(preimage, settleIndex)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("alice unable to accept settle of outbound htlc: %v", err)
|
|
|
|
}
|
2017-02-21 05:01:39 +03:00
|
|
|
if err := forceStateTransition(bobChannel, aliceChannel); err != nil {
|
2017-04-10 10:25:32 +03:00
|
|
|
t.Fatalf("state transition error: %v", err)
|
2016-12-06 17:05:46 +03:00
|
|
|
}
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
// At this point, for Alice's commitment chains, the value of the HTLC
|
|
|
|
// should have been added to Alice's balance and TotalSatoshisSent.
|
2017-08-01 06:50:06 +03:00
|
|
|
commitment := aliceChannel.localCommitChain.tip()
|
2017-06-03 23:22:52 +03:00
|
|
|
if len(commitment.txn.TxOut) != 2 {
|
|
|
|
t.Fatalf("incorrect # of outputs: expected %v, got %v",
|
|
|
|
2, len(commitment.txn.TxOut))
|
|
|
|
}
|
|
|
|
if aliceChannel.channelState.TotalSatoshisSent != uint64(htlcAmount) {
|
|
|
|
t.Fatalf("alice satoshis sent incorrect: expected %v, got %v",
|
|
|
|
htlcAmount, aliceChannel.channelState.TotalSatoshisSent)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestChannelBalanceDustLimit tests the condition when the remaining balance
|
|
|
|
// for one of the channel participants is so small as to be considered dust. In
|
|
|
|
// this case, the output for that participant is removed and all funds (minus
|
|
|
|
// fees) in the commitment transaction are allocated to the remaining channel
|
|
|
|
// participant.
|
|
|
|
//
|
|
|
|
// TODO(roasbeef): test needs to be fixed after reserve limits are done
|
|
|
|
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)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create test channels: %v", err)
|
|
|
|
}
|
|
|
|
defer cleanUp()
|
|
|
|
|
|
|
|
// This amount should leave an amount larger than Alice's dust limit
|
|
|
|
// once fees have been subtracted, but smaller than Bob's dust limit.
|
|
|
|
defaultFee := calcStaticFee(0)
|
2017-07-30 23:26:31 +03:00
|
|
|
dustLimit := aliceChannel.channelState.LocalChanCfg.DustLimit
|
|
|
|
aliceBalance := aliceChannel.channelState.LocalBalance
|
|
|
|
htlcAmount := aliceBalance - (defaultFee + dustLimit + 100)
|
2017-08-01 06:50:37 +03:00
|
|
|
htlcAmount += htlcSuccessFee(aliceChannel.channelState.FeePerKw)
|
2017-01-23 16:56:00 +03:00
|
|
|
|
2017-06-03 23:22:52 +03:00
|
|
|
htlc, preimage := createHTLC(0, htlcAmount)
|
2017-01-23 16:56:00 +03:00
|
|
|
if _, err := aliceChannel.AddHTLC(htlc); err != nil {
|
|
|
|
t.Fatalf("alice unable to add htlc: %v", err)
|
|
|
|
}
|
|
|
|
if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
|
|
|
|
t.Fatalf("bob unable to receive htlc: %v", err)
|
|
|
|
}
|
|
|
|
if err := forceStateTransition(aliceChannel, bobChannel); err != nil {
|
2017-04-10 10:25:32 +03:00
|
|
|
t.Fatalf("state transition error: %v", err)
|
2017-01-23 16:56:00 +03:00
|
|
|
}
|
2017-06-03 23:22:52 +03:00
|
|
|
settleIndex, err := bobChannel.SettleHTLC(preimage)
|
2017-01-23 16:56:00 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bob unable to settle inbound htlc: %v", err)
|
|
|
|
}
|
|
|
|
err = aliceChannel.ReceiveHTLCSettle(preimage, settleIndex)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("alice unable to accept settle of outbound htlc: %v", err)
|
|
|
|
}
|
|
|
|
if err := forceStateTransition(bobChannel, aliceChannel); err != nil {
|
2017-04-10 10:25:32 +03:00
|
|
|
t.Fatalf("state transition error: %v", err)
|
2017-01-23 16:56:00 +03:00
|
|
|
}
|
|
|
|
|
2017-06-03 23:22:52 +03:00
|
|
|
// At the conclusion of this test, in Bob's commitment chains, the
|
|
|
|
// output for Alice's balance should have been removed as dust, leaving
|
|
|
|
// only a single output that will send the remaining funds in the
|
|
|
|
// channel to Bob.
|
|
|
|
commitment := bobChannel.localCommitChain.tip()
|
|
|
|
if len(commitment.txn.TxOut) != 1 {
|
|
|
|
t.Fatalf("incorrect # of outputs: expected %v, got %v",
|
|
|
|
1, len(commitment.txn.TxOut))
|
|
|
|
}
|
|
|
|
if aliceChannel.channelState.TotalSatoshisSent != uint64(htlcAmount) {
|
|
|
|
t.Fatalf("alice satoshis sent incorrect: expected %v, got %v",
|
|
|
|
htlcAmount, aliceChannel.channelState.TotalSatoshisSent)
|
|
|
|
}
|
2016-12-06 17:05:46 +03:00
|
|
|
}
|
|
|
|
|
2016-09-07 20:45:27 +03:00
|
|
|
func TestStateUpdatePersistence(t *testing.T) {
|
2017-06-17 01:59:20 +03:00
|
|
|
t.Parallel()
|
|
|
|
|
2016-09-07 20:45:27 +03:00
|
|
|
// 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.
|
2017-02-21 05:01:39 +03:00
|
|
|
aliceChannel, bobChannel, cleanUp, err := createTestChannels(1)
|
2016-09-07 20:45:27 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create test channels: %v", err)
|
|
|
|
}
|
|
|
|
defer cleanUp()
|
|
|
|
|
|
|
|
if err := aliceChannel.channelState.FullSync(); err != nil {
|
|
|
|
t.Fatalf("unable to sync alice's channel: %v", err)
|
|
|
|
}
|
|
|
|
if err := bobChannel.channelState.FullSync(); err != nil {
|
|
|
|
t.Fatalf("unable to sync bob's channel: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
const numHtlcs = 4
|
2017-08-01 06:53:02 +03:00
|
|
|
const htlcAmt = 20000
|
2016-09-07 20:45:27 +03:00
|
|
|
|
2017-01-13 08:01:50 +03:00
|
|
|
// Alice adds 3 HTLCs to the update log, while Bob adds a single HTLC.
|
2016-09-07 20:45:27 +03:00
|
|
|
var alicePreimage [32]byte
|
|
|
|
copy(alicePreimage[:], bytes.Repeat([]byte{0xaa}, 32))
|
|
|
|
var bobPreimage [32]byte
|
|
|
|
copy(bobPreimage[:], bytes.Repeat([]byte{0xbb}, 32))
|
|
|
|
for i := 0; i < 3; i++ {
|
2017-03-16 04:56:25 +03:00
|
|
|
rHash := sha256.Sum256(alicePreimage[:])
|
2017-02-21 05:01:39 +03:00
|
|
|
h := &lnwire.UpdateAddHTLC{
|
|
|
|
PaymentHash: rHash,
|
2017-08-01 06:53:02 +03:00
|
|
|
Amount: htlcAmt,
|
2017-02-21 05:01:39 +03:00
|
|
|
Expiry: uint32(10),
|
2016-09-07 20:45:27 +03:00
|
|
|
}
|
|
|
|
|
2017-02-21 05:01:39 +03:00
|
|
|
if _, err := aliceChannel.AddHTLC(h); err != nil {
|
|
|
|
t.Fatalf("unable to add alice's htlc: %v", err)
|
|
|
|
}
|
|
|
|
if _, err := bobChannel.ReceiveHTLC(h); err != nil {
|
|
|
|
t.Fatalf("unable to recv alice's htlc: %v", err)
|
|
|
|
}
|
2016-09-07 20:45:27 +03:00
|
|
|
}
|
2017-03-16 04:56:25 +03:00
|
|
|
rHash := sha256.Sum256(bobPreimage[:])
|
2017-02-21 05:01:39 +03:00
|
|
|
bobh := &lnwire.UpdateAddHTLC{
|
|
|
|
PaymentHash: rHash,
|
2017-08-01 06:53:02 +03:00
|
|
|
Amount: htlcAmt,
|
2017-02-21 05:01:39 +03:00
|
|
|
Expiry: uint32(10),
|
|
|
|
}
|
|
|
|
if _, err := bobChannel.AddHTLC(bobh); err != nil {
|
|
|
|
t.Fatalf("unable to add bob's htlc: %v", err)
|
|
|
|
}
|
|
|
|
if _, err := aliceChannel.ReceiveHTLC(bobh); err != nil {
|
|
|
|
t.Fatalf("unable to recv bob's htlc: %v", err)
|
2016-09-07 20:45:27 +03:00
|
|
|
}
|
|
|
|
|
2017-02-21 05:01:39 +03:00
|
|
|
// Next, Alice initiates a state transition to include the HTLC's she
|
|
|
|
// added above in a new commitment state.
|
2016-09-07 20:45:27 +03:00
|
|
|
if err := forceStateTransition(aliceChannel, bobChannel); err != nil {
|
2017-02-21 05:01:39 +03:00
|
|
|
t.Fatalf("unable to complete alice's state transition: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Since the HTLC Bob sent wasn't included in Bob's version of the
|
|
|
|
// commitment transaction (but it was in Alice's, as he ACK'd her
|
|
|
|
// changes before creating a new state), Bob needs to trigger another
|
|
|
|
// state update in order to re-sync their states.
|
|
|
|
if err := forceStateTransition(bobChannel, aliceChannel); err != nil {
|
|
|
|
t.Fatalf("unable to complete bob's state transition: %v", err)
|
2016-09-07 20:45:27 +03:00
|
|
|
}
|
|
|
|
|
2017-01-13 08:01:50 +03:00
|
|
|
// The latest commitment from both sides should have all the HTLCs.
|
2016-09-07 20:45:27 +03:00
|
|
|
numAliceOutgoing := aliceChannel.localCommitChain.tail().outgoingHTLCs
|
|
|
|
numAliceIncoming := aliceChannel.localCommitChain.tail().incomingHTLCs
|
|
|
|
if len(numAliceOutgoing) != 3 {
|
|
|
|
t.Fatalf("expected %v htlcs, instead got %v", 3, numAliceOutgoing)
|
|
|
|
}
|
|
|
|
if len(numAliceIncoming) != 1 {
|
|
|
|
t.Fatalf("expected %v htlcs, instead got %v", 1, numAliceIncoming)
|
|
|
|
}
|
|
|
|
numBobOutgoing := bobChannel.localCommitChain.tail().outgoingHTLCs
|
|
|
|
numBobIncoming := bobChannel.localCommitChain.tail().incomingHTLCs
|
|
|
|
if len(numBobOutgoing) != 1 {
|
|
|
|
t.Fatalf("expected %v htlcs, instead got %v", 1, numBobOutgoing)
|
|
|
|
}
|
|
|
|
if len(numBobIncoming) != 3 {
|
|
|
|
t.Fatalf("expected %v htlcs, instead got %v", 3, numBobIncoming)
|
|
|
|
}
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
// TODO(roasbeef): also ensure signatures were stored
|
|
|
|
// * ensure expiry matches
|
|
|
|
|
2016-09-07 20:45:27 +03:00
|
|
|
// Now fetch both of the channels created above from disk to simulate a
|
|
|
|
// node restart with persistence.
|
2016-10-26 02:40:47 +03:00
|
|
|
alicePub := aliceChannel.channelState.IdentityPub
|
|
|
|
aliceChannels, err := aliceChannel.channelState.Db.FetchOpenChannels(alicePub)
|
2016-09-07 20:45:27 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to fetch channel: %v", err)
|
|
|
|
}
|
2016-10-26 02:40:47 +03:00
|
|
|
bobPub := bobChannel.channelState.IdentityPub
|
|
|
|
bobChannels, err := bobChannel.channelState.Db.FetchOpenChannels(bobPub)
|
2016-09-07 20:45:27 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to fetch channel: %v", err)
|
|
|
|
}
|
2016-09-13 05:05:56 +03:00
|
|
|
notifier := aliceChannel.channelEvents
|
2017-04-10 10:25:32 +03:00
|
|
|
aliceChannelNew, err := NewLightningChannel(aliceChannel.signer,
|
2017-05-01 10:27:12 +03:00
|
|
|
notifier, aliceChannel.feeEstimator, aliceChannels[0])
|
2016-09-07 20:45:27 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create new channel: %v", err)
|
|
|
|
}
|
2017-04-10 10:25:32 +03:00
|
|
|
bobChannelNew, err := NewLightningChannel(bobChannel.signer, notifier,
|
2017-05-01 10:27:12 +03:00
|
|
|
bobChannel.feeEstimator, bobChannels[0])
|
2016-09-07 20:45:27 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create new channel: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// The state update logs of the new channels and the old channels
|
2017-01-13 08:01:50 +03:00
|
|
|
// should now be identical other than the height the HTLCs were added.
|
2017-04-10 10:25:32 +03:00
|
|
|
if aliceChannel.localUpdateLog.logIndex !=
|
|
|
|
aliceChannelNew.localUpdateLog.logIndex {
|
2016-09-07 20:45:27 +03:00
|
|
|
t.Fatalf("alice log counter: expected %v, got %v",
|
2017-02-21 05:01:39 +03:00
|
|
|
aliceChannel.localUpdateLog.logIndex,
|
|
|
|
aliceChannelNew.localUpdateLog.logIndex)
|
2016-09-07 20:45:27 +03:00
|
|
|
}
|
2017-04-10 10:25:32 +03:00
|
|
|
if aliceChannel.remoteUpdateLog.logIndex !=
|
|
|
|
aliceChannelNew.remoteUpdateLog.logIndex {
|
2016-09-07 20:45:27 +03:00
|
|
|
t.Fatalf("alice log counter: expected %v, got %v",
|
2017-02-21 05:01:39 +03:00
|
|
|
aliceChannel.remoteUpdateLog.logIndex,
|
|
|
|
aliceChannelNew.remoteUpdateLog.logIndex)
|
2016-09-07 20:45:27 +03:00
|
|
|
}
|
2017-04-10 10:25:32 +03:00
|
|
|
if aliceChannel.localUpdateLog.Len() !=
|
|
|
|
aliceChannelNew.localUpdateLog.Len() {
|
2016-09-07 20:45:27 +03:00
|
|
|
t.Fatalf("alice log len: expected %v, got %v",
|
2017-02-21 05:01:39 +03:00
|
|
|
aliceChannel.localUpdateLog.Len(),
|
|
|
|
aliceChannelNew.localUpdateLog.Len())
|
2016-09-07 20:45:27 +03:00
|
|
|
}
|
2017-04-10 10:25:32 +03:00
|
|
|
if aliceChannel.remoteUpdateLog.Len() !=
|
|
|
|
aliceChannelNew.remoteUpdateLog.Len() {
|
2016-09-07 20:45:27 +03:00
|
|
|
t.Fatalf("alice log len: expected %v, got %v",
|
2017-02-21 05:01:39 +03:00
|
|
|
aliceChannel.remoteUpdateLog.Len(),
|
|
|
|
aliceChannelNew.remoteUpdateLog.Len())
|
2016-09-07 20:45:27 +03:00
|
|
|
}
|
2017-04-10 10:25:32 +03:00
|
|
|
if bobChannel.localUpdateLog.logIndex !=
|
|
|
|
bobChannelNew.localUpdateLog.logIndex {
|
2016-09-07 20:45:27 +03:00
|
|
|
t.Fatalf("bob log counter: expected %v, got %v",
|
2017-02-21 05:01:39 +03:00
|
|
|
bobChannel.localUpdateLog.logIndex,
|
|
|
|
bobChannelNew.localUpdateLog.logIndex)
|
2016-09-07 20:45:27 +03:00
|
|
|
}
|
2017-04-10 10:25:32 +03:00
|
|
|
if bobChannel.remoteUpdateLog.logIndex !=
|
|
|
|
bobChannelNew.remoteUpdateLog.logIndex {
|
2016-09-07 20:45:27 +03:00
|
|
|
t.Fatalf("bob log counter: expected %v, got %v",
|
2017-02-21 05:01:39 +03:00
|
|
|
bobChannel.remoteUpdateLog.logIndex,
|
|
|
|
bobChannelNew.remoteUpdateLog.logIndex)
|
2016-09-07 20:45:27 +03:00
|
|
|
}
|
2017-04-10 10:25:32 +03:00
|
|
|
if bobChannel.localUpdateLog.Len() !=
|
|
|
|
bobChannelNew.localUpdateLog.Len() {
|
2016-09-07 20:45:27 +03:00
|
|
|
t.Fatalf("bob log len: expected %v, got %v",
|
2017-05-01 21:45:02 +03:00
|
|
|
bobChannel.localUpdateLog.Len(),
|
2017-02-21 05:01:39 +03:00
|
|
|
bobChannelNew.localUpdateLog.Len())
|
2016-09-07 20:45:27 +03:00
|
|
|
}
|
2017-04-10 10:25:32 +03:00
|
|
|
if bobChannel.remoteUpdateLog.Len() !=
|
|
|
|
bobChannelNew.remoteUpdateLog.Len() {
|
2016-09-07 20:45:27 +03:00
|
|
|
t.Fatalf("bob log len: expected %v, got %v",
|
2017-02-21 05:01:39 +03:00
|
|
|
bobChannel.remoteUpdateLog.Len(),
|
|
|
|
bobChannelNew.remoteUpdateLog.Len())
|
2016-09-07 20:45:27 +03:00
|
|
|
}
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
// TODO(roasbeef): expand test to also ensure state revocation log has
|
|
|
|
// proper pk scripts
|
|
|
|
|
2017-01-21 10:23:10 +03:00
|
|
|
// Newly generated pkScripts for HTLCs should be the same as in the old channel.
|
|
|
|
for _, entry := range aliceChannel.localUpdateLog.updateIndex {
|
|
|
|
htlc := entry.Value.(*PaymentDescriptor)
|
|
|
|
restoredHtlc := aliceChannelNew.localUpdateLog.lookup(htlc.Index)
|
|
|
|
if !bytes.Equal(htlc.ourPkScript, restoredHtlc.ourPkScript) {
|
2017-04-10 10:25:32 +03:00
|
|
|
t.Fatalf("alice ourPkScript in ourLog: expected %X, got %X",
|
2017-01-21 10:23:10 +03:00
|
|
|
htlc.ourPkScript[:5], restoredHtlc.ourPkScript[:5])
|
|
|
|
}
|
|
|
|
if !bytes.Equal(htlc.theirPkScript, restoredHtlc.theirPkScript) {
|
2017-04-10 10:25:32 +03:00
|
|
|
t.Fatalf("alice theirPkScript in ourLog: expected %X, got %X",
|
2017-01-21 10:23:10 +03:00
|
|
|
htlc.theirPkScript[:5], restoredHtlc.theirPkScript[:5])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, entry := range aliceChannel.remoteUpdateLog.updateIndex {
|
|
|
|
htlc := entry.Value.(*PaymentDescriptor)
|
|
|
|
restoredHtlc := aliceChannelNew.remoteUpdateLog.lookup(htlc.Index)
|
|
|
|
if !bytes.Equal(htlc.ourPkScript, restoredHtlc.ourPkScript) {
|
2017-04-10 10:25:32 +03:00
|
|
|
t.Fatalf("alice ourPkScript in theirLog: expected %X, got %X",
|
2017-01-21 10:23:10 +03:00
|
|
|
htlc.ourPkScript[:5], restoredHtlc.ourPkScript[:5])
|
|
|
|
}
|
|
|
|
if !bytes.Equal(htlc.theirPkScript, restoredHtlc.theirPkScript) {
|
2017-04-10 10:25:32 +03:00
|
|
|
t.Fatalf("alice theirPkScript in theirLog: expected %X, got %X",
|
2017-01-21 10:23:10 +03:00
|
|
|
htlc.theirPkScript[:5], restoredHtlc.theirPkScript[:5])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, entry := range bobChannel.localUpdateLog.updateIndex {
|
|
|
|
htlc := entry.Value.(*PaymentDescriptor)
|
|
|
|
restoredHtlc := bobChannelNew.localUpdateLog.lookup(htlc.Index)
|
|
|
|
if !bytes.Equal(htlc.ourPkScript, restoredHtlc.ourPkScript) {
|
2017-04-10 10:25:32 +03:00
|
|
|
t.Fatalf("bob ourPkScript in ourLog: expected %X, got %X",
|
2017-01-21 10:23:10 +03:00
|
|
|
htlc.ourPkScript[:5], restoredHtlc.ourPkScript[:5])
|
|
|
|
}
|
|
|
|
if !bytes.Equal(htlc.theirPkScript, restoredHtlc.theirPkScript) {
|
2017-04-10 10:25:32 +03:00
|
|
|
t.Fatalf("bob theirPkScript in ourLog: expected %X, got %X",
|
2017-01-21 10:23:10 +03:00
|
|
|
htlc.theirPkScript[:5], restoredHtlc.theirPkScript[:5])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, entry := range bobChannel.remoteUpdateLog.updateIndex {
|
|
|
|
htlc := entry.Value.(*PaymentDescriptor)
|
|
|
|
restoredHtlc := bobChannelNew.remoteUpdateLog.lookup(htlc.Index)
|
|
|
|
if !bytes.Equal(htlc.ourPkScript, restoredHtlc.ourPkScript) {
|
2017-04-10 10:25:32 +03:00
|
|
|
t.Fatalf("bob ourPkScript in theirLog: expected %X, got %X",
|
2017-01-21 10:23:10 +03:00
|
|
|
htlc.ourPkScript[:5], restoredHtlc.ourPkScript[:5])
|
|
|
|
}
|
|
|
|
if !bytes.Equal(htlc.theirPkScript, restoredHtlc.theirPkScript) {
|
2017-04-10 10:25:32 +03:00
|
|
|
t.Fatalf("bob theirPkScript in theirLog: expected %X, got %X",
|
2017-01-21 10:23:10 +03:00
|
|
|
htlc.theirPkScript[:5], restoredHtlc.theirPkScript[:5])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-13 08:01:50 +03:00
|
|
|
// Now settle all the HTLCs, then force a state update. The state
|
2017-08-01 06:53:02 +03:00
|
|
|
// update should succeed as both sides have identical.
|
2016-09-07 20:45:27 +03:00
|
|
|
for i := 0; i < 3; i++ {
|
|
|
|
settleIndex, err := bobChannelNew.SettleHTLC(alicePreimage)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to settle htlc: %v", err)
|
|
|
|
}
|
|
|
|
err = aliceChannelNew.ReceiveHTLCSettle(alicePreimage, settleIndex)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to settle htlc: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
settleIndex, err := aliceChannelNew.SettleHTLC(bobPreimage)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to settle htlc: %v", err)
|
|
|
|
}
|
|
|
|
err = bobChannelNew.ReceiveHTLCSettle(bobPreimage, settleIndex)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to settle htlc: %v", err)
|
|
|
|
}
|
2017-02-21 05:01:39 +03:00
|
|
|
|
|
|
|
// Similar to the two transitions above, as both Bob and Alice added
|
|
|
|
// entries to the update log before a state transition was initiated by
|
|
|
|
// either side, both sides are required to trigger an update in order
|
|
|
|
// to lock in their changes.
|
2016-09-07 20:45:27 +03:00
|
|
|
if err := forceStateTransition(aliceChannelNew, bobChannelNew); err != nil {
|
|
|
|
t.Fatalf("unable to update commitments: %v", err)
|
|
|
|
}
|
2017-02-21 05:01:39 +03:00
|
|
|
if err := forceStateTransition(bobChannelNew, aliceChannelNew); err != nil {
|
|
|
|
t.Fatalf("unable to update commitments: %v", err)
|
|
|
|
}
|
2016-09-07 20:45:27 +03:00
|
|
|
|
2016-11-18 01:39:38 +03:00
|
|
|
// The amounts transferred should been updated as per the amounts in
|
|
|
|
// the HTLCs
|
2017-08-01 06:53:02 +03:00
|
|
|
if aliceChannelNew.channelState.TotalSatoshisSent != htlcAmt*3 {
|
2016-11-18 01:39:38 +03:00
|
|
|
t.Fatalf("expected %v alice satoshis sent, got %v",
|
2017-08-01 06:53:02 +03:00
|
|
|
htlcAmt*3, aliceChannelNew.channelState.TotalSatoshisSent)
|
2016-11-18 01:39:38 +03:00
|
|
|
}
|
2017-08-01 06:53:02 +03:00
|
|
|
if aliceChannelNew.channelState.TotalSatoshisReceived != htlcAmt {
|
2016-11-18 01:39:38 +03:00
|
|
|
t.Fatalf("expected %v alice satoshis received, got %v",
|
2017-08-01 06:53:02 +03:00
|
|
|
htlcAmt, aliceChannelNew.channelState.TotalSatoshisReceived)
|
2016-11-18 01:39:38 +03:00
|
|
|
}
|
2017-08-01 06:53:02 +03:00
|
|
|
if bobChannelNew.channelState.TotalSatoshisSent != htlcAmt {
|
2016-11-18 01:39:38 +03:00
|
|
|
t.Fatalf("expected %v bob satoshis sent, got %v",
|
2017-08-01 06:53:02 +03:00
|
|
|
htlcAmt, bobChannel.channelState.TotalSatoshisSent)
|
2016-11-18 01:39:38 +03:00
|
|
|
}
|
2017-08-01 06:53:02 +03:00
|
|
|
if bobChannelNew.channelState.TotalSatoshisReceived != htlcAmt*3 {
|
2016-11-18 01:39:38 +03:00
|
|
|
t.Fatalf("expected %v bob satoshis sent, got %v",
|
2017-08-01 06:53:02 +03:00
|
|
|
htlcAmt*3, bobChannel.channelState.TotalSatoshisSent)
|
2016-11-18 01:39:38 +03:00
|
|
|
}
|
2016-09-07 20:45:27 +03:00
|
|
|
}
|
2017-01-06 07:54:39 +03:00
|
|
|
|
|
|
|
func TestCancelHTLC(t *testing.T) {
|
2017-06-17 01:59:20 +03:00
|
|
|
t.Parallel()
|
|
|
|
|
2017-01-06 07:54:39 +03:00
|
|
|
// 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)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create test channels: %v", err)
|
|
|
|
}
|
|
|
|
defer cleanUp()
|
|
|
|
|
|
|
|
// Add a new HTLC from Alice to Bob, then trigger a new state
|
|
|
|
// transition in order to include it in the latest state.
|
|
|
|
const htlcAmt = btcutil.SatoshiPerBitcoin
|
|
|
|
|
|
|
|
var preImage [32]byte
|
|
|
|
copy(preImage[:], bytes.Repeat([]byte{0xaa}, 32))
|
2017-02-21 05:01:39 +03:00
|
|
|
htlc := &lnwire.UpdateAddHTLC{
|
2017-03-16 04:56:25 +03:00
|
|
|
PaymentHash: sha256.Sum256(preImage[:]),
|
2017-02-21 05:01:39 +03:00
|
|
|
Amount: htlcAmt,
|
|
|
|
Expiry: 10,
|
2017-01-06 07:54:39 +03:00
|
|
|
}
|
2017-02-21 05:01:39 +03:00
|
|
|
paymentHash := htlc.PaymentHash
|
2017-01-06 07:54:39 +03:00
|
|
|
|
|
|
|
if _, err := aliceChannel.AddHTLC(htlc); err != nil {
|
|
|
|
t.Fatalf("unable to add alice htlc: %v", err)
|
|
|
|
}
|
2017-01-07 01:10:21 +03:00
|
|
|
if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
|
2017-01-06 07:54:39 +03:00
|
|
|
t.Fatalf("unable to add bob htlc: %v", err)
|
|
|
|
}
|
|
|
|
if err := forceStateTransition(aliceChannel, bobChannel); err != nil {
|
|
|
|
t.Fatalf("unable to create new commitment state: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// With the HTLC committed, Alice's balance should reflect the clearing
|
|
|
|
// of the new HTLC.
|
2017-05-01 21:45:02 +03:00
|
|
|
aliceExpectedBalance := btcutil.Amount(btcutil.SatoshiPerBitcoin*4) -
|
|
|
|
calcStaticFee(1)
|
2017-07-30 23:26:31 +03:00
|
|
|
if aliceChannel.channelState.LocalBalance != aliceExpectedBalance {
|
2017-01-06 07:54:39 +03:00
|
|
|
t.Fatalf("Alice's balance is wrong: expected %v, got %v",
|
2017-07-30 23:26:31 +03:00
|
|
|
aliceExpectedBalance, aliceChannel.channelState.LocalBalance)
|
2017-01-06 07:54:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Now, with the HTLC committed on both sides, trigger a cancellation
|
|
|
|
// from Bob to Alice, removing the HTLC.
|
2017-02-21 05:01:39 +03:00
|
|
|
htlcCancelIndex, err := bobChannel.FailHTLC(paymentHash)
|
2017-01-07 01:10:21 +03:00
|
|
|
if err != nil {
|
2017-01-06 07:54:39 +03:00
|
|
|
t.Fatalf("unable to cancel HTLC: %v", err)
|
|
|
|
}
|
2017-02-21 05:01:39 +03:00
|
|
|
if err := aliceChannel.ReceiveFailHTLC(htlcCancelIndex); err != nil {
|
2017-01-06 07:54:39 +03:00
|
|
|
t.Fatalf("unable to recv htlc cancel: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now trigger another state transition, the HTLC should now be removed
|
|
|
|
// from both sides, with balances reflected.
|
|
|
|
if err := forceStateTransition(bobChannel, aliceChannel); err != nil {
|
|
|
|
t.Fatalf("unable to create new commitment: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
// Now HTLCs should be present on the commitment transaction for either
|
|
|
|
// side.
|
2017-01-06 07:54:39 +03:00
|
|
|
if len(aliceChannel.localCommitChain.tip().outgoingHTLCs) != 0 ||
|
|
|
|
len(aliceChannel.remoteCommitChain.tip().outgoingHTLCs) != 0 {
|
|
|
|
t.Fatalf("htlc's still active from alice's POV")
|
|
|
|
}
|
2017-01-08 06:58:54 +03:00
|
|
|
if len(aliceChannel.localCommitChain.tip().incomingHTLCs) != 0 ||
|
|
|
|
len(aliceChannel.remoteCommitChain.tip().incomingHTLCs) != 0 {
|
|
|
|
t.Fatalf("htlc's still active from alice's POV")
|
|
|
|
}
|
2017-01-06 07:54:39 +03:00
|
|
|
if len(bobChannel.localCommitChain.tip().outgoingHTLCs) != 0 ||
|
|
|
|
len(bobChannel.remoteCommitChain.tip().outgoingHTLCs) != 0 {
|
|
|
|
t.Fatalf("htlc's still active from bob's POV")
|
|
|
|
}
|
2017-01-08 06:58:54 +03:00
|
|
|
if len(bobChannel.localCommitChain.tip().incomingHTLCs) != 0 ||
|
|
|
|
len(bobChannel.remoteCommitChain.tip().incomingHTLCs) != 0 {
|
|
|
|
t.Fatalf("htlc's still active from bob's POV")
|
|
|
|
}
|
2017-01-06 07:54:39 +03:00
|
|
|
|
|
|
|
expectedBalance := btcutil.Amount(btcutil.SatoshiPerBitcoin * 5)
|
2017-07-30 23:26:31 +03:00
|
|
|
if aliceChannel.channelState.LocalBalance != expectedBalance-calcStaticFee(0) {
|
2017-01-06 07:54:39 +03:00
|
|
|
t.Fatalf("balance is wrong: expected %v, got %v",
|
2017-07-30 23:26:31 +03:00
|
|
|
aliceChannel.channelState.LocalBalance, expectedBalance-
|
2017-05-01 21:45:02 +03:00
|
|
|
calcStaticFee(0))
|
2017-01-06 07:54:39 +03:00
|
|
|
}
|
2017-07-30 23:26:31 +03:00
|
|
|
if aliceChannel.channelState.RemoteBalance != expectedBalance {
|
2017-01-06 07:54:39 +03:00
|
|
|
t.Fatalf("balance is wrong: expected %v, got %v",
|
2017-07-30 23:26:31 +03:00
|
|
|
aliceChannel.channelState.RemoteBalance, expectedBalance)
|
2017-01-06 07:54:39 +03:00
|
|
|
}
|
2017-07-30 23:26:31 +03:00
|
|
|
if bobChannel.channelState.LocalBalance != expectedBalance {
|
2017-01-06 07:54:39 +03:00
|
|
|
t.Fatalf("balance is wrong: expected %v, got %v",
|
2017-07-30 23:26:31 +03:00
|
|
|
bobChannel.channelState.LocalBalance, expectedBalance)
|
2017-01-06 07:54:39 +03:00
|
|
|
}
|
2017-07-30 23:26:31 +03:00
|
|
|
if bobChannel.channelState.RemoteBalance != expectedBalance-calcStaticFee(0) {
|
2017-01-06 07:54:39 +03:00
|
|
|
t.Fatalf("balance is wrong: expected %v, got %v",
|
2017-07-30 23:26:31 +03:00
|
|
|
bobChannel.channelState.RemoteBalance,
|
2017-05-01 21:45:02 +03:00
|
|
|
expectedBalance-calcStaticFee(0))
|
2017-01-06 07:54:39 +03:00
|
|
|
}
|
|
|
|
}
|
2017-02-28 06:00:18 +03:00
|
|
|
|
2017-04-10 10:25:32 +03:00
|
|
|
func TestCooperativeCloseDustAdherence(t *testing.T) {
|
2017-06-17 01:59:20 +03:00
|
|
|
t.Parallel()
|
|
|
|
|
2017-03-25 02:20:05 +03:00
|
|
|
// 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.
|
2017-02-28 06:00:18 +03:00
|
|
|
aliceChannel, bobChannel, cleanUp, err := createTestChannels(5)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create test channels: %v", err)
|
|
|
|
}
|
|
|
|
defer cleanUp()
|
|
|
|
|
2017-05-18 02:51:10 +03:00
|
|
|
aliceFeeRate := uint64(aliceChannel.channelState.FeePerKw)
|
|
|
|
bobFeeRate := uint64(bobChannel.channelState.FeePerKw)
|
|
|
|
|
2017-03-25 02:20:05 +03:00
|
|
|
setDustLimit := func(dustVal btcutil.Amount) {
|
2017-07-30 23:26:31 +03:00
|
|
|
aliceChannel.channelState.LocalChanCfg.DustLimit = dustVal
|
|
|
|
aliceChannel.channelState.RemoteChanCfg.DustLimit = dustVal
|
|
|
|
bobChannel.channelState.LocalChanCfg.DustLimit = dustVal
|
|
|
|
bobChannel.channelState.RemoteChanCfg.DustLimit = dustVal
|
2017-03-25 02:20:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
resetChannelState := func() {
|
|
|
|
aliceChannel.status = channelOpen
|
|
|
|
bobChannel.status = channelOpen
|
|
|
|
}
|
|
|
|
|
|
|
|
setBalances := func(aliceBalance, bobBalance btcutil.Amount) {
|
2017-07-30 23:26:31 +03:00
|
|
|
aliceChannel.channelState.LocalBalance = aliceBalance
|
|
|
|
aliceChannel.channelState.RemoteBalance = bobBalance
|
|
|
|
bobChannel.channelState.LocalBalance = bobBalance
|
|
|
|
bobChannel.channelState.RemoteBalance = aliceBalance
|
2017-03-25 02:20:05 +03:00
|
|
|
}
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
aliceDeliveryScript := bobsPrivKey[:]
|
|
|
|
bobDeliveryScript := testHdSeed[:]
|
|
|
|
|
2017-03-25 02:20:05 +03:00
|
|
|
// We'll start be initializing the limit of both Alice and Bob to 10k
|
|
|
|
// satoshis.
|
|
|
|
dustLimit := btcutil.Amount(10000)
|
|
|
|
setDustLimit(dustLimit)
|
|
|
|
|
|
|
|
// Both sides currently have over 1 BTC settled as part of their
|
|
|
|
// balances. As a result, performing a cooperative closure now result
|
|
|
|
// in both sides having an output within the closure transaction.
|
2017-07-30 23:26:31 +03:00
|
|
|
aliceSig, _, err := aliceChannel.CreateCloseProposal(aliceFeeRate,
|
|
|
|
aliceDeliveryScript, bobDeliveryScript)
|
2017-03-25 04:26:09 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to close channel: %v", err)
|
|
|
|
}
|
|
|
|
aliceCloseSig := append(aliceSig, byte(txscript.SigHashAll))
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
bobSig, _, err := bobChannel.CreateCloseProposal(bobFeeRate,
|
|
|
|
bobDeliveryScript, aliceDeliveryScript)
|
2017-03-25 02:20:05 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to close channel: %v", err)
|
|
|
|
}
|
2017-03-25 04:26:09 +03:00
|
|
|
bobCloseSig := append(bobSig, byte(txscript.SigHashAll))
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
closeTx, err := bobChannel.CompleteCooperativeClose(
|
|
|
|
bobCloseSig, aliceCloseSig,
|
|
|
|
bobDeliveryScript, aliceDeliveryScript, bobFeeRate)
|
2017-03-25 02:20:05 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to accept channel close: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// The closure transaction should have exactly two outputs.
|
|
|
|
if len(closeTx.TxOut) != 2 {
|
|
|
|
t.Fatalf("close tx has wrong number of outputs: expected %v "+
|
|
|
|
"got %v", 2, len(closeTx.TxOut))
|
|
|
|
}
|
|
|
|
|
|
|
|
// We'll reset the channel states before proceeding to our nest test.
|
|
|
|
resetChannelState()
|
|
|
|
|
|
|
|
// Next we'll modify the current balances and dust limits such that
|
|
|
|
// Bob's current balance is above _below_ his dust limit.
|
|
|
|
aliceBal := btcutil.Amount(btcutil.SatoshiPerBitcoin)
|
2017-05-01 21:45:02 +03:00
|
|
|
bobBal := btcutil.Amount(250)
|
2017-03-25 02:20:05 +03:00
|
|
|
setBalances(aliceBal, bobBal)
|
|
|
|
|
|
|
|
// Attempt another cooperative channel closure. It should succeed
|
|
|
|
// without any issues.
|
2017-07-30 23:26:31 +03:00
|
|
|
aliceSig, _, err = aliceChannel.CreateCloseProposal(aliceFeeRate,
|
|
|
|
aliceDeliveryScript, bobDeliveryScript)
|
2017-03-25 02:20:05 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to close channel: %v", err)
|
|
|
|
}
|
2017-03-25 04:26:09 +03:00
|
|
|
aliceCloseSig = append(aliceSig, byte(txscript.SigHashAll))
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
bobSig, _, err = bobChannel.CreateCloseProposal(bobFeeRate,
|
|
|
|
bobDeliveryScript, aliceDeliveryScript)
|
2017-03-25 04:26:09 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to close channel: %v", err)
|
|
|
|
}
|
|
|
|
bobCloseSig = append(bobSig, byte(txscript.SigHashAll))
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
closeTx, err = bobChannel.CompleteCooperativeClose(
|
|
|
|
bobCloseSig, aliceCloseSig,
|
|
|
|
bobDeliveryScript, aliceDeliveryScript, bobFeeRate)
|
2017-03-25 02:20:05 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to accept channel close: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// The closure transaction should only have a single output, and that
|
|
|
|
// output should be Alice's balance.
|
|
|
|
if len(closeTx.TxOut) != 1 {
|
|
|
|
t.Fatalf("close tx has wrong number of outputs: expected %v "+
|
|
|
|
"got %v", 1, len(closeTx.TxOut))
|
|
|
|
}
|
2017-05-01 21:45:02 +03:00
|
|
|
if closeTx.TxOut[0].Value != int64(aliceBal-calcStaticFee(0)) {
|
2017-03-25 02:20:05 +03:00
|
|
|
t.Fatalf("alice's balance is incorrect: expected %v, got %v",
|
2017-05-17 05:05:12 +03:00
|
|
|
int64(aliceBal-calcStaticFee(0)), closeTx.TxOut[0].Value)
|
2017-03-25 02:20:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Finally, we'll modify the current balances and dust limits such that
|
|
|
|
// Alice's current balance is _below_ his her limit.
|
|
|
|
setBalances(bobBal, aliceBal)
|
|
|
|
resetChannelState()
|
|
|
|
|
|
|
|
// Our final attempt at another cooperative channel closure. It should
|
|
|
|
// succeed without any issues.
|
2017-07-30 23:26:31 +03:00
|
|
|
aliceSig, _, err = aliceChannel.CreateCloseProposal(aliceFeeRate,
|
|
|
|
aliceDeliveryScript, bobDeliveryScript)
|
2017-03-25 04:26:09 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to close channel: %v", err)
|
|
|
|
}
|
|
|
|
aliceCloseSig = append(aliceSig, byte(txscript.SigHashAll))
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
bobSig, _, err = bobChannel.CreateCloseProposal(bobFeeRate,
|
|
|
|
bobDeliveryScript, aliceDeliveryScript)
|
2017-03-25 02:20:05 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to close channel: %v", err)
|
|
|
|
}
|
2017-03-25 04:26:09 +03:00
|
|
|
bobCloseSig = append(bobSig, byte(txscript.SigHashAll))
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
closeTx, err = bobChannel.CompleteCooperativeClose(
|
|
|
|
bobCloseSig, aliceCloseSig,
|
|
|
|
bobDeliveryScript, aliceDeliveryScript, bobFeeRate)
|
2017-03-25 02:20:05 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to accept channel close: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// The closure transaction should only have a single output, and that
|
|
|
|
// output should be Bob's balance.
|
|
|
|
if len(closeTx.TxOut) != 1 {
|
|
|
|
t.Fatalf("close tx has wrong number of outputs: expected %v "+
|
|
|
|
"got %v", 1, len(closeTx.TxOut))
|
|
|
|
}
|
|
|
|
if closeTx.TxOut[0].Value != int64(aliceBal) {
|
|
|
|
t.Fatalf("bob's balance is incorrect: expected %v, got %v",
|
|
|
|
aliceBal, closeTx.TxOut[0].Value)
|
2017-02-28 06:00:18 +03:00
|
|
|
}
|
|
|
|
}
|
2017-07-14 21:38:35 +03:00
|
|
|
|
|
|
|
// TestUpdateFeeFail tests that the signature verification will fail if they
|
|
|
|
// fee updates are out of sync.
|
|
|
|
func TestUpdateFeeFail(t *testing.T) {
|
|
|
|
aliceChannel, bobChannel, cleanUp, err := createTestChannels(1)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create test channels: %v", err)
|
|
|
|
}
|
|
|
|
defer cleanUp()
|
|
|
|
|
|
|
|
// Bob receives the update, that will apply to his commitment
|
|
|
|
// transaction.
|
|
|
|
bobChannel.ReceiveUpdateFee(111)
|
|
|
|
|
|
|
|
// Alice sends signature for commitment that does not cover any fee
|
|
|
|
// update.
|
2017-07-30 23:26:31 +03:00
|
|
|
aliceSig, aliceHtlcSigs, err := aliceChannel.SignNextCommitment()
|
2017-07-14 21:38:35 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("alice unable to sign commitment: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bob verifies this commit, meaning that he checks that it is
|
|
|
|
// consistent everything he has received. This should fail, since he got
|
|
|
|
// the fee update, but Alice never sent it.
|
2017-07-30 23:26:31 +03:00
|
|
|
err = bobChannel.ReceiveNewCommitment(aliceSig, aliceHtlcSigs)
|
2017-07-14 21:38:35 +03:00
|
|
|
if err == nil {
|
|
|
|
t.Fatalf("expected bob to fail receiving alice's signature")
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestUpdateFeeSenderCommits veriefies that the state machine progresses as
|
|
|
|
// expected if we send a fee update, and then the sender of the fee update
|
|
|
|
// sends a commitment signature.
|
|
|
|
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)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create test channels: %v", err)
|
|
|
|
}
|
|
|
|
defer cleanUp()
|
|
|
|
|
|
|
|
paymentPreimage := bytes.Repeat([]byte{1}, 32)
|
|
|
|
paymentHash := sha256.Sum256(paymentPreimage)
|
|
|
|
htlc := &lnwire.UpdateAddHTLC{
|
|
|
|
PaymentHash: paymentHash,
|
|
|
|
Amount: btcutil.SatoshiPerBitcoin,
|
|
|
|
Expiry: uint32(5),
|
|
|
|
}
|
|
|
|
|
|
|
|
// First Alice adds the outgoing HTLC to her local channel's state
|
|
|
|
// update log. Then Alice sends this wire message over to Bob who
|
|
|
|
// adds this htlc to his remote state update log.
|
|
|
|
if _, err := aliceChannel.AddHTLC(htlc); err != nil {
|
|
|
|
t.Fatalf("unable to add htlc: %v", err)
|
|
|
|
}
|
|
|
|
if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
|
|
|
|
t.Fatalf("unable to recv htlc: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Simulate Alice sending update fee message to bob.
|
|
|
|
fee := btcutil.Amount(111)
|
|
|
|
aliceChannel.UpdateFee(fee)
|
|
|
|
bobChannel.ReceiveUpdateFee(fee)
|
|
|
|
|
|
|
|
// Alice signs a commitment, which will cover everything sent to Bob
|
|
|
|
// (the HTLC and the fee update), and everything acked by Bob (nothing
|
|
|
|
// so far).
|
2017-07-30 23:26:31 +03:00
|
|
|
aliceSig, aliceHtlcSigs, err := aliceChannel.SignNextCommitment()
|
2017-07-14 21:38:35 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("alice unable to sign commitment: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bob receives this signature message, and verifies that it is
|
|
|
|
// consistent with the state he had for Alice, including the received
|
|
|
|
// HTLC and fee update.
|
2017-07-30 23:26:31 +03:00
|
|
|
err = bobChannel.ReceiveNewCommitment(aliceSig, aliceHtlcSigs)
|
|
|
|
if err != nil {
|
2017-07-14 21:38:35 +03:00
|
|
|
t.Fatalf("bob unable to process alice's new commitment: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if bobChannel.channelState.FeePerKw == fee {
|
|
|
|
t.Fatalf("bob's feePerKw was unexpectedly locked in")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bob can revoke the prior commitment he had. This should lock in the
|
|
|
|
// fee update for him.
|
|
|
|
bobRevocation, err := bobChannel.RevokeCurrentCommitment()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to generate bob revocation: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if bobChannel.channelState.FeePerKw != fee {
|
|
|
|
t.Fatalf("bob's feePerKw was not locked in")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bob commits to all updates he has received from Alice. This includes
|
|
|
|
// the HTLC he received, and the fee update.
|
2017-07-30 23:26:31 +03:00
|
|
|
bobSig, bobHtlcSigs, err := bobChannel.SignNextCommitment()
|
2017-07-14 21:38:35 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bob unable to sign alice's commitment: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
// Alice receives the revocation of the old one, and can now assume
|
|
|
|
// that Bob's received everything up to the signature she sent,
|
|
|
|
// including the HTLC and fee update.
|
2017-07-14 21:38:35 +03:00
|
|
|
if _, err := aliceChannel.ReceiveRevocation(bobRevocation); err != nil {
|
|
|
|
t.Fatalf("alice unable to rocess bob's revocation: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Alice receives new signature from Bob, and assumes this covers the
|
|
|
|
// changes.
|
2017-07-30 23:26:31 +03:00
|
|
|
err = aliceChannel.ReceiveNewCommitment(bobSig, bobHtlcSigs)
|
|
|
|
if err != nil {
|
2017-07-14 21:38:35 +03:00
|
|
|
t.Fatalf("alice unable to process bob's new commitment: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if aliceChannel.channelState.FeePerKw == fee {
|
|
|
|
t.Fatalf("alice's feePerKw was unexpectedly locked in")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Alice can revoke the old commitment, which will lock in the fee
|
|
|
|
// update.
|
|
|
|
aliceRevocation, err := aliceChannel.RevokeCurrentCommitment()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to revoke alice channel: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if aliceChannel.channelState.FeePerKw != fee {
|
|
|
|
t.Fatalf("alice's feePerKw was not locked in")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bob receives revocation from Alice.
|
|
|
|
if _, err := bobChannel.ReceiveRevocation(aliceRevocation); err != nil {
|
|
|
|
t.Fatalf("bob unable to process alive's revocation: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestUpdateFeeReceiverCommits tests that the state machine progresses as
|
|
|
|
// expected if we send a fee update, and then the receiver of the fee update
|
|
|
|
// sends a commitment signature.
|
|
|
|
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)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create test channels: %v", err)
|
|
|
|
}
|
|
|
|
defer cleanUp()
|
|
|
|
|
|
|
|
paymentPreimage := bytes.Repeat([]byte{1}, 32)
|
|
|
|
paymentHash := sha256.Sum256(paymentPreimage)
|
|
|
|
htlc := &lnwire.UpdateAddHTLC{
|
|
|
|
PaymentHash: paymentHash,
|
|
|
|
Amount: btcutil.SatoshiPerBitcoin,
|
|
|
|
Expiry: uint32(5),
|
|
|
|
}
|
|
|
|
|
|
|
|
// First Alice adds the outgoing HTLC to her local channel's state
|
|
|
|
// update log. Then Alice sends this wire message over to Bob who
|
|
|
|
// adds this htlc to his remote state update log.
|
|
|
|
if _, err := aliceChannel.AddHTLC(htlc); err != nil {
|
|
|
|
t.Fatalf("unable to add htlc: %v", err)
|
|
|
|
}
|
|
|
|
if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
|
|
|
|
t.Fatalf("unable to recv htlc: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Simulate Alice sending update fee message to bob
|
|
|
|
fee := btcutil.Amount(111)
|
|
|
|
aliceChannel.UpdateFee(fee)
|
|
|
|
bobChannel.ReceiveUpdateFee(fee)
|
|
|
|
|
|
|
|
// Bob commits to every change he has sent since last time (none). He
|
|
|
|
// does not commit to the received HTLC and fee update, since Alice
|
|
|
|
// cannot know if he has received them.
|
2017-07-30 23:26:31 +03:00
|
|
|
bobSig, bobHtlcSigs, err := bobChannel.SignNextCommitment()
|
2017-07-14 21:38:35 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("alice unable to sign commitment: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Alice receives this signature message, and verifies that it is
|
|
|
|
// consistent with the remote state, not including any of the updates.
|
2017-07-30 23:26:31 +03:00
|
|
|
err = aliceChannel.ReceiveNewCommitment(bobSig, bobHtlcSigs)
|
|
|
|
if err != nil {
|
2017-07-14 21:38:35 +03:00
|
|
|
t.Fatalf("bob unable to process alice's new commitment: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Alice can revoke the prior commitment she had, this will ack
|
|
|
|
// everything received before last commitment signature, but in this
|
|
|
|
// case that is nothing.
|
|
|
|
aliceRevocation, err := aliceChannel.RevokeCurrentCommitment()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to generate bob revocation: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bob receives the revocation of the old commitment
|
|
|
|
if _, err := bobChannel.ReceiveRevocation(aliceRevocation); err != nil {
|
|
|
|
t.Fatalf("alice unable to rocess bob's revocation: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Alice will sign next commitment. Since she sent the revocation, she
|
|
|
|
// also ack'ed everything received, but in this case this is nothing.
|
|
|
|
// Since she sent the two updates, this signature will cover those two.
|
2017-07-30 23:26:31 +03:00
|
|
|
aliceSig, aliceHtlcSigs, err := aliceChannel.SignNextCommitment()
|
2017-07-14 21:38:35 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bob unable to sign alice's commitment: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bob gets the signature for the new commitment from Alice. He assumes
|
|
|
|
// this covers everything received from alice, including the two updates.
|
2017-07-30 23:26:31 +03:00
|
|
|
err = bobChannel.ReceiveNewCommitment(aliceSig, aliceHtlcSigs)
|
|
|
|
if err != nil {
|
2017-07-14 21:38:35 +03:00
|
|
|
t.Fatalf("alice unable to process bob's new commitment: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if bobChannel.channelState.FeePerKw == fee {
|
|
|
|
t.Fatalf("bob's feePerKw was unexpectedly locked in")
|
|
|
|
}
|
2017-07-30 23:26:31 +03:00
|
|
|
|
2017-07-14 21:38:35 +03:00
|
|
|
// Bob can revoke the old commitment. This will ack what he has
|
|
|
|
// received, including the HTLC and fee update. This will lock in the
|
|
|
|
// fee update for bob.
|
|
|
|
bobRevocation, err := bobChannel.RevokeCurrentCommitment()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to revoke alice channel: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if bobChannel.channelState.FeePerKw != fee {
|
|
|
|
t.Fatalf("bob's feePerKw was not locked in")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bob will send a new signature, which will cover what he just acked:
|
|
|
|
// the HTLC and fee update.
|
2017-07-30 23:26:31 +03:00
|
|
|
bobSig, bobHtlcSigs, err = bobChannel.SignNextCommitment()
|
2017-07-14 21:38:35 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("alice unable to sign commitment: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Alice receives revokation from Bob, and can now be sure that Bob
|
|
|
|
// received the two updates, and they are considered locked in.
|
|
|
|
if _, err := aliceChannel.ReceiveRevocation(bobRevocation); err != nil {
|
|
|
|
t.Fatalf("bob unable to process alive's revocation: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Alice will receive the signature from Bob, which will cover what was
|
|
|
|
// just acked by his revocation.
|
2017-07-30 23:26:31 +03:00
|
|
|
err = aliceChannel.ReceiveNewCommitment(bobSig, bobHtlcSigs)
|
|
|
|
if err != nil {
|
2017-07-14 21:38:35 +03:00
|
|
|
t.Fatalf("alice unable to process bob's new commitment: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if aliceChannel.channelState.FeePerKw == fee {
|
|
|
|
t.Fatalf("alice's feePerKw was unexpectedly locked in")
|
|
|
|
}
|
|
|
|
|
|
|
|
// After Alice now revokes her old commitment, the fee update should
|
|
|
|
// lock in.
|
|
|
|
aliceRevocation, err = aliceChannel.RevokeCurrentCommitment()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to generate bob revocation: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if aliceChannel.channelState.FeePerKw != fee {
|
|
|
|
t.Fatalf("Alice's feePerKw was not locked in")
|
|
|
|
}
|
|
|
|
|
2017-07-30 23:26:31 +03:00
|
|
|
// Bob receives revocation from Alice.
|
2017-07-14 21:38:35 +03:00
|
|
|
if _, err := bobChannel.ReceiveRevocation(aliceRevocation); err != nil {
|
|
|
|
t.Fatalf("bob unable to process alive's revocation: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestUpdateFeeReceiverSendsUpdate tests that receiving a fee update as channel
|
2017-07-30 23:26:31 +03:00
|
|
|
// initiator fails, and that trying to initiate fee update as non-initiation
|
2017-07-14 21:38:35 +03:00
|
|
|
// fails.
|
|
|
|
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)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create test channels: %v", err)
|
|
|
|
}
|
|
|
|
defer cleanUp()
|
|
|
|
|
|
|
|
// Since Alice is the channel initiator, she should fail when receiving
|
|
|
|
// fee update
|
|
|
|
fee := btcutil.Amount(111)
|
|
|
|
err = aliceChannel.ReceiveUpdateFee(fee)
|
|
|
|
if err == nil {
|
|
|
|
t.Fatalf("expected alice to fail receiving fee update")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Similarly, initiating fee update should fail for Bob.
|
|
|
|
err = bobChannel.UpdateFee(fee)
|
|
|
|
if err == nil {
|
|
|
|
t.Fatalf("expected bob to fail initiating fee update")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test that if multiple update fee messages are sent consecutively, then the
|
|
|
|
// last one is the one that is being committed to.
|
|
|
|
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)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create test channels: %v", err)
|
|
|
|
}
|
|
|
|
defer cleanUp()
|
|
|
|
|
|
|
|
// Simulate Alice sending update fee message to bob.
|
|
|
|
fee1 := btcutil.Amount(111)
|
|
|
|
fee2 := btcutil.Amount(222)
|
|
|
|
fee := btcutil.Amount(333)
|
|
|
|
aliceChannel.UpdateFee(fee1)
|
|
|
|
aliceChannel.UpdateFee(fee2)
|
|
|
|
aliceChannel.UpdateFee(fee)
|
|
|
|
|
|
|
|
// Alice signs a commitment, which will cover everything sent to Bob
|
|
|
|
// (the HTLC and the fee update), and everything acked by Bob (nothing
|
|
|
|
// so far).
|
2017-07-30 23:26:31 +03:00
|
|
|
aliceSig, aliceHtlcSigs, err := aliceChannel.SignNextCommitment()
|
2017-07-14 21:38:35 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("alice unable to sign commitment: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
bobChannel.ReceiveUpdateFee(fee1)
|
|
|
|
bobChannel.ReceiveUpdateFee(fee2)
|
|
|
|
bobChannel.ReceiveUpdateFee(fee)
|
|
|
|
|
|
|
|
// Bob receives this signature message, and verifies that it is
|
|
|
|
// consistent with the state he had for Alice, including the received
|
|
|
|
// HTLC and fee update.
|
2017-07-30 23:26:31 +03:00
|
|
|
err = bobChannel.ReceiveNewCommitment(aliceSig, aliceHtlcSigs)
|
|
|
|
if err != nil {
|
2017-07-14 21:38:35 +03:00
|
|
|
t.Fatalf("bob unable to process alice's new commitment: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if bobChannel.channelState.FeePerKw == fee {
|
|
|
|
t.Fatalf("bob's feePerKw was unexpectedly locked in")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Alice sending more fee updates now should not mess up the old fee
|
|
|
|
// they both committed to.
|
|
|
|
fee3 := btcutil.Amount(444)
|
|
|
|
fee4 := btcutil.Amount(555)
|
|
|
|
fee5 := btcutil.Amount(666)
|
|
|
|
aliceChannel.UpdateFee(fee3)
|
|
|
|
aliceChannel.UpdateFee(fee4)
|
|
|
|
aliceChannel.UpdateFee(fee5)
|
|
|
|
bobChannel.ReceiveUpdateFee(fee3)
|
|
|
|
bobChannel.ReceiveUpdateFee(fee4)
|
|
|
|
bobChannel.ReceiveUpdateFee(fee5)
|
|
|
|
|
|
|
|
// Bob can revoke the prior commitment he had. This should lock in the
|
|
|
|
// fee update for him.
|
|
|
|
bobRevocation, err := bobChannel.RevokeCurrentCommitment()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to generate bob revocation: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if bobChannel.channelState.FeePerKw != fee {
|
|
|
|
t.Fatalf("bob's feePerKw was not locked in")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bob commits to all updates he has received from Alice. This includes
|
|
|
|
// the HTLC he received, and the fee update.
|
2017-07-30 23:26:31 +03:00
|
|
|
bobSig, bobHtlcSigs, err := bobChannel.SignNextCommitment()
|
2017-07-14 21:38:35 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bob unable to sign alice's commitment: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Alice receives the revocation of the old one, and can now assume that
|
|
|
|
// Bob's received everything up to the signature she sent, including the
|
|
|
|
// HTLC and fee update.
|
|
|
|
if _, err := aliceChannel.ReceiveRevocation(bobRevocation); err != nil {
|
|
|
|
t.Fatalf("alice unable to rocess bob's revocation: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Alice receives new signature from Bob, and assumes this covers the
|
|
|
|
// changes.
|
2017-07-30 23:26:31 +03:00
|
|
|
if err := aliceChannel.ReceiveNewCommitment(bobSig, bobHtlcSigs); err != nil {
|
2017-07-14 21:38:35 +03:00
|
|
|
t.Fatalf("alice unable to process bob's new commitment: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if aliceChannel.channelState.FeePerKw == fee {
|
|
|
|
t.Fatalf("alice's feePerKw was unexpectedly locked in")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Alice can revoke the old commitment, which will lock in the fee
|
|
|
|
// update.
|
|
|
|
aliceRevocation, err := aliceChannel.RevokeCurrentCommitment()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to revoke alice channel: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if aliceChannel.channelState.FeePerKw != fee {
|
|
|
|
t.Fatalf("alice's feePerKw was not locked in")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bob receives revocation from Alice.
|
|
|
|
if _, err := bobChannel.ReceiveRevocation(aliceRevocation); err != nil {
|
|
|
|
t.Fatalf("bob unable to process alive's revocation: %v", err)
|
|
|
|
}
|
|
|
|
}
|